Attempt to emulate reserved load/store more closely. can't do anything for stores of the same value that are done via a non-reserved store to a reserved location

uses a bitmap that splits up the memory space into 65k blocks per bit.  Currently is using the guest virtual address but should be using physical addresses instead.

Currently if a guest does a reserve on a location and then a reserved store to a totally different location we trigger a breakpoint. This should never happen
Also removed the NEGATED_MUL_blah operations. They weren't necessary, nothing special is needed for the negated result variants.

Added a log message for when watched physical memory has a race, it just would be nice to know when it happens and in what games.
This commit is contained in:
chss95cs@gmail.com
2023-04-15 16:06:07 -04:00
parent 5e0c67438c
commit 7fb4b4cd41
12 changed files with 353 additions and 187 deletions

View File

@@ -1143,7 +1143,7 @@ int InstrEmit_vnmsubfp_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb,
Value* b = f.VectorDenormFlush(f.LoadVR(vb));
Value* c = f.VectorDenormFlush(f.LoadVR(vc));
Value* v = f.NegatedMulSub(a, c, b);
Value* v = f.Neg(f.MulSub(a, c, b));
f.StoreVR(vd, v);
return 0;
}

View File

@@ -195,8 +195,8 @@ int InstrEmit_fmsubsx(PPCHIRBuilder& f, const InstrData& i) {
int InstrEmit_fnmaddx(PPCHIRBuilder& f, const InstrData& i) {
// frD <- -([frA x frC] + frB)
Value* v = f.NegatedMulAdd(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC),
f.LoadFPR(i.A.FRB));
Value* v = f.Neg(
f.MulAdd(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB)));
f.StoreFPR(i.A.FRT, v);
f.UpdateFPSCR(v, i.A.Rc);
return 0;
@@ -204,8 +204,8 @@ int InstrEmit_fnmaddx(PPCHIRBuilder& f, const InstrData& i) {
int InstrEmit_fnmaddsx(PPCHIRBuilder& f, const InstrData& i) {
// frD <- -([frA x frC] + frB)
Value* v = f.NegatedMulAdd(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC),
f.LoadFPR(i.A.FRB));
Value* v = f.Neg(
f.MulAdd(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB)));
v = f.ToSingle(v);
f.StoreFPR(i.A.FRT, v);
f.UpdateFPSCR(v, i.A.Rc);
@@ -214,8 +214,8 @@ int InstrEmit_fnmaddsx(PPCHIRBuilder& f, const InstrData& i) {
int InstrEmit_fnmsubx(PPCHIRBuilder& f, const InstrData& i) {
// frD <- -([frA x frC] - frB)
Value* v = f.NegatedMulSub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC),
f.LoadFPR(i.A.FRB));
Value* v = f.Neg(
f.MulSub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB)));
f.StoreFPR(i.A.FRT, v);
f.UpdateFPSCR(v, i.A.Rc);
return 0;
@@ -223,8 +223,8 @@ int InstrEmit_fnmsubx(PPCHIRBuilder& f, const InstrData& i) {
int InstrEmit_fnmsubsx(PPCHIRBuilder& f, const InstrData& i) {
// frD <- -([frA x frC] - frB)
Value* v = f.NegatedMulSub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC),
f.LoadFPR(i.A.FRB));
Value* v = f.Neg(
f.MulSub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB)));
v = f.ToSingle(v);
f.StoreFPR(i.A.FRT, v);
f.UpdateFPSCR(v, i.A.Rc);
@@ -444,13 +444,12 @@ int InstrEmit_fabsx(PPCHIRBuilder& f, const InstrData& i) {
f.StoreFPR(i.X.RT, v);
/*
The contents of frB with bit 0 cleared are placed into frD.
Note that the fabs instruction treats NaNs just like any other kind of value. That is, the sign
bit of a NaN may be altered by fabs. This instruction does not alter the FPSCR.
Other registers altered:
• Condition Register (CR1 field):
Note that the fabs instruction treats NaNs just like any other kind of value.
That is, the sign bit of a NaN may be altered by fabs. This instruction does not
alter the FPSCR. Other registers altered: • Condition Register (CR1 field):
Affected: FX, FEX, VX, OX (if Rc = 1)
*/
// f.UpdateFPSCR(v, i.X.Rc);
// f.UpdateFPSCR(v, i.X.Rc);
if (i.X.Rc) {
// todo
}
@@ -469,9 +468,9 @@ int InstrEmit_fnabsx(PPCHIRBuilder& f, const InstrData& i) {
// frD <- !abs(frB)
Value* v = f.Neg(f.Abs(f.LoadFPR(i.X.RB)));
f.StoreFPR(i.X.RT, v);
//f.UpdateFPSCR(v, i.X.Rc);
// f.UpdateFPSCR(v, i.X.Rc);
if (i.X.Rc) {
//todo
// todo
}
return 0;
}
@@ -480,9 +479,9 @@ int InstrEmit_fnegx(PPCHIRBuilder& f, const InstrData& i) {
// frD <- ¬ frB[0] || frB[1-63]
Value* v = f.Neg(f.LoadFPR(i.X.RB));
f.StoreFPR(i.X.RT, v);
//f.UpdateFPSCR(v, i.X.Rc);
// f.UpdateFPSCR(v, i.X.Rc);
if (i.X.Rc) {
//todo
// todo
}
return 0;
}

View File

@@ -22,6 +22,12 @@ DEFINE_bool(
"instructions were written with the Xbox 360's cache in mind, and modern "
"processors do their own automatic prefetching.",
"CPU");
DEFINE_bool(no_reserved_ops, false,
"For testing whether a game may have races with a broken reserved "
"load/store impl",
"CPU");
namespace xe {
namespace cpu {
namespace ppc {
@@ -772,12 +778,17 @@ int InstrEmit_ldarx(PPCHIRBuilder& f, const InstrData& i) {
// already, but I haven't see anything but interrupt callbacks (which are
// always under a global lock) do that yet.
// We issue a memory barrier here to make sure that we get good values.
f.MemoryBarrier();
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
Value* rt = f.ByteSwap(f.Load(ea, INT64_TYPE));
f.StoreReserved(rt);
f.StoreGPR(i.X.RT, rt);
if (cvars::no_reserved_ops) {
f.StoreGPR(i.X.RT, f.ByteSwap(f.Load(ea, INT64_TYPE)));
} else {
f.MemoryBarrier();
Value* rt = f.ByteSwap(f.LoadWithReserve(ea, INT64_TYPE));
f.StoreGPR(i.X.RT, rt);
}
return 0;
}
@@ -797,12 +808,19 @@ int InstrEmit_lwarx(PPCHIRBuilder& f, const InstrData& i) {
// already, but I haven't see anything but interrupt callbacks (which are
// always under a global lock) do that yet.
// We issue a memory barrier here to make sure that we get good values.
f.MemoryBarrier();
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
Value* rt = f.ZeroExtend(f.ByteSwap(f.Load(ea, INT32_TYPE)), INT64_TYPE);
f.StoreReserved(rt);
f.StoreGPR(i.X.RT, rt);
if (cvars::no_reserved_ops) {
f.StoreGPR(i.X.RT,
f.ZeroExtend(f.ByteSwap(f.Load(ea, INT32_TYPE)), INT64_TYPE));
} else {
f.MemoryBarrier();
Value* rt =
f.ZeroExtend(f.ByteSwap(f.LoadWithReserve(ea, INT32_TYPE)), INT64_TYPE);
f.StoreGPR(i.X.RT, rt);
}
return 0;
}
@@ -826,17 +844,24 @@ int InstrEmit_stdcx(PPCHIRBuilder& f, const InstrData& i) {
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
Value* rt = f.ByteSwap(f.LoadGPR(i.X.RT));
Value* res = f.ByteSwap(f.LoadReserved());
Value* v = f.AtomicCompareExchange(ea, res, rt);
f.StoreContext(offsetof(PPCContext, cr0.cr0_eq), v);
if (cvars::no_reserved_ops) {
f.Store(ea, rt);
f.StoreContext(offsetof(PPCContext, cr0.cr0_eq), f.LoadConstantInt8(1));
} else {
Value* v = f.StoreWithReserve(ea, rt, INT64_TYPE);
f.StoreContext(offsetof(PPCContext, cr0.cr0_eq), v);
}
f.StoreContext(offsetof(PPCContext, cr0.cr0_lt), f.LoadZeroInt8());
f.StoreContext(offsetof(PPCContext, cr0.cr0_gt), f.LoadZeroInt8());
// Issue memory barrier for when we go out of lock and want others to see our
// updates.
f.MemoryBarrier();
if (!cvars::no_reserved_ops) {
f.MemoryBarrier();
}
return 0;
}
@@ -859,20 +884,29 @@ int InstrEmit_stwcx(PPCHIRBuilder& f, const InstrData& i) {
// This will always succeed if under the global lock, however.
Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB);
Value* rt = f.ByteSwap(f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE));
Value* res = f.ByteSwap(f.Truncate(f.LoadReserved(), INT32_TYPE));
Value* v = f.AtomicCompareExchange(ea, res, rt);
f.StoreContext(offsetof(PPCContext, cr0.cr0_eq), v);
if (cvars::no_reserved_ops) {
f.Store(ea, rt);
f.StoreContext(offsetof(PPCContext, cr0.cr0_eq), f.LoadConstantInt8(1));
} else {
Value* v = f.StoreWithReserve(ea, rt, INT64_TYPE);
f.StoreContext(offsetof(PPCContext, cr0.cr0_eq), v);
}
f.StoreContext(offsetof(PPCContext, cr0.cr0_lt), f.LoadZeroInt8());
f.StoreContext(offsetof(PPCContext, cr0.cr0_gt), f.LoadZeroInt8());
// Issue memory barrier for when we go out of lock and want others to see our
// updates.
f.MemoryBarrier();
if (!cvars::no_reserved_ops) {
f.MemoryBarrier();
}
return 0;
}
// Floating-point load (A-19)
int InstrEmit_lfd(PPCHIRBuilder& f, const InstrData& i) {