A bunch of fixes for division logic:

"turns out theres a lot of quirks with the div instructions we havent been covering
if the denom is 0, we jump to the end and mov eax/rax to dst, which is correct because ppc raises no exceptions for divide by 0 unlike x86
except we don't initialize eax before that jump, so whatever garbage from the previous sequence that has been left in eax/rax is what the result of the instruction will be
and then in our constant folding, we don't do the same zero check in Value::Div, so if we constant folded the denom to 0 we will host crash
the ppc manual says the result for a division by 0 is undefined, but in reality it seems it is always 0
there are a few posts i saw from googling about it, and tests on my rgh gave me 0, but then another issue came up
and that is that we dont check for signed overflow in our division, so we raise an exception if guest code ever does (1<<signbit_pos) / -1
signed overflow in division also produces 0 on ppc
the last thing is that if src2 is constant we skip the 0 check for division
without checking if its nonzero
all weird, likely very rare edge cases, except for maybe the signed overflow division
chrispy — Today at 9:51 AM
oh yeah, and because the int members of constantvalue are all signed ints, we were actually doing signed division always with constant folding"

fixed an earlier mistake by me with the precision of fresx
made some optimization disableable

implemented vkpkx
fixed possible bugs with vsr/vsl constant folding
disabled the nice imul code for now, there was a bug with int64 version and i dont have time to check
started on multiplication/addition/subtraction/division identities
Removed optimized VSL implementation, it's going to have to be rewritten anyway
Added ppc_ctx_t to xboxkrnl shim for direct context access
started working on KeSaveFloatingPointState, re'ed most of it
Exposed some more state/functionality to the kernel for implementing lower level routines like the save/restore ones
Add cvar to re-enable incorrect mxcsr behavior if a user doesnt care and wants better cpu performance
Stubbed out more impossible sequences, replace mul_hi_i32 with a 64 bit multiply
This commit is contained in:
chss95cs@gmail.com
2022-08-07 10:41:26 -07:00
parent f45e9e5e9a
commit 324a8eb818
19 changed files with 512 additions and 477 deletions

View File

@@ -67,6 +67,7 @@ class Backend {
// up until the start of ctx may be used by the backend to store whatever data
// they want
virtual void InitializeBackendContext(void* ctx) {}
virtual void SetGuestRoundingMode(void* ctx, unsigned int mode){};
protected:
Processor* processor_ = nullptr;

View File

@@ -689,8 +689,7 @@ void X64ThunkEmitter::EmitLoadNonvolatileRegs() {
#endif
}
void X64Backend::InitializeBackendContext(void* ctx) {
X64BackendContext* bctx = reinterpret_cast<X64BackendContext*>(
reinterpret_cast<intptr_t>(ctx) - sizeof(X64BackendContext));
X64BackendContext* bctx = BackendContextForGuestContext(ctx);
bctx->ResolveFunction_Ptr = reinterpret_cast<void*>(&ResolveFunction);
bctx->mxcsr_fpu =
DEFAULT_FPU_MXCSR; // idk if this is right, check on rgh what the
@@ -700,6 +699,18 @@ void X64Backend::InitializeBackendContext(void* ctx) {
// https://media.discordapp.net/attachments/440280035056943104/1000765256643125308/unknown.png
bctx->Ox1000 = 0x1000;
}
const uint32_t mxcsr_table[8] = {
0x1F80, 0x7F80, 0x5F80, 0x3F80, 0x9F80, 0xFF80, 0xDF80, 0xBF80,
};
void X64Backend::SetGuestRoundingMode(void* ctx, unsigned int mode) {
X64BackendContext* bctx = BackendContextForGuestContext(ctx);
uint32_t control = mode & 7;
_mm_setcsr(mxcsr_table[control]);
bctx->mxcsr_fpu = mxcsr_table[control];
((ppc::PPCContext*)ctx)->fpscr.bits.rn = control;
}
} // namespace x64
} // namespace backend
} // namespace cpu

View File

@@ -37,9 +37,10 @@ typedef void (*ResolveFunctionThunk)();
// negatively index the membase reg)
struct X64BackendContext {
void* ResolveFunction_Ptr; // cached pointer to resolvefunction
unsigned int mxcsr_fpu; //currently, the way we implement rounding mode affects both vmx and the fpu
unsigned int mxcsr_fpu; // currently, the way we implement rounding mode
// affects both vmx and the fpu
unsigned int mxcsr_vmx;
unsigned int flags; //bit 0 = 0 if mxcsr is fpu, else it is vmx
unsigned int flags; // bit 0 = 0 if mxcsr is fpu, else it is vmx
unsigned int Ox1000; // constant 0x1000 so we can shrink each tail emitted
// add of it by... 2 bytes lol
};
@@ -48,7 +49,7 @@ constexpr unsigned int DEFAULT_VMX_MXCSR =
0x0040 | (_MM_MASK_MASK); // default rounding mode for vmx
constexpr unsigned int DEFAULT_FPU_MXCSR = 0x1F80;
extern const uint32_t mxcsr_table[8];
class X64Backend : public Backend {
public:
static const uint32_t kForceReturnAddress = 0x9FFF0000u;
@@ -85,6 +86,12 @@ class X64Backend : public Backend {
void UninstallBreakpoint(Breakpoint* breakpoint) override;
virtual void InitializeBackendContext(void* ctx) override;
X64BackendContext* BackendContextForGuestContext(void* ctx) {
return reinterpret_cast<X64BackendContext*>(
reinterpret_cast<intptr_t>(ctx) - sizeof(X64BackendContext));
}
virtual void SetGuestRoundingMode(void* ctx, unsigned int mode) override;
private:
static bool ExceptionCallbackThunk(Exception* ex, void* data);
bool ExceptionCallback(Exception* ex);

View File

@@ -50,6 +50,13 @@ DEFINE_bool(resolve_rel32_guest_calls, true,
"Experimental optimization, directly call already resolved "
"functions via x86 rel32 call/jmp",
"CPU");
DEFINE_bool(enable_incorrect_roundingmode_behavior, false,
"Disables the FPU/VMX MXCSR sharing workaround, potentially "
"causing incorrect rounding behavior and denormal handling in VMX "
"code. The workaround may cause reduced CPU performance but is a "
"more accurate emulation",
"x64");
namespace xe {
namespace cpu {
namespace backend {
@@ -1374,13 +1381,13 @@ Xbyak::Label& X64Emitter::NewCachedLabel() {
return *tmp;
}
template<bool switching_to_fpu>
template <bool switching_to_fpu>
static void ChangeMxcsrModeDynamicHelper(X64Emitter& e) {
auto flags = e.GetBackendFlagsPtr();
if (switching_to_fpu) {
e.btr(flags, 0); // bit 0 set to 0 = is fpu mode
} else {
e.bts(flags, 0); // bit 0 set to 1 = is vmx mode
e.bts(flags, 0); // bit 0 set to 1 = is vmx mode
}
Xbyak::Label& come_back = e.NewCachedLabel();
@@ -1391,20 +1398,24 @@ static void ChangeMxcsrModeDynamicHelper(X64Emitter& e) {
e.LoadFpuMxcsrDirect();
} else {
e.LoadVmxMxcsrDirect();
}
}
e.jmp(come_back, X64Emitter::T_NEAR);
});
if (switching_to_fpu) {
e.jc(reload_bailout,
X64Emitter::T_NEAR); // if carry flag was set, we were VMX mxcsr mode.
} else {
e.jnc(reload_bailout,
X64Emitter::T_NEAR); // if carry flag was set, we were VMX mxcsr mode.
e.jnc(
reload_bailout,
X64Emitter::T_NEAR); // if carry flag was set, we were VMX mxcsr mode.
}
e.L(come_back);
}
bool X64Emitter::ChangeMxcsrMode(MXCSRMode new_mode, bool already_set) {
if (cvars::enable_incorrect_roundingmode_behavior) {
return false; // no MXCSR mode handling!
}
if (new_mode == mxcsr_mode_) {
return false;
}
@@ -1420,21 +1431,21 @@ bool X64Emitter::ChangeMxcsrMode(MXCSRMode new_mode, bool already_set) {
ChangeMxcsrModeDynamicHelper<false>(*this);
} else {
assert_unhandled_case(new_mode);
}
} else { //even if already set, we still need to update flags to reflect our mode
}
} else { // even if already set, we still need to update flags to reflect
// our mode
if (new_mode == MXCSRMode::Fpu) {
btr(GetBackendFlagsPtr(), 0);
} else if (new_mode == MXCSRMode::Vmx) {
bts(GetBackendFlagsPtr(), 0);
} else {
assert_unhandled_case(new_mode);
}
}
}
}
} else {
mxcsr_mode_ = new_mode;
if (!already_set) {
if (new_mode == MXCSRMode::Fpu) {
LoadFpuMxcsrDirect();
btr(GetBackendFlagsPtr(), 0);
return true;

View File

@@ -23,6 +23,10 @@ DEFINE_bool(
elide_e0_check, false,
"Eliminate e0 check on some memory accesses, like to r13(tls) or r1(sp)",
"CPU");
DEFINE_bool(enable_rmw_context_merging, false,
"Permit merging read-modify-write HIR instr sequences together "
"into x86 instructions that use a memory operand.",
"x64");
namespace xe {
namespace cpu {
@@ -88,6 +92,9 @@ struct LoadModStoreContext : public LoadModStore {
};
static bool GetLoadModStoreContext(const hir::Instr* loadinsn,
LoadModStoreContext* out) {
if (!cvars::enable_rmw_context_merging) {
return false;
}
if (!GetLoadModStore(loadinsn, out)) {
return false;
}

File diff suppressed because one or more lines are too long

View File

@@ -10,11 +10,13 @@
#ifndef XENIA_CPU_BACKEND_X64_X64_SEQUENCES_H_
#define XENIA_CPU_BACKEND_X64_X64_SEQUENCES_H_
#include "xenia/base/logging.h"
#include "xenia/cpu/hir/instr.h"
#include <unordered_map>
#define assert_impossible_sequence(name) \
assert_always("impossible sequence hit" #name);
#define assert_impossible_sequence(name) \
assert_always("impossible sequence hit" #name); \
XELOGE("impossible sequence hit: {}", #name)
namespace xe {
namespace cpu {