Files
jak-project/test/test_emitter_arm64.cpp
T
Tyler Wilding bfc4c18ada goalc: replicate tests for the majority of ARM64 non-simd cases (#4377)
- Adds `capstone` as a disassembling library that could eventually
replace Zydis (for now left that alone)
- Replicates all of the existing x86 tests to ARM64, fixed a bunch of
underlying issues along the way
- There are a very small handful of tests remaining that need to be
enabled / fixed
2026-08-16 15:34:37 -04:00

3207 lines
124 KiB
C++

#include <cstdio>
#include "emitter_test_helpers.h"
#include "emitter_util.h"
#include "goalc/emitter/CodeTester.h"
#include "goalc/emitter/IGen.h"
#include "goalc/emitter/Register.h"
#include "gtest/gtest.h"
#include <capstone/arm.h>
#include <fmt/base.h>
#include <fmt/format.h>
using namespace emitter;
namespace {
const auto instr_set = InstructionSet::ARM64;
CodeTester create_tester(int code_capacity = 1024) {
CodeTester tester(instr_set);
tester.init_code_buffer(code_capacity);
return tester;
}
}; // namespace
template <typename Fn>
void for_each_register_except(CodeTester& tester, Register excluded, Fn&& fn) {
for (int i = 0; i < tester.get_reg_count(); i++) {
if (::testing::Test::HasFatalFailure()) {
return;
}
Register reg(i);
if (reg.id() == excluded.id()) {
continue;
}
fn(reg);
}
}
template <typename Fn>
void for_each_register_except(CodeTester& tester,
std::initializer_list<Register> excluded,
Fn&& fn) {
for (int i = 0; i < tester.get_reg_count(); i++) {
Register reg(i);
bool skip = false;
for (Register ex : excluded) {
if (reg.id() == ex.id()) {
skip = true;
break;
}
}
if (skip) {
continue;
}
fn(reg);
// Stop iterating if a FAIL()/ASSERT_* occurred inside the lambda.
if (::testing::Test::HasFatalFailure()) {
return;
}
}
}
template <typename Fn>
void for_each_register_except_stack_and_scratch(CodeTester& tester, Fn&& fn) {
for_each_register_except(tester, {tester.get_stack_reg(), Register(X16)}, std::forward<Fn>(fn));
}
TEST(ARM64EmitterIntegerMath, add_gpr64_imm8s) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
// test the ones that aren't sp
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for (auto val : vals) {
for (auto imm : imms) {
tester.clear();
auto expected = val + imm;
tester.emit_push_all_gprs(true);
// move initial value to register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), i, X0));
// do the add
tester.emit(IGen::add_gpr64_imm8s(tester.generator(), i, imm));
// move for return
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), X0, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, val, expected);
}
}
});
}
TEST(ARM64EmitterIntegerMath, add_gpr64_imm32s) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for (auto val : vals) {
for (auto imm : imms) {
tester.clear();
auto expected = val + imm;
tester.emit_push_all_gprs(true);
// move initial value to register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), i, tester.get_c_abi_arg_reg(0)));
// do the add
tester.emit(IGen::add_gpr64_imm32s(tester.generator(), i, imm));
// move for return
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, val, expected);
}
}
});
}
TEST(ARM64EmitterIntegerMath, sub_gpr64_imm8s) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for (auto val : vals) {
for (auto imm : imms) {
tester.clear();
auto expected = val - imm;
tester.emit_push_all_gprs(true);
// move initial value to register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), i, tester.get_c_abi_arg_reg(0)));
// do the add
tester.emit(IGen::sub_gpr64_imm8s(tester.generator(), i, imm));
// move for return
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, val, expected);
}
}
});
}
TEST(ARM64EmitterIntegerMath, sub_gpr64_imm32s) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for (auto val : vals) {
for (auto imm : imms) {
tester.clear();
auto expected = val - imm;
tester.emit_push_all_gprs(true);
// move initial value to register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), i, tester.get_c_abi_arg_reg(0)));
// do the add
tester.emit(IGen::sub_gpr64_imm32s(tester.generator(), i, imm));
// move for return
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, val, expected);
}
}
});
}
TEST(ARM64EmitterIntegerMath, add_gpr64_gpr64) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for (auto v1 : vals) {
for (auto v2 : vals) {
tester.clear();
auto expected = v1 + v2;
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v1));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), j, v2));
tester.emit(IGen::add_gpr64_gpr64(tester.generator(), i, j));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), X0, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
}
}
});
});
}
TEST(ARM64EmitterIntegerMath, sub_gpr64_gpr64) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for (auto v1 : vals) {
for (auto v2 : vals) {
tester.clear();
auto expected = v1 - v2;
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v1));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), j, v2));
tester.emit(IGen::sub_gpr64_gpr64(tester.generator(), i, j));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
}
}
});
});
}
TEST(ARM64EmitterIntegerMath, mul_gpr32_gpr32) {
auto tester = create_tester();
std::vector<s32> vals = {
0, 1, -2, -20, 123123, INT32_MIN, INT32_MAX, INT32_MIN + 1, INT32_MAX - 1};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for (auto v1 : vals) {
for (auto v2 : vals) {
// this is kind of weird behavior, but it's what the PS2 CPU does, I think.
// the lower 32-bits of the result are sign extended, even if this sign doesn't match
// the sign of the real product. This is true for both signed and unsigned multiply.
tester.clear();
auto expected = ((s64(v1) * s64(v2)) << 32) >> 32;
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, (s64)v1));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), j, (s64)v2));
tester.emit(IGen::imul_gpr32_gpr32(tester.generator(), i, j));
tester.emit(IGen::movsx_r64_r32(tester.generator(), tester.get_return_reg(),
i)); // weird PS2 sign extend.
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
}
}
});
});
}
TEST(ARM64EmitterIntegerMath, or_gpr64_gpr64) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for (auto v1 : vals) {
for (auto v2 : vals) {
tester.clear();
auto expected = v1 | v2;
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v1));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), j, v2));
tester.emit(IGen::or_gpr64_gpr64(tester.generator(), i, j));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
}
}
});
});
}
TEST(ARM64EmitterIntegerMath, and_gpr64_gpr64) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for (auto v1 : vals) {
for (auto v2 : vals) {
tester.clear();
auto expected = v1 & v2;
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v1));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), j, v2));
tester.emit(IGen::and_gpr64_gpr64(tester.generator(), i, j));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ_MSG(tester, 0, expected, fmt::format("{} & {}", v1, v2));
}
}
});
});
}
TEST(ARM64EmitterIntegerMath, xor_gpr64_gpr64) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for (auto v1 : vals) {
for (auto v2 : vals) {
tester.clear();
auto expected = v1 ^ v2;
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v1));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), j, v2));
tester.emit(IGen::xor_gpr64_gpr64(tester.generator(), i, j));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
}
}
});
});
}
TEST(ARM64EmitterIntegerMath, not_gpr64) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for (auto v1 : vals) {
auto expected = ~v1;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v1));
tester.emit(IGen::not_gpr64(tester.generator(), i));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
}
});
}
// TODO - not yet implemented
// TEST(ARM64EmitterIntegerMath, shl_gpr64_cl) {
// auto tester = create_tester();
// std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
// INT64_MAX, 117, 32, -348473, 83747382};
// std::vector<u8> sas = {0, 1, 23, 53, 64};
// for_each_register_except_stack_and_scratch(tester, [&](Register i) {
// for (auto v : vals) {
// for (auto sa : sas) {
// auto expected = v << sa;
// tester.clear();
// tester.emit_push_all_gprs(true);
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v));
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), RCX, sa));
// tester.emit(IGen::shl_gpr64_reg(tester.generator(), i, 0));
// tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
// tester.emit_pop_all_gprs(true);
// tester.emit_return();
// EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
// }
// }
// });
// }
// TEST(ARM64EmitterIntegerMath, shr_gpr64_cl) {
// auto tester = create_tester();
// std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
// INT64_MAX, 117, 32, u64(-348473), 83747382};
// std::vector<u8> sas = {0, 1, 23, 53, 64};
// for_each_register_except_stack_and_scratch(tester, [&](Register i) {
// for (auto v : vals) {
// for (auto sa : sas) {
// auto expected = v >> sa;
// tester.clear();
// tester.emit_push_all_gprs(true);
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v));
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), RCX, sa));
// tester.emit(IGen::shr_gpr64_reg(tester.generator(), i, 0));
// tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
// tester.emit_pop_all_gprs(true);
// tester.emit_return();
// EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
// }
// }
// });
// }
// TEST(ARM64EmitterIntegerMath, sar_gpr64_cl) {
// auto tester = create_tester();
// std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
// INT64_MAX, 117, 32, -348473, 83747382};
// std::vector<u8> sas = {0, 1, 23, 53, 64};
// for_each_register_except_stack_and_scratch(tester, [&](Register i) {
// for (auto v : vals) {
// for (auto sa : sas) {
// auto expected = v >> sa;
// tester.clear();
// tester.emit_push_all_gprs(true);
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v));
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), RCX, sa));
// tester.emit(IGen::sar_gpr64_reg(tester.generator(), i, 0));
// tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
// tester.emit_pop_all_gprs(true);
// tester.emit_return();
// EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
// }
// }
// });
// }
TEST(ARM64EmitterIntegerMath, shl_gpr64_u8) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> sas = {0, 1, 23, 53, 64};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for (auto v : vals) {
for (auto sa : sas) {
auto expected = v << sa;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v));
tester.emit(IGen::shl_gpr64_u8(tester.generator(), i, sa));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
}
}
});
}
TEST(ARM64EmitterIntegerMath, shr_gpr64_u8) {
auto tester = create_tester();
std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
INT64_MAX, 117, 32, u64(-348473), 83747382};
std::vector<u8> sas = {0, 1, 23, 53, 64};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for (auto v : vals) {
for (auto sa : sas) {
auto expected = v >> sa;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v));
tester.emit(IGen::shr_gpr64_u8(tester.generator(), i, sa));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
}
}
});
}
TEST(ARM64EmitterIntegerMath, sar_gpr64_u8) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> sas = {0, 1, 23, 53, 64};
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for (auto v : vals) {
for (auto sa : sas) {
auto expected = v >> sa;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, v));
tester.emit(IGen::sar_gpr64_u8(tester.generator(), i, sa));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_RET_EQ(tester, 0, expected);
}
}
});
}
TEST(ARM64EmitterIntegerMath, jumps) {
auto tester = create_tester();
auto x = IGen::jmp_imm(tester.generator());
tester.emit(x);
// read the instruction we just emitted
auto last_instr = tester.read<u32>(tester.size());
// analyze it, ARM is nice in this way, every instruction is just 32bits
// no need to defer and check the immediate like in the x86 tests.
// this has an imm26, the rest are all imm19
EXPECT_EQ(0, last_instr & 0x03ffffff);
x = IGen::je_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
x = IGen::jne_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
x = IGen::jle_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
x = IGen::jge_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
x = IGen::jl_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
x = IGen::jg_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
x = IGen::jbe_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
x = IGen::jae_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
x = IGen::jb_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
x = IGen::ja_imm(tester.generator());
tester.emit(x);
last_instr = tester.read<u32>(tester.size());
EXPECT_EQ(0, (last_instr >> 5) & 0x7ffff);
}
TEST(ARM64EmitterIntegerMath, null) {
CodeTester tester;
auto instr = IGen::null(tester.generator());
EXPECT_EQ(0, instr.emit(nullptr));
}
TEST(ARM64EmitterLoadsAndStores, load_constant_64_and_move_gpr_gpr_64) {
std::vector<u64> u64_constants = {0, UINT64_MAX, INT64_MAX, 7, 12};
// test we can load a 64-bit constant into all gprs, move it to any other gpr, and return it.
// SP is skipping because that's the stack pointer and would prevent us from popping gprs after
auto tester = create_tester();
for (auto constant : u64_constants) {
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, constant));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), j, i));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), j));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_EQ(tester, constant);
});
});
}
}
TEST(ARM64EmitterLoadsAndStores, load_constant_32_unsigned) {
std::vector<u64> u64_constants = {0, UINT32_MAX, INT32_MAX, 7, 12};
// test loading 32-bit constants, with all upper 32-bits zero.
// this uses a different opcode than 64-bit loads.
auto tester = create_tester();
for (auto constant : u64_constants) {
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, UINT64_MAX));
tester.emit(IGen::mov_gpr64_u32(tester.generator(), i, constant));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_EQ(tester, constant);
});
}
}
TEST(ARM64EmitterLoadsAndStores, load_constant_32_signed) {
std::vector<s32> s32_constants = {0, 1, INT32_MAX, INT32_MIN, 12, -1};
// test loading signed 32-bit constants. for values < 0 this will sign extend.
auto tester = create_tester();
for (auto constant : s32_constants) {
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_s32(tester.generator(), i, constant));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_EQ(tester, (u64)constant);
});
}
}
TEST(ARM64EmitterLoadsAndStores, load8s_gpr64_goal_ptr_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64(tester.generator(), X0, X1, X2));
EXPECT_EQ(tester.dump_to_hex_string(true), "20E8A238");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k.id() != i.id() && k.id() != j.id()) {
// TODO - there is a bug here of some sort, the tests will fail if this junk
// initialization is done makes no sense to me yet
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64(tester.generator(), k, i, j));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)3, (u64)0, (u64)0, (u64)-2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)2, (u64)0, (u64)0, (u64)-3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)4, (u64)0, (u64)0, (u64)-1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)5, (u64)0, (u64)0, (u64)0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load8s_gpr64_gpr64_gpr64_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "30E0228B00D29F38");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)3 + 3, (u64)0, (u64)0, (u64)-2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)2 + 3, (u64)0, (u64)0, (u64)-3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)4 + 3, (u64)0, (u64)0, (u64)-1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)5 + 3, (u64)0, (u64)0, (u64)0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load8s_gpr64_gpr64_gpr64_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "3000028B100E00D100028039");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)3 + 3, (u64)0, (u64)0, (u64)-2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)2 + 3, (u64)0, (u64)0, (u64)-3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)4 + 3, (u64)0, (u64)0, (u64)-1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, (u64)5 + 3, (u64)0, (u64)0, (u64)0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load8u_gpr64_goal_ptr_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64(tester.generator(), X0, X1, X2));
EXPECT_EQ(tester.dump_to_hex_string(true), "20E86238");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64(tester.generator(), k, i, j));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, memory, 3, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, memory, 2, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, memory, 4, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, memory, 5, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load8u_gpr64_gpr64_gpr64_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "30E0228B00D25F38");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 3 + 3, 0, 0, 0xfe);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 2 + 3, 0, 0, 0xfd);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 4 + 3, 0, 0, 0xff);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 5 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load8u_gpr64_gpr64_gpr64_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "3000028B100E00D100024039");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 3 + 3, 0, 0, 0xfe);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 2 + 3, 0, 0, 0xfd);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 4 + 3, 0, 0, 0xff);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 5 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load16s_gpr64_goal_ptr_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64(tester.generator(), X0, X1, X2));
EXPECT_EQ(tester.dump_to_hex_string(true), "20E8A278");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64(tester.generator(), k, i, j));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s16 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 6, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 4, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 10, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load16s_gpr64_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "30E0228B00D29F78");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u16 memory[8] = {0, 0, 0xfffd, 0xfffe, 0xffff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, memory, 6 + 3, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, memory, 4 + 3, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, memory, 8 + 3, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, memory, 10 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load16s_gpr64_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "3000028B100E00D100028079");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u16 memory[8] = {0, 0, 0xfffd, 0xfffe, 0xffff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 6 + 3, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 4 + 3, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8 + 3, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 10 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load16u_gpr64_goal_ptr_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64(tester.generator(), X0, X1, X2));
EXPECT_EQ(tester.dump_to_hex_string(true), "20E86278");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64(tester.generator(), k, i, j));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s16 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 6, 0, 0, 0xfffe);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 4, 0, 0, 0xfffd);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8, 0, 0, 0xffff);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 10, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load16u_gpr64_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "30E0228B00D25F78");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u16 memory[8] = {0, 0, 0xfffd, 0xfffe, 0xffff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 6 + 3, 0, 0, 0xfffe);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 4 + 3, 0, 0, 0xfffd);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8 + 3, 0, 0, 0xffff);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 10 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load16u_gpr64_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "3000028B100E00D100024079");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u16 memory[8] = {0, 0, 0xfffd, 0xfffe, 0xffff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 6 + 3, 0, 0, 0xfffe);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 4 + 3, 0, 0, 0xfffd);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8 + 3, 0, 0, 0xffff);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 10 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load32s_gpr64_goal_ptr_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64(tester.generator(), X0, X1, X2));
EXPECT_EQ(tester.dump_to_hex_string(true), "20E8A2B8");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64(tester.generator(), k, i, j));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s32 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 12, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 16, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 20, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load32s_gpr64_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "30E0228B00D29FB8");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u32 memory[8] = {0, 0, 0xfffffffd, 0xfffffffe, 0xffffffff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 12 + 3, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8 + 3, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 16 + 3, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 20 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load32s_gpr64_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "3000028B100E00D1000280B9");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u32 memory[8] = {0, 0, 0xfffffffd, 0xfffffffe, 0xffffffff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 12 + 3, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8 + 3, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 16 + 3, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 20 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load32u_gpr64_goal_ptr_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64(tester.generator(), X0, X1, X2));
EXPECT_EQ(tester.dump_to_hex_string(true), "20E862B8");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64(tester.generator(), k, i, j));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s32 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 12, 0, 0, 0xfffffffe);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8, 0, 0, 0xfffffffd);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 16, 0, 0, 0xffffffff);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 20, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load32u_gpr64_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "30E0228B00D25FB8");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s32 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 12 + 3, 0, 0, 0xfffffffe);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8 + 3, 0, 0, 0xfffffffd);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 16 + 3, 0, 0, 0xffffffff);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 20 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load32u_gpr64_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "3000028B100E00D1000640B8");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
u32 memory[8] = {0, 0, 0xfffffffd, 0xfffffffe, 0xffffffff, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 12 + 3, 0, 0, 0xfffffffe);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 8 + 3, 0, 0, 0xfffffffd);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 16 + 3, 0, 0, 0xffffffff);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 20 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load64_gpr64_goal_ptr_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64(tester.generator(), X0, X1, X2));
EXPECT_EQ(tester.dump_to_hex_string(true), "20E862F8");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64(tester.generator(), k, i, j));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s64 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 24, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 16, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 32, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 40, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load64_gpr64_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "30E0228B00D25FF8");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s64 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 24 + 3, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 16 + 3, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 32 + 3, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 40 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load64_gpr64_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), X0, X1, X2, -3));
EXPECT_EQ(tester.dump_to_hex_string(true), "3000028B100E00D1000640F8");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except_stack_and_scratch(tester, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// fill k with junk
if (k != i && k != j) {
// TODO
// tester.emit(IGen::mov_gpr64_u64(tester.generator(), k, (iter & 1) ? 0 : UINT64_MAX));
}
// load into k
tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), k, i, j, -3));
// move k to return register
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s64 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 24 + 3, 0, 0, -2);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 16 + 3, 0, 0, -3);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 32 + 3, 0, 0, -1);
EXPECT_EXECUTE_4ARG_EQ(tester, (u64)memory, 40 + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store8_gpr64_gpr64_plus_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(
IGen::store8_gpr64_gpr64_plus_gpr64(tester.generator(), tester.get_return_reg(), RCX, RDX));
EXPECT_EQ(tester.dump_to_hex_string(true), "02E82138");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store!
tester.emit(IGen::store8_gpr64_gpr64_plus_gpr64(tester.generator(), i, j, k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s8 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 3, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], 7);
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store8_gpr64_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::store8_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(),
tester.get_return_reg(), RCX, RDX, 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "10E0218B02C20038");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store
tester.emit(IGen::store8_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), i, j, k, -3));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s8 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 6, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], 7);
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store8_gpr64_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::store8_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(),
tester.get_return_reg(), RCX, RDX, 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B1032009102020039");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store
tester.emit(IGen::store8_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), i, j, k, -3));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s8 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 6, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], 7);
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store16_gpr64_gpr64_plus_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(
IGen::store16_gpr64_gpr64_plus_gpr64(tester.generator(), RCX, tester.get_return_reg(), R8));
EXPECT_EQ(tester.dump_to_hex_string(true), "28E82078");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store!
tester.emit(IGen::store16_gpr64_gpr64_plus_gpr64(tester.generator(), i, j, k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s16 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 6, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], s16(0xff07));
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store16_gpr64_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::store16_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(),
tester.get_return_reg(), RCX, R8, 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "10E0218B08C20078");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store
tester.emit(IGen::store16_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), i, j, k, -3));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s16 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 6 + 3, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], s16(0xff07));
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store16_gpr64_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::store16_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(),
tester.get_return_reg(), RCX, R8, 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B1032009108020079");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store
tester.emit(IGen::store16_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), i, j, k, -3));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s16 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 6 + 3, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], s16(0xff07));
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store32_gpr64_gpr64_plus_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(
IGen::store32_gpr64_gpr64_plus_gpr64(tester.generator(), RCX, tester.get_return_reg(), R8));
EXPECT_EQ(tester.dump_to_hex_string(true), "28E820B8");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store!
tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64(tester.generator(), i, j, k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s32 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 12, 0xffffffff12341234, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], 0x12341234);
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store32_gpr64_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(),
tester.get_return_reg(), RCX, R8, 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B10320091080200B9");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store
tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), i, j, k, -3));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s32 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 12 + 3, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], s32(0xffffff07));
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store32_gpr64_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(),
tester.get_return_reg(), RCX, R8, 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B10320091080200B9");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store
tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), i, j, k, -3));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s32 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 12 + 3, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], s32(0xffffff07));
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store64_gpr64_gpr64_plus_gpr64) {
auto tester = create_tester();
tester.clear();
tester.emit(
IGen::store64_gpr64_gpr64_plus_gpr64(tester.generator(), RCX, tester.get_return_reg(), R8));
EXPECT_EQ(tester.dump_to_hex_string(true), "28E820F8");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store!
tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64(tester.generator(), i, j, k));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s64 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 24, 0xffffffff12341234, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], 0xffffffff12341234);
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store64_gpr64_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(),
tester.get_return_reg(), RCX, R8, 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B10320091080200F9");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store
tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64_plus_s8(tester.generator(), i, j, k, -3));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s64 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 24 + 3, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], s64(0xffffffffffffff07));
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, store64_gpr64_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.clear();
tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(),
tester.get_return_reg(), RCX, R8, 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B10320091080200F9");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i, j}, [&](Register k) {
tester.clear();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
tester.emit(IGen::pop_gpr64(tester.generator(), k)); // k will have the value to store.
// store
tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64_plus_s32(tester.generator(), i, j, k, -3));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_return();
// prepare the memory:
s64 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0};
// run!
const auto did_execute =
execute_tester_no_cmp(tester, (u64)memory, 24 + 3, 0xffffffffffffff07, 0);
if (did_execute) {
EXPECT_EQ(memory[2], 3);
EXPECT_EQ(memory[3], s64(0xffffffffffffff07));
EXPECT_EQ(memory[4], 1);
}
});
});
});
}
TEST(ARM64EmitterLoadsAndStores, load64_rip) {
auto tester = create_tester();
tester.emit(IGen::load64_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "60000058");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::load64_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"600000586100005862000058630000586400005865000058660000586700005868000058690000586A0000"
"586B0000586C0000586D0000586E0000586F00005870000058710000587200005873000058740000587500"
"0058760000587700005878000058790000587A0000587B0000587C0000587D0000587E000058");
}
TEST(ARM64EmitterLoadsAndStores, load32s_rip) {
auto tester = create_tester();
tester.emit(IGen::load32s_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "60000098");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::load32s_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"600000986100009862000098630000986400009865000098660000986700009868000098690000986A0000"
"986B0000986C0000986D0000986E0000986F00009870000098710000987200009873000098740000987500"
"0098760000987700009878000098790000987A0000987B0000987C0000987D0000987E000098");
}
TEST(ARM64EmitterLoadsAndStores, load32u_rip) {
auto tester = create_tester();
tester.emit(IGen::load32u_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "60000018");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::load32u_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"600000186100001862000018630000186400001865000018660000186700001868000018690000186A0000"
"186B0000186C0000186D0000186E0000186F00001870000018710000187200001873000018740000187500"
"0018760000187700001878000018790000187A0000187B0000187C0000187D0000187E000018");
}
TEST(ARM64EmitterLoadsAndStores, load16u_rip) {
auto tester = create_tester();
tester.emit(IGen::load16u_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000009000324079");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::load16u_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"10000090003240791000009001324079100000900232407910000090033240791000009004324079100000"
"90053240791000009006324079100000900732407910000090083240791000009009324079100000900A32"
"4079100000900B324079100000900C324079100000900D324079100000900E324079100000900F32407910"
"00009010324079100000901132407910000090123240791000009013324079100000901432407910000090"
"153240791000009016324079100000901732407910000090183240791000009019324079100000901A3240"
"79100000901B324079100000901C324079100000901D324079100000901E324079");
}
TEST(ARM64EmitterLoadsAndStores, load16s_rip) {
auto tester = create_tester();
tester.emit(IGen::load16s_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000009000328079");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::load16s_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"10000090003280791000009001328079100000900232807910000090033280791000009004328079100000"
"90053280791000009006328079100000900732807910000090083280791000009009328079100000900A32"
"8079100000900B328079100000900C328079100000900D328079100000900E328079100000900F32807910"
"00009010328079100000901132807910000090123280791000009013328079100000901432807910000090"
"153280791000009016328079100000901732807910000090183280791000009019328079100000901A3280"
"79100000901B328079100000901C328079100000901D328079100000901E328079");
}
TEST(ARM64EmitterLoadsAndStores, load8s_rip) {
auto tester = create_tester();
tester.emit(IGen::load8s_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000009000328039");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::load8s_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"10000090003280391000009001328039100000900232803910000090033280391000009004328039100000"
"90053280391000009006328039100000900732803910000090083280391000009009328039100000900A32"
"8039100000900B328039100000900C328039100000900D328039100000900E328039100000900F32803910"
"00009010328039100000901132803910000090123280391000009013328039100000901432803910000090"
"153280391000009016328039100000901732803910000090183280391000009019328039100000901A3280"
"39100000901B328039100000901C328039100000901D328039100000901E328039");
}
TEST(ARM64EmitterLoadsAndStores, load8u_rip) {
auto tester = create_tester();
tester.emit(IGen::load8u_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000009000324039");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::load8u_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"10000090003240391000009001324039100000900232403910000090033240391000009004324039100000"
"90053240391000009006324039100000900732403910000090083240391000009009324039100000900A32"
"4039100000900B324039100000900C324039100000900D324039100000900E324039100000900F32403910"
"00009010324039100000901132403910000090123240391000009013324039100000901432403910000090"
"153240391000009016324039100000901732403910000090183240391000009019324039100000901A3240"
"39100000901B324039100000901C324039100000901D324039100000901E324039");
}
TEST(ARM64EmitterLoadsAndStores, store64_rip_s32) {
auto tester = create_tester();
tester.emit(IGen::store64_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "10000090003200F9");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::store64_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"10000090003200F910000090013200F910000090023200F910000090033200F910000090043200F9100000"
"90053200F910000090063200F910000090073200F910000090083200F910000090093200F9100000900A32"
"00F9100000900B3200F9100000900C3200F9100000900D3200F9100000900E3200F9100000900F3200F910"
"000090103200F910000090113200F910000090123200F910000090133200F910000090143200F910000090"
"153200F910000090163200F910000090173200F910000090183200F910000090193200F9100000901A3200"
"F9100000901B3200F9100000901C3200F9100000901D3200F9100000901E3200F9");
}
TEST(ARM64EmitterLoadsAndStores, store32_rip_s32) {
auto tester = create_tester();
tester.emit(IGen::store32_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "10000090003200B9");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::store32_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"10000090003200B910000090013200B910000090023200B910000090033200B910000090043200B9100000"
"90053200B910000090063200B910000090073200B910000090083200B910000090093200B9100000900A32"
"00B9100000900B3200B9100000900C3200B9100000900D3200B9100000900E3200B9100000900F3200B910"
"000090103200B910000090113200B910000090123200B910000090133200B910000090143200B910000090"
"153200B910000090163200B910000090173200B910000090183200B910000090193200B9100000901A3200"
"B9100000901B3200B9100000901C3200B9100000901D3200B9100000901E3200B9");
}
TEST(ARM64EmitterLoadsAndStores, store16_rip_s32) {
auto tester = create_tester();
tester.emit(IGen::store16_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000009000320079");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::store16_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"10000090003200791000009001320079100000900232007910000090033200791000009004320079100000"
"90053200791000009006320079100000900732007910000090083200791000009009320079100000900A32"
"0079100000900B320079100000900C320079100000900D320079100000900E320079100000900F32007910"
"00009010320079100000901132007910000090123200791000009013320079100000901432007910000090"
"153200791000009016320079100000901732007910000090183200791000009019320079100000901A3200"
"79100000901B320079100000901C320079100000901D320079100000901E320079");
}
TEST(ARM64EmitterLoadsAndStores, store8_rip_s32) {
auto tester = create_tester();
tester.emit(IGen::store8_rip_s32(tester.generator(), tester.get_return_reg(), 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000009000320039");
tester.clear();
for_each_register_except(tester, {}, [&](Register i) {
tester.emit(IGen::store8_rip_s32(tester.generator(), i, 12));
});
EXPECT_EQ(tester.dump_to_hex_string(true),
"10000090003200391000009001320039100000900232003910000090033200391000009004320039100000"
"90053200391000009006320039100000900732003910000090083200391000009009320039100000900A32"
"0039100000900B320039100000900C320039100000900D320039100000900E320039100000900F32003910"
"00009010320039100000901132003910000090123200391000009013320039100000901432003910000090"
"153200391000009016320039100000901732003910000090183200391000009019320039100000901A3200"
"39100000901B320039100000901C320039100000901D320039100000901E320039");
}
TEST(ARM64EmitterLoadsAndStores, static_addr) {
auto tester = create_tester();
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, 12345)); // load test reg with junk
int start_of_adr = tester.size();
auto adr_instr = IGen::static_addr(tester.generator(), i, 1);
tester.emit(adr_instr);
// Patch ADR to point at tester.data() + 1
const s64 target = (s64)(tester.code_address() + 1);
const s64 pc = (s64)(tester.code_address() + start_of_adr);
const s64 offset = target - pc;
ASSERT(offset >= -(1 << 20));
ASSERT(offset < (1 << 20));
u32 imm = static_cast<u32>(offset) & 0x1fffff;
u32 immlo = imm & 0x3;
u32 immhi = (imm >> 2) & 0x7ffff;
u32 instr = tester.read<u32>(start_of_adr);
instr &= ~((0x3 << 29) | (0x7ffff << 5));
instr |= (immlo << 29);
instr |= (immhi << 5);
tester.write<u32>(instr, start_of_adr);
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), tester.get_return_reg(), i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EXECUTE_EQ(tester, (u64)(tester.data()) + 1);
});
}
TEST(ARM64Emitter, LEA) {
auto tester = create_tester();
tester.emit(IGen::lea_reg_plus_off(tester.generator(), X4, SP, -3));
tester.emit(IGen::lea_reg_plus_off(tester.generator(), X4, X12, -3));
tester.emit(IGen::lea_reg_plus_off(tester.generator(), X13, SP, -3));
tester.emit(IGen::lea_reg_plus_off(tester.generator(), X13, X12, -3));
tester.emit(IGen::lea_reg_plus_off(tester.generator(), X4, SP, -300));
tester.emit(IGen::lea_reg_plus_off(tester.generator(), X4, X12, -300));
tester.emit(IGen::lea_reg_plus_off(tester.generator(), X13, SP, -300));
tester.emit(IGen::lea_reg_plus_off(tester.generator(), X13, X12, -300));
EXPECT_EQ(tester.dump_to_hex_string(true),
"E4030091840C00D184010091840C00D1ED030091AD0D00D18D010091AD0D00D1E403009184B004D1840100"
"9184B004D1ED030091ADB104D18D010091ADB104D1");
}
TEST(ARM64EmitterXMM, StackLoad32) {
auto tester = create_tester();
tester.emit(IGen::load32_xmm32_gpr64_plus_s32(tester.generator(), V0 + 3, SP, -1234));
tester.emit(IGen::load32_xmm32_gpr64_plus_s32(tester.generator(), V0 + 13, SP, -1234));
EXPECT_EQ(tester.dump_to_hex_string(true),
"F0030091519A80D2100211CB030240BDF0030091519A80D2100211CB0D0240BD");
}
TEST(ARM64EmitterXMM, StackLoad8) {
auto tester = create_tester();
tester.emit(IGen::load32_xmm32_gpr64_plus_s8(tester.generator(), V0 + 3, SP, -12));
tester.emit(IGen::load32_xmm32_gpr64_plus_s8(tester.generator(), V0 + 13, SP, -12));
EXPECT_EQ(tester.dump_to_hex_string(true),
"F0030091910180D2100211CB030240BDF0030091910180D2100211CB0D0240BD");
}
TEST(ARM64EmitterXMM, StackLoadFull32) {
auto tester = create_tester();
tester.emit(IGen::load128_simd128_gpr64_s32(tester.generator(), V0 + 3, SP, -1234));
tester.emit(IGen::load128_simd128_gpr64_s32(tester.generator(), V0 + 13, SP, -1234));
EXPECT_EQ(tester.dump_to_hex_string(true), "F0030091104A13D10302C03DF0030091104A13D10D02C03D");
}
TEST(ARM64EmitterXMM, StackLoadFull8) {
auto tester = create_tester();
tester.emit(IGen::load128_simd128_gpr64_s8(tester.generator(), V0 + 3, SP, -12));
tester.emit(IGen::load128_simd128_gpr64_s8(tester.generator(), V0 + 13, SP, -12));
EXPECT_EQ(tester.dump_to_hex_string(true), "F0030091103200D10302C03DF0030091103200D10D02C03D");
}
TEST(ARM64EmitterXMM, StackStore32) {
auto tester = create_tester();
tester.emit(IGen::store32_xmm32_gpr64_plus_s32(tester.generator(), SP, V0 + 3, -1234));
tester.emit(IGen::store32_xmm32_gpr64_plus_s32(tester.generator(), SP, V0 + 13, -1234));
EXPECT_EQ(tester.dump_to_hex_string(true),
"F0030091519A80D2100211CB030200BDF0030091519A80D2100211CB0D0200BD");
}
TEST(ARM64EmitterXMM, StackStore8) {
auto tester = create_tester();
tester.emit(IGen::store32_xmm32_gpr64_plus_s8(tester.generator(), SP, V0 + 3, -12));
tester.emit(IGen::store32_xmm32_gpr64_plus_s8(tester.generator(), SP, V0 + 13, -12));
EXPECT_EQ(tester.dump_to_hex_string(true),
"F0030091910180D2100211CB030200BDF0030091910180D2100211CB0D0200BD");
}
TEST(ARM64EmitterXMM, StackStoreFull32) {
auto tester = create_tester();
tester.emit(IGen::store128_gpr64_simd128_s32(tester.generator(), SP, V0 + 3, -1234));
tester.emit(IGen::store128_gpr64_simd128_s32(tester.generator(), SP, V0 + 13, -1234));
EXPECT_EQ(tester.dump_to_hex_string(true), "F0030091104A13D103DA0300F0030091104A13D10DDA0300");
}
TEST(ARM64EmitterXMM, StackStoreFull8) {
auto tester = create_tester();
tester.emit(IGen::store128_gpr64_simd128_s8(tester.generator(), SP, V0 + 3, -12));
tester.emit(IGen::store128_gpr64_simd128_s8(tester.generator(), SP, V0 + 13, -12));
EXPECT_EQ(tester.dump_to_hex_string(true), "F0030091103200D103DA0300F0030091103200D10DDA0300");
}
TEST(ARM64EmitterXMM, SqrtS) {
auto tester = create_tester();
tester.emit(IGen::sqrt_f32(tester.generator(), V0 + 1, V0 + 2));
tester.emit(IGen::sqrt_f32(tester.generator(), V0 + 11, V0 + 2));
tester.emit(IGen::sqrt_f32(tester.generator(), V0 + 1, V0 + 12));
tester.emit(IGen::sqrt_f32(tester.generator(), V0 + 11, V0 + 12));
EXPECT_EQ(tester.dump_to_hex_string(true), "40C0211E40C02B1E80C1211E80C12B1E");
}
TEST(ARM64EmitterXmm32, load32_xmm32_gpr64_plus_gpr64) {
auto tester = create_tester();
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64(tester.generator(), V3, X0, X1));
EXPECT_EQ(tester.dump_to_hex_string(true), "23E860BC");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {}, [&](Register k) {
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// fill k with junk
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, (iter & 1) ? 0 : UINT64_MAX));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + k.id(), i));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// load into k
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64(tester.generator(), V0 + k.id(), i, j));
// move to return
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + k.id()));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
// prepare the memory:
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
// run!
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 3 * sizeof(float), 0, 0, 3.45f);
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 2 * sizeof(float), 0, 0, 1.23f);
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 4 * sizeof(float), 0, 0, 5.67f);
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 5 * sizeof(float), 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterXmm32, load32_xmm32_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(tester.generator(), V3, X0, X1, -1));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B310080D2100211CB030240BD");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {}, [&](Register k) {
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// fill k with junk
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, (iter & 1) ? 0 : UINT64_MAX));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + k.id(), i));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
// load into k
tester.emit(
IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(tester.generator(), V0 + k.id(), i, j, -3));
// move to return
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + k.id()));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
// prepare the memory:
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
// run!
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 3 * sizeof(float) + 3, 0, 0, 3.45f);
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 2 * sizeof(float) + 3, 0, 0, 1.23f);
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 4 * sizeof(float) + 3, 0, 0, 5.67f);
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 5 * sizeof(float) + 3, 0, 0, 0);
iter++;
});
});
});
}
TEST(ARM64EmitterXmm32, load32_xmm32_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(tester.generator(), V3, X0, X1, -1));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B310080D2100211CB030240BD");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {}, [&](Register k) {
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1)));
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0)));
// fill k with junk
tester.emit(IGen::mov_gpr64_u64(tester.generator(), i, (iter & 1) ? 0 : UINT64_MAX));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + k.id(), i));
// pop args into appropriate register
tester.emit(IGen::pop_gpr64(tester.generator(), i)); // i will have offset 0
tester.emit(IGen::pop_gpr64(tester.generator(), j)); // j will have offset 1
s64 offset = (iter & 1) ? INT32_MAX : INT32_MIN;
// load into k
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(tester.generator(), V0 + k.id(), i,
j, offset));
// move to return
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + k.id()));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
// prepare the memory:
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
// run!
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 3 * sizeof(float) - offset, 0, 0, 3.45f);
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 2 * sizeof(float) - offset, 0, 0, 1.23f);
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 4 * sizeof(float) - offset, 0, 0, 5.67f);
EXPECT_EXECUTE_RET_4ARG_EQ(tester, (u64)memory, 5 * sizeof(float) - offset, 0, 0, 0);
iter++;
});
});
});
}
namespace {
template <typename T>
float as_float(T x) {
float result;
memcpy(&result, &x, sizeof(float));
return result;
}
u32 as_u32(float x) {
u32 result;
memcpy(&result, &x, 4);
return result;
}
} // namespace
TEST(ARM64EmitterXmm32, store32_xmm32_gpr64_plus_gpr64) {
auto tester = create_tester();
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64(tester.generator(), X0, X1, XMM7));
EXPECT_EQ(tester.dump_to_hex_string(true), "17C821BC");
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {}, [&](Register k) {
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1))); // addr2
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0))); // addr1
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2))); // value
// pop value into addr1 GPR
tester.emit(IGen::pop_gpr64(tester.generator(), i));
// move to XMM
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + k.id(), i));
// pop addrs
tester.emit(IGen::pop_gpr64(tester.generator(), i));
tester.emit(IGen::pop_gpr64(tester.generator(), j));
// store
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64(tester.generator(), i, j, V0 + k.id()));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
// prepare the memory:
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_NO_CMP(tester, (u64)memory, 12, as_u32(1.234f), 0);
EXPECT_EXECUTE_IF_NATIVE(tester, {
EXPECT_FLOAT_EQ(memory[2], 1.23f);
EXPECT_FLOAT_EQ(memory[3], 1.234f);
EXPECT_FLOAT_EQ(memory[4], 5.67f);
});
});
});
});
}
TEST(ARM64EmitterXmm32, store32_xmm32_gpr64_plus_gpr64_plus_s8) {
auto tester = create_tester();
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(tester.generator(), X0, X1, V3, -1));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B310080D2100211CB030200BD");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {}, [&](Register k) {
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1))); // addr2
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0))); // addr1
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2))); // value
// pop value into addr1 GPR
tester.emit(IGen::pop_gpr64(tester.generator(), i));
// move to XMM
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + k.id(), i));
// pop addrs
tester.emit(IGen::pop_gpr64(tester.generator(), i));
tester.emit(IGen::pop_gpr64(tester.generator(), j));
s64 offset = (iter & 1) ? INT8_MAX : INT8_MIN;
// load into k
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(tester.generator(), i, j,
V0 + k.id(), offset));
// move to return
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + k.id()));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
// prepare the memory:
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_NO_CMP(tester, (u64)memory, 12 - offset, as_u32(1.234f), 0);
EXPECT_EXECUTE_IF_NATIVE(tester, {
EXPECT_FLOAT_EQ(memory[2], 1.23f);
EXPECT_FLOAT_EQ(memory[3], 1.234f);
EXPECT_FLOAT_EQ(memory[4], 5.67f);
});
});
});
});
}
TEST(ARM64EmitterXmm32, store32_xmm32_gpr64_plus_gpr64_plus_s32) {
auto tester = create_tester();
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(tester.generator(), X0, X1, V3, -1));
EXPECT_EQ(tester.dump_to_hex_string(true), "1000018B310080D2100211CB030200BD");
int iter = 0;
for_each_register_except_stack_and_scratch(tester, [&](Register i) {
for_each_register_except(tester, {tester.get_stack_reg(), X16, i}, [&](Register j) {
for_each_register_except(tester, {}, [&](Register k) {
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
// push args to the stack
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(1))); // addr2
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(0))); // addr1
tester.emit(IGen::push_gpr64(tester.generator(), tester.get_c_abi_arg_reg(2))); // value
// pop value into addr1 GPR
tester.emit(IGen::pop_gpr64(tester.generator(), i));
// move to XMM
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + k.id(), i));
// pop addrs
tester.emit(IGen::pop_gpr64(tester.generator(), i));
tester.emit(IGen::pop_gpr64(tester.generator(), j));
s64 offset = (iter & 1) ? INT32_MAX : INT32_MIN;
// load into k
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(tester.generator(), i, j,
V0 + k.id(), offset));
// move to return
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + k.id()));
// return!
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
// prepare the memory:
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
// run!
EXPECT_EXECUTE_4ARG_NO_CMP(tester, (u64)memory, 12 - offset, as_u32(1.234f), 0);
EXPECT_EXECUTE_IF_NATIVE(tester, {
EXPECT_FLOAT_EQ(memory[2], 1.23f);
EXPECT_FLOAT_EQ(memory[3], 1.234f);
EXPECT_FLOAT_EQ(memory[4], 5.67f);
});
});
});
});
}
// TEST(ARM64EmitterXmm32, static_load_xmm32) {
// // TODO - int32 max is not supported in current arm64 impl because
// // the assumption is that we don't need that much range
// auto tester = create_tester();
// for_each_register_except(tester, {}, [&](Register i) {
// tester.clear();
// tester.emit_push_all_simd();
// tester.emit_push_all_gprs(true);
// auto loc_of_load = tester.size();
// auto load_instr = IGen::static_load_f32(tester.generator(), V0 + i.id(), INT32_MAX);
// tester.emit(load_instr);
// tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + i.id()));
// tester.emit_pop_all_gprs(true);
// tester.emit_pop_all_simd();
// tester.emit_return();
// auto loc_of_float = tester.emit_data(float(1.2345f));
// // patch offset
// tester.write<s32>(loc_of_float - loc_of_load - load_instr.length(),
// loc_of_load + load_instr.offset_of_disp());
// EXPECT_EXECUTE_RET_4ARG_EQ(tester, 0, 0, 0, 0, 1.2345f);
// });
// }
// TEST(ARM64EmitterXmm32, static_store_xmm32) {
// // TODO - int32 max is not supported in current arm64 impl because
// // the assumption is that we don't need that much range
// auto tester = create_tester();
// for_each_register_except(tester, {}, [&](Register i) {
// tester.clear();
// tester.emit_push_all_simd();
// tester.emit_push_all_gprs(true);
// tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + i.id(),
// tester.get_c_abi_arg_reg(0)));
// auto loc_of_store = tester.size();
// auto store_instr = IGen::static_store_f32(tester.generator(), V0 + i.id(), INT32_MAX);
// tester.emit(store_instr);
// tester.emit_pop_all_gprs(true);
// tester.emit_pop_all_simd();
// tester.emit_return();
// auto loc_of_float = tester.emit_data(float(1.2345f));
// tester.write<s32>(loc_of_float - loc_of_store - store_instr.length(),
// loc_of_store + store_instr.offset_of_disp());
// EXPECT_EXECUTE_4ARG_NO_CMP(tester, as_u32(-44.567f), 0, 0, 0);
// EXPECT_FLOAT_EQ(-44.567f, tester.read<float>(loc_of_float));
// });
// }
TEST(ARM64EmitterXmm32, ucomiss) {
auto tester = create_tester();
tester.emit(IGen::cmp_f32_f32(tester.generator(), V13, V14));
EXPECT_EQ("A0212E1E", tester.dump_to_hex_string(true));
}
TEST(ARM64EmitterXmm32, mul) {
auto tester = create_tester();
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
for (auto f : vals) {
for (auto g : vals) {
for_each_register_except(tester, {}, [&](Register i) {
for_each_register_except(tester, {i}, [&](Register j) {
auto expected = f * g;
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
u64 val = 0;
memcpy(&val, &f, sizeof(float));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), X0, val));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + i.id(), X0));
memcpy(&val, &g, sizeof(float));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), X0, val));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + j.id(), X0));
tester.emit(IGen::mul_f32_f32(tester.generator(), V0 + j.id(), V0 + i.id()));
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + j.id()));
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
EXPECT_EXECUTE_RET_4ARG_FLOAT_EQ(tester, 0, 0, 0, 0, expected);
});
});
}
}
}
TEST(ARM64EmitterXmm32, div) {
auto tester = create_tester();
std::vector<float> vals = {1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
for (auto f : vals) {
for (auto g : vals) {
for_each_register_except(tester, {}, [&](Register i) {
for_each_register_except(tester, {i}, [&](Register j) {
auto expected = g / f;
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
u64 val = 0;
memcpy(&val, &f, sizeof(float));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), X0, val));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + i.id(), X0));
memcpy(&val, &g, sizeof(float));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), X0, val));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + j.id(), X0));
tester.emit(IGen::div_f32_f32(tester.generator(), V0 + j.id(), V0 + i.id()));
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + j.id()));
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
EXPECT_EXECUTE_RET_4ARG_EQ(tester, 0, 0, 0, 0, expected);
});
});
}
}
}
TEST(ARM64EmitterXmm32, add) {
auto tester = create_tester();
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
for (auto f : vals) {
for (auto g : vals) {
for_each_register_except(tester, {}, [&](Register i) {
for_each_register_except(tester, {i}, [&](Register j) {
auto expected = g + f;
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
u64 val = 0;
memcpy(&val, &f, sizeof(float));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), X0, val));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + i.id(), X0));
memcpy(&val, &g, sizeof(float));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), X0, val));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + j.id(), X0));
tester.emit(IGen::add_f32_f32(tester.generator(), V0 + j.id(), V0 + i.id()));
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + j.id()));
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
EXPECT_EXECUTE_RET_4ARG_FLOAT_EQ(tester, 0, 0, 0, 0, expected);
});
});
}
}
}
TEST(ARM64EmitterXmm32, sub) {
auto tester = create_tester();
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
for (auto f : vals) {
for (auto g : vals) {
for_each_register_except(tester, {}, [&](Register i) {
for_each_register_except(tester, {i}, [&](Register j) {
auto expected = g - f;
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
u64 val = 0;
memcpy(&val, &f, sizeof(float));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), X0, val));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + i.id(), X0));
memcpy(&val, &g, sizeof(float));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), X0, val));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + j.id(), X0));
tester.emit(IGen::sub_f32_f32(tester.generator(), V0 + j.id(), V0 + i.id()));
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + j.id()));
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
EXPECT_EXECUTE_RET_4ARG_FLOAT_EQ(tester, 0, 0, 0, 0, expected);
});
});
}
}
}
TEST(ARM64EmitterXmm32, float_to_int) {
auto tester = create_tester();
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f,
7.545f, 0.1f, 0.9f, -0.1f, -0.9f};
for (auto g : vals) {
for_each_register_except(tester, {}, [&](Register i) {
for_each_register_except(tester, {X0, i}, [&](Register j) {
s32 expected = g;
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
u64 val = 0;
memcpy(&val, &g, sizeof(float));
tester.emit(IGen::mov_gpr64_u64(tester.generator(), X0, val));
tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + i.id(), X0));
tester.emit(IGen::f32_to_int32(tester.generator(), j, V0 + i.id()));
tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), X0, j));
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
EXPECT_EXECUTE_RET_4ARG_EQ(tester, 0, 0, 0, 0, expected);
});
});
}
}
TEST(ARM64EmitterXmm32, int_to_float) {
auto tester = create_tester();
std::vector<s64> vals = {0, 1, -1, INT32_MAX, -3457343, 7, INT32_MIN};
for (auto g : vals) {
for_each_register_except(tester, {}, [&](Register i) {
for_each_register_except(tester, {i}, [&](Register j) {
float expected = g;
tester.clear();
tester.emit_push_all_simd();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(tester.generator(), j, g));
tester.emit(IGen::int32_to_f32(tester.generator(), V0 + i.id(), j));
tester.emit(IGen::movd_gpr32_f32(tester.generator(), X0, V0 + i.id()));
tester.emit_pop_all_gprs(true);
tester.emit_pop_all_simd();
tester.emit_return();
EXPECT_EXECUTE_RET_4ARG_FLOAT_EQ(tester, 0, 0, 0, 0, expected);
});
});
}
}
// TEST(ARM64EmitterSlow, xmm32_move) {
// std::vector<u32> u32_constants = {0, INT32_MAX, UINT32_MAX, 17};
// // test moving between xmms (32-bit) and gprs.
// auto tester = create_tester();
// for (auto constant : u32_constants) {
// for (int r1 = 0; r1 < 16; r1++) {
// if (r1 == SP) {
// continue;
// }
// for (int r2 = 0; r2 < 16; r2++) {
// if (r2 == SP) {
// continue;
// }
// for (int r3 = 0; r3 < 16; r3++) {
// for (int r4 = 0; r4 < 16; r4++) {
// tester.clear();
// tester.emit_push_all_simd();
// tester.emit_push_all_gprs(true);
// // move constant to gpr
// tester.emit(IGen::mov_gpr64_u32(tester.generator(), r1, constant));
// // move gpr to xmm
// tester.emit(IGen::movd_f32_gpr32(tester.generator(), V0 + r3, r1));
// // move xmm to xmm
// tester.emit(IGen::mov_f32_f32(tester.generator(), V0 + r4, V0 + r3));
// // move xmm to gpr
// tester.emit(IGen::movd_gpr32_f32(tester.generator(), r2, V0 + r4));
// // return!
// tester.emit(IGen::mov_gpr64_gpr64(tester.generator(), X0, r2));
// tester.emit_pop_all_gprs(true);
// tester.emit_pop_all_simd();
// tester.emit_return();
// }
// }
// }
// }
// }
// // todo - finish this test
// }