callback: Properly handle calls with return pointers and simplify interface

This commit is contained in:
MerryMage 2018-02-09 15:58:16 +00:00
parent 15871910af
commit ad428cbd7a
5 changed files with 86 additions and 128 deletions

View file

@ -590,9 +590,9 @@ void A32EmitX64::EmitA32ExceptionRaised(A32EmitContext& ctx, IR::Inst* inst) {
ASSERT(args[0].IsImmediate() && args[1].IsImmediate()); ASSERT(args[0].IsImmediate() && args[1].IsImmediate());
u32 pc = args[0].GetImmediateU32(); u32 pc = args[0].GetImmediateU32();
u64 exception = args[1].GetImmediateU64(); u64 exception = args[1].GetImmediateU64();
DEVIRT(config.callbacks, &A32::UserCallbacks::ExceptionRaised).EmitCall(code, [&](Xbyak::Reg64 param1, Xbyak::Reg64 param2) { DEVIRT(config.callbacks, &A32::UserCallbacks::ExceptionRaised).EmitCall(code, [&](RegList param) {
code.mov(param1, pc); code.mov(param[0], pc);
code.mov(param2, exception); code.mov(param[1], exception);
}); });
} }

View file

@ -293,8 +293,8 @@ void A64EmitX64::EmitA64CallSupervisor(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(args[0].IsImmediate()); ASSERT(args[0].IsImmediate());
u32 imm = args[0].GetImmediateU32(); u32 imm = args[0].GetImmediateU32();
DEVIRT(conf.callbacks, &A64::UserCallbacks::CallSVC).EmitCall(code, [&](Xbyak::Reg64 param1) { DEVIRT(conf.callbacks, &A64::UserCallbacks::CallSVC).EmitCall(code, [&](RegList param) {
code.mov(param1.cvt32(), imm); code.mov(param[0], imm);
}); });
} }
@ -304,42 +304,34 @@ void A64EmitX64::EmitA64ExceptionRaised(A64EmitContext& ctx, IR::Inst* inst) {
ASSERT(args[0].IsImmediate() && args[1].IsImmediate()); ASSERT(args[0].IsImmediate() && args[1].IsImmediate());
u64 pc = args[0].GetImmediateU64(); u64 pc = args[0].GetImmediateU64();
u64 exception = args[1].GetImmediateU64(); u64 exception = args[1].GetImmediateU64();
DEVIRT(conf.callbacks, &A64::UserCallbacks::ExceptionRaised).EmitCall(code, [&](Xbyak::Reg64 param1, Xbyak::Reg64 param2) { DEVIRT(conf.callbacks, &A64::UserCallbacks::ExceptionRaised).EmitCall(code, [&](RegList param) {
code.mov(param1, pc); code.mov(param[0], pc);
code.mov(param2, exception); code.mov(param[1], exception);
}); });
} }
void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead8).EmitCall(code, [&](Xbyak::Reg64 vaddr) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2); ctx.reg_alloc.HostCall(inst, {}, args[0]);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead8).EmitCall(code);
ctx.reg_alloc.HostCall(inst, {}, args[0]);
});
} }
void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead16).EmitCall(code, [&](Xbyak::Reg64 vaddr) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2); ctx.reg_alloc.HostCall(inst, {}, args[0]);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead16).EmitCall(code);
ctx.reg_alloc.HostCall(inst, {}, args[0]);
});
} }
void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead32).EmitCall(code, [&](Xbyak::Reg64 vaddr) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2); ctx.reg_alloc.HostCall(inst, {}, args[0]);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead32).EmitCall(code);
ctx.reg_alloc.HostCall(inst, {}, args[0]);
});
} }
void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead64).EmitCall(code, [&](Xbyak::Reg64 vaddr) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2); ctx.reg_alloc.HostCall(inst, {}, args[0]);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead64).EmitCall(code);
ctx.reg_alloc.HostCall(inst, {}, args[0]);
});
} }
void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
@ -348,11 +340,10 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
static_assert(ABI_SHADOW_SPACE >= 16); static_assert(ABI_SHADOW_SPACE >= 16);
ctx.reg_alloc.HostCall(nullptr, {}, {}, args[0]); ctx.reg_alloc.HostCall(nullptr, {}, {}, args[0]);
code.lea(code.ABI_PARAM2, ptr[rsp]);
code.sub(rsp, ABI_SHADOW_SPACE);
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead128).EmitCall(code, [&](Xbyak::Reg64 return_value, Xbyak::Reg64 vaddr) { DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead128).EmitCallWithReturnPointer(code, [&](Xbyak::Reg64 return_value_ptr, RegList) {
ASSERT(return_value == code.ABI_PARAM2 && vaddr == code.ABI_PARAM3); code.lea(return_value_ptr, ptr[rsp]);
code.sub(rsp, ABI_SHADOW_SPACE);
}); });
Xbyak::Xmm result = xmm0; Xbyak::Xmm result = xmm0;
@ -361,11 +352,10 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.DefineValue(inst, result); ctx.reg_alloc.DefineValue(inst, result);
#else #else
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead128).EmitCall(code, [&](Xbyak::Reg64 vaddr) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2); ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead128).EmitCall(code);
ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
});
Xbyak::Xmm result = xmm0; Xbyak::Xmm result = xmm0;
if (code.DoesCpuSupport(Xbyak::util::Cpu::tSSE41)) { if (code.DoesCpuSupport(Xbyak::util::Cpu::tSSE41)) {
code.movq(result, code.ABI_RETURN); code.movq(result, code.ABI_RETURN);
@ -381,35 +371,27 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
} }
void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) {
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite8).EmitCall(code, [&](Xbyak::Reg64 vaddr, Xbyak::Reg64 value) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2 && value == code.ABI_PARAM3); ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite8).EmitCall(code);
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
});
} }
void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) {
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite16).EmitCall(code, [&](Xbyak::Reg64 vaddr, Xbyak::Reg64 value) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2 && value == code.ABI_PARAM3); ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite16).EmitCall(code);
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
});
} }
void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) {
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite32).EmitCall(code, [&](Xbyak::Reg64 vaddr, Xbyak::Reg64 value) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2 && value == code.ABI_PARAM3); ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite32).EmitCall(code);
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
});
} }
void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) {
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite64).EmitCall(code, [&](Xbyak::Reg64 vaddr, Xbyak::Reg64 value) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2 && value == code.ABI_PARAM3); ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite64).EmitCall(code);
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
});
} }
void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
@ -425,40 +407,36 @@ void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
code.sub(rsp, ABI_SHADOW_SPACE); code.sub(rsp, ABI_SHADOW_SPACE);
code.movaps(xword[code.ABI_PARAM3], xmm_value); code.movaps(xword[code.ABI_PARAM3], xmm_value);
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite128).EmitCall(code, [&](Xbyak::Reg64 vaddr, Xbyak::Reg64 value_ptr) { DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite128).EmitCall(code);
ASSERT(vaddr == code.ABI_PARAM2 && value_ptr == code.ABI_PARAM3);
});
code.add(rsp, ABI_SHADOW_SPACE); code.add(rsp, ABI_SHADOW_SPACE);
#else #else
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite128).EmitCall(code, [&](Xbyak::Reg64 vaddr, Xbyak::Reg64 value0, Xbyak::Reg64 value1) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(vaddr == code.ABI_PARAM2 && value0 == code.ABI_PARAM3 && value1 == code.ABI_PARAM4); ctx.reg_alloc.Use(args[0], ABI_PARAM2);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); ctx.reg_alloc.ScratchGpr({ABI_PARAM3});
ctx.reg_alloc.Use(args[0], ABI_PARAM2); ctx.reg_alloc.ScratchGpr({ABI_PARAM4});
ctx.reg_alloc.ScratchGpr({ABI_PARAM3}); if (code.DoesCpuSupport(Xbyak::util::Cpu::tSSE41)) {
ctx.reg_alloc.ScratchGpr({ABI_PARAM4}); Xbyak::Xmm xmm_value = ctx.reg_alloc.UseXmm(args[1]);
if (code.DoesCpuSupport(Xbyak::util::Cpu::tSSE41)) { code.movq(code.ABI_PARAM3, xmm_value);
Xbyak::Xmm xmm_value = ctx.reg_alloc.UseXmm(args[1]); code.pextrq(code.ABI_PARAM4, xmm_value, 1);
code.movq(code.ABI_PARAM3, xmm_value); } else {
code.pextrq(code.ABI_PARAM4, xmm_value, 1); Xbyak::Xmm xmm_value = ctx.reg_alloc.UseScratchXmm(args[1]);
} else { code.movq(code.ABI_PARAM3, xmm_value);
Xbyak::Xmm xmm_value = ctx.reg_alloc.UseScratchXmm(args[1]); code.punpckhqdq(xmm_value, xmm_value);
code.movq(code.ABI_PARAM3, xmm_value); code.movq(code.ABI_PARAM4, xmm_value);
code.punpckhqdq(xmm_value, xmm_value); }
code.movq(code.ABI_PARAM4, xmm_value); ctx.reg_alloc.EndOfAllocScope();
} ctx.reg_alloc.HostCall(nullptr);
ctx.reg_alloc.EndOfAllocScope(); DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryWrite128).EmitCall(code);
ctx.reg_alloc.HostCall(nullptr);
});
#endif #endif
} }
void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor) { void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor) {
code.SwitchMxcsrOnExit(); code.SwitchMxcsrOnExit();
DEVIRT(conf.callbacks, &A64::UserCallbacks::InterpreterFallback).EmitCall(code, [&](Xbyak::Reg64 param1, Xbyak::Reg64 param2) { DEVIRT(conf.callbacks, &A64::UserCallbacks::InterpreterFallback).EmitCall(code, [&](RegList param) {
code.mov(param1, A64::LocationDescriptor{terminal.next}.PC()); code.mov(param[0], A64::LocationDescriptor{terminal.next}.PC());
code.mov(qword[r15 + offsetof(A64JitState, pc)], param1); code.mov(qword[r15 + offsetof(A64JitState, pc)], param[0]);
code.mov(param2.cvt32(), terminal.num_instructions); code.mov(param[1].cvt32(), terminal.num_instructions);
}); });
code.ReturnFromRunCode(true); // TODO: Check cycles code.ReturnFromRunCode(true); // TODO: Check cycles
} }

View file

@ -152,9 +152,9 @@ void BlockOfCode::GenRunCode() {
SwitchMxcsrOnExit(); SwitchMxcsrOnExit();
} }
cb.AddTicks->EmitCall(*this, [this](Xbyak::Reg64 param1) { cb.AddTicks->EmitCall(*this, [this](RegList param) {
mov(param1, qword[r15 + jsi.offsetof_cycles_to_run]); mov(param[0], qword[r15 + jsi.offsetof_cycles_to_run]);
sub(param1, qword[r15 + jsi.offsetof_cycles_remaining]); sub(param[0], qword[r15 + jsi.offsetof_cycles_remaining]);
}); });
ABI_PopCalleeSaveRegistersAndAdjustStack(*this); ABI_PopCalleeSaveRegistersAndAdjustStack(*this);

View file

@ -9,47 +9,30 @@
namespace Dynarmic::BackendX64 { namespace Dynarmic::BackendX64 {
void SimpleCallback::EmitCall(BlockOfCode& code, std::function<void()> l) { void SimpleCallback::EmitCall(BlockOfCode& code, std::function<void(RegList)> l) {
l(); l({code.ABI_PARAM1, code.ABI_PARAM2, code.ABI_PARAM3, code.ABI_PARAM4});
code.CallFunction(fn); code.CallFunction(fn);
} }
void SimpleCallback::EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64)> l) { void SimpleCallback::EmitCallWithReturnPointer(BlockOfCode& code, std::function<void(Xbyak::Reg64, RegList)> l) {
l(code.ABI_PARAM1); l(code.ABI_PARAM1, {code.ABI_PARAM2, code.ABI_PARAM3, code.ABI_PARAM4});
code.CallFunction(fn); code.CallFunction(fn);
} }
void SimpleCallback::EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64)> l) { void ArgCallback::EmitCall(BlockOfCode& code, std::function<void(RegList)> l) {
l(code.ABI_PARAM1, code.ABI_PARAM2); l({code.ABI_PARAM2, code.ABI_PARAM3, code.ABI_PARAM4});
code.CallFunction(fn);
}
void SimpleCallback::EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64, Xbyak::Reg64)> l) {
l(code.ABI_PARAM1, code.ABI_PARAM2, code.ABI_PARAM3);
code.CallFunction(fn);
}
void ArgCallback::EmitCall(BlockOfCode& code, std::function<void()> l) {
l();
code.mov(code.ABI_PARAM1, arg); code.mov(code.ABI_PARAM1, arg);
code.CallFunction(fn); code.CallFunction(fn);
} }
void ArgCallback::EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64)> l) { void ArgCallback::EmitCallWithReturnPointer(BlockOfCode& code, std::function<void(Xbyak::Reg64, RegList)> l) {
l(code.ABI_PARAM2); #if defined(WIN32) && !defined(__MINGW64__)
code.mov(code.ABI_PARAM1, arg); l(code.ABI_PARAM2, {code.ABI_PARAM3, code.ABI_PARAM4});
code.CallFunction(fn);
}
void ArgCallback::EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64)> l) {
l(code.ABI_PARAM2, code.ABI_PARAM3);
code.mov(code.ABI_PARAM1, arg);
code.CallFunction(fn);
}
void ArgCallback::EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64, Xbyak::Reg64)> l) {
l(code.ABI_PARAM2, code.ABI_PARAM3, code.ABI_PARAM4);
code.mov(code.ABI_PARAM1, arg); code.mov(code.ABI_PARAM1, arg);
#else
l(code.ABI_PARAM1, {code.ABI_PARAM3, code.ABI_PARAM4});
code.mov(code.ABI_PARAM2, arg);
#endif
code.CallFunction(fn); code.CallFunction(fn);
} }

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <vector>
#include <xbyak.h> #include <xbyak.h>
@ -14,16 +15,16 @@
namespace Dynarmic::BackendX64 { namespace Dynarmic::BackendX64 {
using RegList = std::vector<Xbyak::Reg64>;
class BlockOfCode; class BlockOfCode;
class Callback { class Callback {
public: public:
virtual ~Callback() = default; virtual ~Callback() = default;
virtual void EmitCall(BlockOfCode& code, std::function<void()> fn = []{}) = 0; virtual void EmitCall(BlockOfCode& code, std::function<void(RegList)> fn = [](RegList){}) = 0;
virtual void EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64)> fn) = 0; virtual void EmitCallWithReturnPointer(BlockOfCode& code, std::function<void(Xbyak::Reg64, RegList)> fn) = 0;
virtual void EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64)> fn) = 0;
virtual void EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64, Xbyak::Reg64)> fn) = 0;
}; };
class SimpleCallback final : public Callback { class SimpleCallback final : public Callback {
@ -33,10 +34,8 @@ public:
~SimpleCallback() override = default; ~SimpleCallback() override = default;
void EmitCall(BlockOfCode& code, std::function<void()> l = []{}) override; void EmitCall(BlockOfCode& code, std::function<void(RegList)> fn = [](RegList){}) override;
void EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64)> l) override; void EmitCallWithReturnPointer(BlockOfCode& code, std::function<void(Xbyak::Reg64, RegList)> fn) override;
void EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64)> l) override;
void EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64, Xbyak::Reg64)> l) override;
private: private:
void (*fn)(); void (*fn)();
@ -49,10 +48,8 @@ public:
~ArgCallback() override = default; ~ArgCallback() override = default;
void EmitCall(BlockOfCode& code, std::function<void()> l = []{}) override; void EmitCall(BlockOfCode& code, std::function<void(RegList)> fn = [](RegList){}) override;
void EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64)> l) override; void EmitCallWithReturnPointer(BlockOfCode& code, std::function<void(Xbyak::Reg64, RegList)> fn) override;
void EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64)> l) override;
void EmitCall(BlockOfCode& code, std::function<void(Xbyak::Reg64, Xbyak::Reg64, Xbyak::Reg64)> l) override;
private: private:
void (*fn)(); void (*fn)();