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:
@@ -364,12 +364,11 @@ int InstrEmit_mfvscr(PPCHIRBuilder& f, const InstrData& i) {
|
||||
|
||||
int InstrEmit_mtvscr(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// is this the right format?
|
||||
//todo: what mtvscr does with the unused bits is implementation defined, figure out what it does
|
||||
|
||||
// todo: what mtvscr does with the unused bits is implementation defined,
|
||||
// figure out what it does
|
||||
|
||||
Value* v = f.LoadVR(i.VX128_1.RB);
|
||||
|
||||
|
||||
Value* has_njm_value = f.Extract(v, (uint8_t)3, INT32_TYPE);
|
||||
|
||||
f.SetNJM(f.IsTrue(f.And(has_njm_value, f.LoadConstantInt32(65536))));
|
||||
@@ -1824,9 +1823,38 @@ int InstrEmit_vsum4ubs(PPCHIRBuilder& f, const InstrData& i) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static Value* vkpkx_in_low(PPCHIRBuilder& f, Value* input) {
|
||||
// truncate from argb8888 to 1 bit alpha, 5 bit red, 5 bit green, 5 bit blue
|
||||
auto ShrU32Vec = [&f](Value* input, unsigned shift) {
|
||||
return f.VectorShr(input, f.LoadConstantVec128(vec128i(shift)), INT32_TYPE);
|
||||
};
|
||||
auto AndU32Vec = [&f](Value* input, unsigned msk) {
|
||||
return f.And(input, f.LoadConstantVec128(vec128i(msk)));
|
||||
};
|
||||
auto tmp1 = AndU32Vec(ShrU32Vec(input, 9), 0xFC00);
|
||||
auto tmp2 = AndU32Vec(ShrU32Vec(input, 6), 0x3E0);
|
||||
auto tmp3 = AndU32Vec(ShrU32Vec(input, 3), 0x1F);
|
||||
return f.Or(tmp3, f.Or(tmp1, tmp2));
|
||||
}
|
||||
|
||||
int InstrEmit_vpkpx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
// I compared the results of this against over a million randomly generated
|
||||
// sets of inputs and all compared equal
|
||||
|
||||
Value* src1 = f.LoadVR(i.VX.VA);
|
||||
|
||||
Value* src2 = f.LoadVR(i.VX.VB);
|
||||
|
||||
Value* pck1 = vkpkx_in_low(f, src1);
|
||||
Value* pck2 = vkpkx_in_low(f, src2);
|
||||
|
||||
Value* result = f.Pack(
|
||||
pck1, pck2,
|
||||
PACK_TYPE_16_IN_32 | PACK_TYPE_IN_UNSIGNED | PACK_TYPE_OUT_UNSIGNED);
|
||||
|
||||
f.StoreVR(i.VX.VD, result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int InstrEmit_vpkshss_(PPCHIRBuilder& f, uint32_t vd, uint32_t va,
|
||||
|
||||
@@ -336,10 +336,14 @@ int InstrEmit_mulhwx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
Value* ratrunc =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), INT64_TYPE);
|
||||
|
||||
Value* rbtrunc =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), INT64_TYPE);
|
||||
|
||||
Value* v = f.Sha(f.Mul(ratrunc, rbtrunc), 32);
|
||||
|
||||
Value* v = f.SignExtend(f.MulHi(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE),
|
||||
f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE)),
|
||||
INT64_TYPE);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
@@ -355,10 +359,13 @@ int InstrEmit_mulhwux(PPCHIRBuilder& f, const InstrData& i) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Value* v = f.ZeroExtend(
|
||||
f.MulHi(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE),
|
||||
f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), ARITHMETIC_UNSIGNED),
|
||||
INT64_TYPE);
|
||||
Value* ratrunc =
|
||||
f.ZeroExtend(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), INT64_TYPE);
|
||||
|
||||
Value* rbtrunc =
|
||||
f.ZeroExtend(f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), INT64_TYPE);
|
||||
|
||||
Value* v = f.Shr(f.Mul(ratrunc, rbtrunc, ARITHMETIC_UNSIGNED), 32);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
|
||||
@@ -89,8 +89,10 @@ int InstrEmit_fmulsx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
int InstrEmit_fresx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// frD <- 1.0 / (frB)
|
||||
|
||||
Value* v = f.Recip(f.LoadFPR(i.A.FRB));
|
||||
v = f.ToSingle(v);
|
||||
// this actually does seem to require single precision, oddly
|
||||
// more research is needed
|
||||
Value* v = f.Recip(f.Convert(f.LoadFPR(i.A.FRB), FLOAT32_TYPE));
|
||||
v = f.Convert(v, FLOAT64_TYPE); // f.ToSingle(v);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
f.UpdateFPSCR(v, i.A.Rc);
|
||||
return 0;
|
||||
|
||||
@@ -11,9 +11,17 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/cpu/ppc/ppc_context.h"
|
||||
#include "xenia/cpu/ppc/ppc_hir_builder.h"
|
||||
|
||||
DEFINE_bool(
|
||||
disable_prefetch_and_cachecontrol, false,
|
||||
"Disables translating ppc prefetch/cache flush instructions to host "
|
||||
"prefetch/cacheflush instructions. This may improve performance as these "
|
||||
"instructions were written with the Xbox 360's cache in mind, and modern "
|
||||
"processors do their own automatic prefetching.",
|
||||
"CPU");
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
@@ -1080,28 +1088,36 @@ int InstrEmit_stfsx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// https://randomascii.wordpress.com/2018/01/07/finding-a-cpu-design-bug-in-the-xbox-360/
|
||||
|
||||
int InstrEmit_dcbf(PPCHIRBuilder& f, const InstrData& i) {
|
||||
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
|
||||
f.CacheControl(ea, 128,
|
||||
CacheControlType::CACHE_CONTROL_TYPE_DATA_STORE_AND_FLUSH);
|
||||
if (!cvars::disable_prefetch_and_cachecontrol) {
|
||||
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
|
||||
f.CacheControl(ea, 128,
|
||||
CacheControlType::CACHE_CONTROL_TYPE_DATA_STORE_AND_FLUSH);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int InstrEmit_dcbst(PPCHIRBuilder& f, const InstrData& i) {
|
||||
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
|
||||
f.CacheControl(ea, 128, CacheControlType::CACHE_CONTROL_TYPE_DATA_STORE);
|
||||
if (!cvars::disable_prefetch_and_cachecontrol) {
|
||||
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
|
||||
f.CacheControl(ea, 128, CacheControlType::CACHE_CONTROL_TYPE_DATA_STORE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int InstrEmit_dcbt(PPCHIRBuilder& f, const InstrData& i) {
|
||||
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
|
||||
f.CacheControl(ea, 128, CacheControlType::CACHE_CONTROL_TYPE_DATA_TOUCH);
|
||||
if (!cvars::disable_prefetch_and_cachecontrol) {
|
||||
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
|
||||
f.CacheControl(ea, 128, CacheControlType::CACHE_CONTROL_TYPE_DATA_TOUCH);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int InstrEmit_dcbtst(PPCHIRBuilder& f, const InstrData& i) {
|
||||
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
|
||||
f.CacheControl(ea, 128,
|
||||
CacheControlType::CACHE_CONTROL_TYPE_DATA_TOUCH_FOR_STORE);
|
||||
if (!cvars::disable_prefetch_and_cachecontrol) {
|
||||
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
|
||||
f.CacheControl(ea, 128,
|
||||
CacheControlType::CACHE_CONTROL_TYPE_DATA_TOUCH_FOR_STORE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,9 @@ class PPCFrontend {
|
||||
PPCBuiltins builtins_ = {0};
|
||||
TypePool<PPCTranslator, PPCFrontend*> translator_pool_;
|
||||
};
|
||||
|
||||
// Checks the state of the global lock and sets scratch to the current MSR
|
||||
// value.
|
||||
void CheckGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1);
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
Reference in New Issue
Block a user