[dynarmic] jit fix branch v2 #203
7 changed files with 48 additions and 56 deletions
|
@ -13,9 +13,9 @@
|
|||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
// Our static vector will contain 32 elements, stt. an uint16_t will fill up 64 bytes
|
||||
// Our static vector will contain 32 elements, stt. an uint8_t will fill up 64 bytes
|
||||
// (an entire cache line). Thanks.
|
||||
enum class HostLoc : uint16_t {
|
||||
enum class HostLoc : std::uint8_t {
|
||||
// Ordering of the registers is intentional. See also: HostLocToX64.
|
||||
RAX,
|
||||
RCX,
|
||||
|
@ -60,48 +60,48 @@ enum class HostLoc : uint16_t {
|
|||
|
||||
constexpr size_t NonSpillHostLocCount = static_cast<size_t>(HostLoc::FirstSpill);
|
||||
|
||||
inline bool HostLocIsGPR(HostLoc reg) {
|
||||
constexpr bool HostLocIsGPR(HostLoc reg) {
|
||||
return reg >= HostLoc::RAX && reg <= HostLoc::R15;
|
||||
}
|
||||
|
||||
inline bool HostLocIsXMM(HostLoc reg) {
|
||||
constexpr bool HostLocIsXMM(HostLoc reg) {
|
||||
return reg >= HostLoc::XMM0 && reg <= HostLoc::XMM15;
|
||||
}
|
||||
|
||||
inline bool HostLocIsRegister(HostLoc reg) {
|
||||
constexpr bool HostLocIsRegister(HostLoc reg) {
|
||||
return HostLocIsGPR(reg) || HostLocIsXMM(reg);
|
||||
}
|
||||
|
||||
inline bool HostLocIsFlag(HostLoc reg) {
|
||||
constexpr bool HostLocIsFlag(HostLoc reg) {
|
||||
return reg >= HostLoc::CF && reg <= HostLoc::OF;
|
||||
}
|
||||
|
||||
inline HostLoc HostLocRegIdx(int idx) {
|
||||
constexpr HostLoc HostLocRegIdx(int idx) {
|
||||
ASSERT(idx >= 0 && idx <= 15);
|
||||
return HostLoc(idx);
|
||||
}
|
||||
|
||||
inline HostLoc HostLocXmmIdx(int idx) {
|
||||
constexpr HostLoc HostLocXmmIdx(int idx) {
|
||||
ASSERT(idx >= 0 && idx <= 15);
|
||||
return HostLoc(size_t(HostLoc::XMM0) + idx);
|
||||
}
|
||||
|
||||
inline HostLoc HostLocSpill(size_t i) {
|
||||
constexpr HostLoc HostLocSpill(size_t i) {
|
||||
return HostLoc(size_t(HostLoc::FirstSpill) + i);
|
||||
}
|
||||
|
||||
inline bool HostLocIsSpill(HostLoc reg) {
|
||||
constexpr bool HostLocIsSpill(HostLoc reg) {
|
||||
return reg >= HostLoc::FirstSpill;
|
||||
}
|
||||
|
||||
inline size_t HostLocBitWidth(HostLoc loc) {
|
||||
constexpr size_t HostLocBitWidth(HostLoc loc) {
|
||||
if (HostLocIsGPR(loc))
|
||||
return 64;
|
||||
if (HostLocIsXMM(loc))
|
||||
else if (HostLocIsXMM(loc))
|
||||
return 128;
|
||||
if (HostLocIsSpill(loc))
|
||||
else if (HostLocIsSpill(loc))
|
||||
return 128;
|
||||
if (HostLocIsFlag(loc))
|
||||
else if (HostLocIsFlag(loc))
|
||||
return 1;
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
|
@ -357,9 +357,8 @@ void RegAlloc::HostCall(IR::Inst* result_def,
|
|||
static const boost::container::static_vector<HostLoc, 28> other_caller_save = [args_hostloc]() noexcept {
|
||||
boost::container::static_vector<HostLoc, 28> ret(ABI_ALL_CALLER_SAVE.begin(), ABI_ALL_CALLER_SAVE.end());
|
||||
ret.erase(std::find(ret.begin(), ret.end(), ABI_RETURN));
|
||||
for (auto const hostloc : args_hostloc) {
|
||||
for (auto const hostloc : args_hostloc)
|
||||
ret.erase(std::find(ret.begin(), ret.end(), hostloc));
|
||||
}
|
||||
return ret;
|
||||
}();
|
||||
|
||||
|
@ -368,7 +367,7 @@ void RegAlloc::HostCall(IR::Inst* result_def,
|
|||
DefineValueImpl(result_def, ABI_RETURN);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < args_count; i++) {
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
if (args[i] && !args[i]->get().IsVoid()) {
|
||||
UseScratch(*args[i], args_hostloc[i]);
|
||||
// LLVM puts the burden of zero-extension of 8 and 16 bit values on the caller instead of the callee
|
||||
|
@ -383,36 +382,35 @@ void RegAlloc::HostCall(IR::Inst* result_def,
|
|||
case IR::Type::U32:
|
||||
code->mov(reg.cvt32(), reg.cvt32());
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
break; //no op
|
||||
default:
|
||||
break; // Nothing needs to be done
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < args_count; i++) {
|
||||
for (size_t i = 0; i < args.size(); i++)
|
||||
if (!args[i]) {
|
||||
// TODO: Force spill
|
||||
ScratchGpr(args_hostloc[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (HostLoc caller_saved : other_caller_save) {
|
||||
for (auto const caller_saved : other_caller_save)
|
||||
ScratchImpl({caller_saved});
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::AllocStackSpace(const size_t stack_space) noexcept {
|
||||
ASSERT(stack_space < static_cast<size_t>(std::numeric_limits<s32>::max()));
|
||||
ASSERT(stack_space < size_t(std::numeric_limits<s32>::max()));
|
||||
ASSERT(reserved_stack_space == 0);
|
||||
reserved_stack_space = stack_space;
|
||||
code->sub(code->rsp, static_cast<u32>(stack_space));
|
||||
code->sub(code->rsp, u32(stack_space));
|
||||
}
|
||||
|
||||
void RegAlloc::ReleaseStackSpace(const size_t stack_space) noexcept {
|
||||
ASSERT(stack_space < static_cast<size_t>(std::numeric_limits<s32>::max()));
|
||||
ASSERT(stack_space < size_t(std::numeric_limits<s32>::max()));
|
||||
ASSERT(reserved_stack_space == stack_space);
|
||||
reserved_stack_space = 0;
|
||||
code->add(code->rsp, static_cast<u32>(stack_space));
|
||||
code->add(code->rsp, u32(stack_space));
|
||||
}
|
||||
|
||||
HostLoc RegAlloc::SelectARegister(const boost::container::static_vector<HostLoc, 28>& desired_locations) const noexcept {
|
||||
|
@ -494,7 +492,6 @@ void RegAlloc::DefineValueImpl(IR::Inst* def_inst, const IR::Value& use_inst) no
|
|||
|
||||
HostLoc RegAlloc::LoadImmediate(IR::Value imm, HostLoc host_loc) noexcept {
|
||||
ASSERT_MSG(imm.IsImmediate(), "imm is not an immediate");
|
||||
|
||||
if (HostLocIsGPR(host_loc)) {
|
||||
const Xbyak::Reg64 reg = HostLocToReg64(host_loc);
|
||||
const u64 imm_value = imm.GetImmediateAsU64();
|
||||
|
@ -503,10 +500,7 @@ HostLoc RegAlloc::LoadImmediate(IR::Value imm, HostLoc host_loc) noexcept {
|
|||
} else {
|
||||
code->mov(reg, imm_value);
|
||||
}
|
||||
return host_loc;
|
||||
}
|
||||
|
||||
if (HostLocIsXMM(host_loc)) {
|
||||
} else if (HostLocIsXMM(host_loc)) {
|
||||
const Xbyak::Xmm reg = HostLocToXmm(host_loc);
|
||||
const u64 imm_value = imm.GetImmediateAsU64();
|
||||
if (imm_value == 0) {
|
||||
|
@ -514,23 +508,19 @@ HostLoc RegAlloc::LoadImmediate(IR::Value imm, HostLoc host_loc) noexcept {
|
|||
} else {
|
||||
MAYBE_AVX(movaps, reg, code->Const(code->xword, imm_value));
|
||||
}
|
||||
return host_loc;
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return host_loc;
|
||||
}
|
||||
|
||||
void RegAlloc::Move(HostLoc to, HostLoc from) noexcept {
|
||||
const size_t bit_width = LocInfo(from).GetMaxBitWidth();
|
||||
|
||||
ASSERT(LocInfo(to).IsEmpty() && !LocInfo(from).IsLocked());
|
||||
ASSERT(bit_width <= HostLocBitWidth(to));
|
||||
ASSERT_MSG(!LocInfo(from).IsEmpty(), "Mov eliminated");
|
||||
|
||||
if (!LocInfo(from).IsEmpty()) {
|
||||
EmitMove(bit_width, to, from);
|
||||
LocInfo(to) = std::exchange(LocInfo(from), {});
|
||||
}
|
||||
EmitMove(bit_width, to, from);
|
||||
LocInfo(to) = std::exchange(LocInfo(from), {});
|
||||
}
|
||||
|
||||
void RegAlloc::CopyToScratch(size_t bit_width, HostLoc to, HostLoc from) noexcept {
|
||||
|
|
|
@ -264,7 +264,7 @@ private:
|
|||
BlockOfCode* code = nullptr;
|
||||
size_t reserved_stack_space = 0;
|
||||
};
|
||||
// Ensure a cache line is used, this is primordial
|
||||
static_assert(sizeof(boost::container::static_vector<HostLoc, 28>) == 64);
|
||||
// Ensure a cache line (or less) is used, this is primordial
|
||||
static_assert(sizeof(boost::container::static_vector<HostLoc, 28>) == 40);
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
enum class HostLoc : uint16_t;
|
||||
enum class HostLoc : std::uint8_t;
|
||||
using Vector = std::array<u64, 2>;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -91,6 +91,9 @@ static u32 GenRandomInst(u64 pc, bool is_last_inst) {
|
|||
"MSR_reg",
|
||||
"MSR_imm",
|
||||
"MRS",
|
||||
// Does not need test
|
||||
"SVC",
|
||||
"BRK"
|
||||
};
|
||||
|
||||
for (const auto& [fn, bitstring] : list) {
|
||||
|
@ -200,7 +203,7 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
|
|||
jit_env.ticks_left = instructions.size();
|
||||
CheckedRun([&]() { jit.Run(); });
|
||||
|
||||
uni_env.ticks_left = instructions.size();
|
||||
uni_env.ticks_left = instructions.size() * 4;
|
||||
uni.Run();
|
||||
|
||||
SCOPE_FAIL {
|
||||
|
@ -296,7 +299,7 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
|
|||
return;
|
||||
}
|
||||
|
||||
REQUIRE(uni.GetPC() == jit.GetPC());
|
||||
REQUIRE(uni.GetPC() + 4 == jit.GetPC());
|
||||
REQUIRE(uni.GetRegisters() == jit.GetRegisters());
|
||||
REQUIRE(uni.GetVectors() == jit.GetVectors());
|
||||
REQUIRE(uni.GetSP() == jit.GetSP());
|
||||
|
@ -306,7 +309,7 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
|
|||
REQUIRE(FP::FPSR{uni.GetFpsr()}.QC() == FP::FPSR{jit.GetFpsr()}.QC());
|
||||
}
|
||||
|
||||
TEST_CASE("A64: Single random instruction", "[a64]") {
|
||||
TEST_CASE("A64: Single random instruction", "[a64][unicorn]") {
|
||||
A64TestEnv jit_env{};
|
||||
A64TestEnv uni_env{};
|
||||
|
||||
|
@ -333,7 +336,7 @@ TEST_CASE("A64: Single random instruction", "[a64]") {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("A64: Floating point instructions", "[a64]") {
|
||||
TEST_CASE("A64: Floating point instructions", "[a64][unicorn]") {
|
||||
A64TestEnv jit_env{};
|
||||
A64TestEnv uni_env{};
|
||||
|
||||
|
@ -458,7 +461,7 @@ TEST_CASE("A64: Floating point instructions", "[a64]") {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("A64: Small random block", "[a64]") {
|
||||
TEST_CASE("A64: Small random block", "[a64][unicorn]") {
|
||||
A64TestEnv jit_env{};
|
||||
A64TestEnv uni_env{};
|
||||
|
||||
|
@ -493,7 +496,7 @@ TEST_CASE("A64: Small random block", "[a64]") {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("A64: Large random block", "[a64]") {
|
||||
TEST_CASE("A64: Large random block", "[a64][unicorn]") {
|
||||
A64TestEnv jit_env{};
|
||||
A64TestEnv uni_env{};
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
using namespace Dynarmic;
|
||||
|
||||
TEST_CASE("Unicorn: Sanity test", "[a64]") {
|
||||
TEST_CASE("Unicorn: Sanity test", "[a64][unicorn]") {
|
||||
A64TestEnv env;
|
||||
|
||||
env.code_mem.emplace_back(0x8b020020); // ADD X0, X1, X2
|
||||
|
@ -39,7 +39,7 @@ TEST_CASE("Unicorn: Sanity test", "[a64]") {
|
|||
REQUIRE(unicorn.GetPC() == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("Unicorn: Ensure 0xFFFF'FFFF'FFFF'FFFF is readable", "[a64]") {
|
||||
TEST_CASE("Unicorn: Ensure 0xFFFF'FFFF'FFFF'FFFF is readable", "[a64][unicorn]") {
|
||||
A64TestEnv env;
|
||||
|
||||
env.code_mem.emplace_back(0x385fed99); // LDRB W25, [X12, #0xfffffffffffffffe]!
|
||||
|
@ -59,7 +59,7 @@ TEST_CASE("Unicorn: Ensure 0xFFFF'FFFF'FFFF'FFFF is readable", "[a64]") {
|
|||
REQUIRE(unicorn.GetPC() == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("Unicorn: Ensure is able to read across page boundaries", "[a64]") {
|
||||
TEST_CASE("Unicorn: Ensure is able to read across page boundaries", "[a64][unicorn]") {
|
||||
A64TestEnv env;
|
||||
|
||||
env.code_mem.emplace_back(0xb85f93d9); // LDUR W25, [X30, #0xfffffffffffffff9]
|
||||
|
|
|
@ -28,11 +28,10 @@ public:
|
|||
for (u64 page = page_start; page < page_end; ++page) {
|
||||
int& value = page_table[page];
|
||||
value += delta;
|
||||
if (value < 0) {
|
||||
throw std::logic_error{"negative page"};
|
||||
}
|
||||
if (value == 0) {
|
||||
page_table.erase(page);
|
||||
} else if (value < 0) {
|
||||
throw std::logic_error{"negative page"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue