mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-16 07:50:28 +02:00
VFPU: Decode prefixes with shared helpers
The prefix fields were decoded by hand in the interpreter, the IR frontend, JitState, MIPSState and the disassembler, with three copies of the constant table. They now share small decoders and one table, in MIPS.h. The backends' Vec4Shuffle lane decoding gets a helper of its own in IRInst.h. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66b26d08c1
commit
124f86d7d0
12 files changed
+75
-71
No files matched your search
@@ -123,12 +123,12 @@ void ArmJitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
if (inst.dest == inst.src1) {
|
||||
// Go through the scratch regs, since a lane may be read after it's written.
|
||||
for (int i = 0; i < 4; ++i)
|
||||
VMOV((ARMReg)(SCRATCHF1 + i), regs_.F(inst.src1 + ((inst.src2 >> (i * 2)) & 3)));
|
||||
VMOV((ARMReg)(SCRATCHF1 + i), regs_.F(inst.src1 + Vec4ShuffleLane(inst.src2, i)));
|
||||
for (int i = 0; i < 4; ++i)
|
||||
VMOV(regs_.F(inst.dest + i), (ARMReg)(SCRATCHF1 + i));
|
||||
} else {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
VMOV(regs_.F(inst.dest + i), regs_.F(inst.src1 + ((inst.src2 >> (i * 2)) & 3)));
|
||||
VMOV(regs_.F(inst.dest + i), regs_.F(inst.src1 + Vec4ShuffleLane(inst.src2, i)));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -277,8 +277,8 @@ uint8_t Arm64ShuffleResult(uint8_t mask, uint8_t prev) {
|
||||
|
||||
uint8_t result = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int takeLane = (mask >> (i * 2)) & 3;
|
||||
int lane = (prev >> (takeLane * 2)) & 3;
|
||||
int takeLane = Vec4ShuffleLane(mask, i);
|
||||
int lane = Vec4ShuffleLane(prev, takeLane);
|
||||
result |= lane << (i * 2);
|
||||
}
|
||||
return result;
|
||||
|
||||
+21
-43
@@ -156,10 +156,10 @@ namespace MIPSComp {
|
||||
static bool IsPrefixWithinSize(u32 prefix, VectorSize sz) {
|
||||
int n = GetNumVectorElements(sz);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int regnum = (prefix >> (i * 2)) & 3;
|
||||
int abs = (prefix >> (8 + i)) & 1;
|
||||
int negate = (prefix >> (16 + i)) & 1;
|
||||
int constants = (prefix >> (12 + i)) & 1;
|
||||
int regnum = VFPUPrefixSwizzle(prefix, i);
|
||||
bool abs = VFPUPrefixAbs(prefix, i);
|
||||
bool negate = VFPUPrefixNegate(prefix, i);
|
||||
bool constants = VFPUPrefixIsConstant(prefix, i);
|
||||
if (constants) {
|
||||
continue;
|
||||
}
|
||||
@@ -178,19 +178,6 @@ namespace MIPSComp {
|
||||
return IsPrefixWithinSize(prefix, GetVecSize(op));
|
||||
}
|
||||
|
||||
// Whether lane i of an S or T prefix is a constant, and its value. Past the vector size, the dot
|
||||
// products add in the lanes where both S and T are constants, and read zero for the rest
|
||||
// (cpu/vfpu/prefix_lanes), which IsPrefixWithinSize() lets through.
|
||||
static bool PrefixLaneConstant(u32 prefix, int i, float *value) {
|
||||
static const float constants[8] = { 0.f, 1.f, 2.f, 0.5f, 3.f, 1.f / 3.f, 0.25f, 1.f / 6.f };
|
||||
if (((prefix >> (12 + i)) & 1) == 0)
|
||||
return false;
|
||||
const int regnum = (prefix >> (i * 2)) & 3;
|
||||
const int abs = (prefix >> (8 + i)) & 1;
|
||||
const int negate = (prefix >> (16 + i)) & 1;
|
||||
*value = negate ? -constants[regnum + (abs << 2)] : constants[regnum + (abs << 2)];
|
||||
return true;
|
||||
}
|
||||
|
||||
void IRFrontend::Comp_VPFX(MIPSOpcode op) {
|
||||
CONDITIONAL_DISABLE(VFPU_XFER);
|
||||
@@ -229,7 +216,6 @@ namespace MIPSComp {
|
||||
|
||||
int n = GetNumVectorElements(sz);
|
||||
u8 origV[4]{};
|
||||
static const float constantArray[8] = { 0.f, 1.f, 2.f, 0.5f, 3.f, 1.f / 3.f, 0.25f, 1.f / 6.f };
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
origV[i] = vregs[i];
|
||||
@@ -277,10 +263,10 @@ namespace MIPSComp {
|
||||
// Check if we're just zeroing certain lanes - this is common.
|
||||
u32 zeroedLanes = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int regnum = (prefix >> (i * 2)) & 3;
|
||||
int abs = (prefix >> (8 + i)) & 1;
|
||||
int negate = (prefix >> (16 + i)) & 1;
|
||||
int constants = (prefix >> (12 + i)) & 1;
|
||||
int regnum = VFPUPrefixSwizzle(prefix, i);
|
||||
bool abs = VFPUPrefixAbs(prefix, i);
|
||||
bool negate = VFPUPrefixNegate(prefix, i);
|
||||
bool constants = VFPUPrefixIsConstant(prefix, i);
|
||||
|
||||
if (!constants && regnum == i && !abs && !negate)
|
||||
continue;
|
||||
@@ -304,10 +290,10 @@ namespace MIPSComp {
|
||||
|
||||
// Alright, fall back to the generic approach.
|
||||
for (int i = 0; i < n; i++) {
|
||||
int regnum = (prefix >> (i * 2)) & 3;
|
||||
int abs = (prefix >> (8 + i)) & 1;
|
||||
int negate = (prefix >> (16 + i)) & 1;
|
||||
int constants = (prefix >> (12 + i)) & 1;
|
||||
int regnum = VFPUPrefixSwizzle(prefix, i);
|
||||
bool abs = VFPUPrefixAbs(prefix, i);
|
||||
bool negate = VFPUPrefixNegate(prefix, i);
|
||||
bool constants = VFPUPrefixIsConstant(prefix, i);
|
||||
|
||||
// Unchanged, hurray.
|
||||
if (!constants && regnum == i && !abs && !negate)
|
||||
@@ -331,11 +317,7 @@ namespace MIPSComp {
|
||||
ir.Write(IROp::FMov, vregs[i], origV[regnum]);
|
||||
}
|
||||
} else {
|
||||
if (negate) {
|
||||
ir.WriteSetConstantFloat(vregs[i], -constantArray[regnum + (abs << 2)]);
|
||||
} else {
|
||||
ir.WriteSetConstantFloat(vregs[i], constantArray[regnum + (abs << 2)]);
|
||||
}
|
||||
ir.WriteSetConstantFloat(vregs[i], VFPUPrefixConstant(prefix, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -385,10 +367,6 @@ namespace MIPSComp {
|
||||
}
|
||||
}
|
||||
|
||||
inline int GetDSat(int prefix, int i) {
|
||||
return (prefix >> (i * 2)) & 3;
|
||||
}
|
||||
|
||||
// "D" prefix is really a post process. No need to allocate a temporary register (except
|
||||
// dummies to simulate writemask, which is done in GetVectorRegsPrefixD
|
||||
void IRFrontend::ApplyPrefixD(u8 *vregs, VectorSize sz, int vectorReg) {
|
||||
@@ -402,7 +380,7 @@ namespace MIPSComp {
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (js.VfpuWriteMask(i))
|
||||
continue;
|
||||
int sat = GetDSat(js.prefixD, i);
|
||||
int sat = VFPUPrefixSat(js.prefixD, i);
|
||||
if (sat == 1) {
|
||||
// clamped = x < 0 ? (x > 1 ? 1 : x) : x [0, 1]
|
||||
ir.Write(IROp::FSat0_1, vregs[i], vregs[i]);
|
||||
@@ -752,10 +730,10 @@ namespace MIPSComp {
|
||||
VectorSize sz = GetVecSize(op);
|
||||
int n = GetNumVectorElements(sz);
|
||||
|
||||
// Constants past n are added in too (see PrefixLaneConstant()), rare enough for the interpreter.
|
||||
// On the PSP, an S constant past n is added in too (cpu/vfpu/prefix_lanes), which
|
||||
// IsPrefixWithinSize() lets through. Rare enough for the interpreter.
|
||||
for (int i = n; i < 4; i++) {
|
||||
float s;
|
||||
if (PrefixLaneConstant(js.prefixS, i, &s))
|
||||
if (VFPUPrefixIsConstant(js.prefixS, i))
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
@@ -798,11 +776,11 @@ namespace MIPSComp {
|
||||
VectorSize sz = GetVecSize(op);
|
||||
int n = GetNumVectorElements(sz);
|
||||
|
||||
// Lanes past n where both prefixes are constants add their product (see
|
||||
// PrefixLaneConstant()), rare enough for the interpreter.
|
||||
// On the PSP, lanes past n where both prefixes are constants add their product, and the rest
|
||||
// read zero (cpu/vfpu/prefix_lanes). IsPrefixWithinSize() lets those through, and they're
|
||||
// rare enough for the interpreter.
|
||||
for (int i = n; i < 4; i++) {
|
||||
float s, t;
|
||||
if (PrefixLaneConstant(js.prefixS, i, &s) && PrefixLaneConstant(js.prefixT, i, &t))
|
||||
if (VFPUPrefixIsConstant(js.prefixS, i) && VFPUPrefixIsConstant(js.prefixT, i))
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
|
||||
@@ -278,6 +278,11 @@ enum class Vec4Init {
|
||||
Set_0001,
|
||||
};
|
||||
|
||||
// The source lane of dest lane i in a Vec4Shuffle, two bits per lane like a VFPU prefix's swizzle.
|
||||
inline int Vec4ShuffleLane(u32 shuffle, int i) {
|
||||
return (shuffle >> (i * 2)) & 3;
|
||||
}
|
||||
|
||||
enum class IRRoundMode : uint8_t {
|
||||
RINT_0 = 0,
|
||||
CAST_1 = 1,
|
||||
|
||||
@@ -465,7 +465,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
|
||||
// or a big switch - there are only 256 shuffles possible (4^4)
|
||||
float temp[4];
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
temp[i] = mips->f[(u32)inst->src1 + (u32)((inst->src2 >> (i * 2)) & 3)];
|
||||
temp[i] = mips->f[(u32)inst->src1 + (u32)Vec4ShuffleLane(inst->src2, i)];
|
||||
const u32 dest = inst->dest;
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
mips->f[dest + i] = temp[i];
|
||||
|
||||
@@ -129,17 +129,16 @@ static void ApplyPrefixST(MIPSState *mips, float *r, u32 data, VectorSize size,
|
||||
|
||||
int n = GetNumVectorElements(size);
|
||||
float origV[4]{ invalid, invalid, invalid, invalid };
|
||||
static const float constantArray[8] = {0.f, 1.f, 2.f, 0.5f, 3.f, 1.f/3.f, 0.25f, 1.f/6.f};
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
origV[i] = r[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
int regnum = (data >> (i*2)) & 3;
|
||||
int abs = (data >> (8+i)) & 1;
|
||||
int negate = (data >> (16+i)) & 1;
|
||||
int constants = (data >> (12+i)) & 1;
|
||||
int regnum = VFPUPrefixSwizzle(data, i);
|
||||
bool abs = VFPUPrefixAbs(data, i);
|
||||
bool negate = VFPUPrefixNegate(data, i);
|
||||
bool constants = VFPUPrefixIsConstant(data, i);
|
||||
|
||||
if (!constants) {
|
||||
if (regnum >= n) {
|
||||
@@ -150,7 +149,7 @@ static void ApplyPrefixST(MIPSState *mips, float *r, u32 data, VectorSize size,
|
||||
if (abs)
|
||||
((u32 *)r)[i] = ((u32 *)r)[i] & 0x7FFFFFFF;
|
||||
} else {
|
||||
r[i] = constantArray[regnum + (abs<<2)];
|
||||
r[i] = vfpuPrefixConstants[regnum + (abs << 2)];
|
||||
}
|
||||
|
||||
if (negate)
|
||||
@@ -176,7 +175,7 @@ void ApplyPrefixD(MIPSState *mips, float *v, VectorSize size, bool onlyWriteMask
|
||||
int n = GetNumVectorElements(size);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
int sat = (data >> (i * 2)) & 3;
|
||||
int sat = VFPUPrefixSat(data, i);
|
||||
if (sat == 1)
|
||||
v[i] = vfpu_clamp(v[i], 0.0f, 1.0f);
|
||||
else if (sat == 3)
|
||||
@@ -202,10 +201,10 @@ static void RetainInvalidSwizzleST(MIPSState *mips, float *d, VectorSize sz) {
|
||||
|
||||
// TODO: We can probably do some faster check of sPrefix and tPrefix to skip over this loop.
|
||||
for (int i = 0; i < n; i++) {
|
||||
int swizzleS = (sPrefix >> (i + i)) & 3;
|
||||
int swizzleT = (tPrefix >> (i + i)) & 3;
|
||||
int constS = (sPrefix >> (12 + i)) & 1;
|
||||
int constT = (tPrefix >> (12 + i)) & 1;
|
||||
int swizzleS = VFPUPrefixSwizzle(sPrefix, i);
|
||||
int swizzleT = VFPUPrefixSwizzle(tPrefix, i);
|
||||
bool constS = VFPUPrefixIsConstant(sPrefix, i);
|
||||
bool constT = VFPUPrefixIsConstant(tPrefix, i);
|
||||
if ((swizzleS >= n && !constS) || (swizzleT >= n && !constT))
|
||||
d[i] = 0.0f;
|
||||
}
|
||||
|
||||
@@ -143,12 +143,12 @@ namespace MIPSComp {
|
||||
|
||||
u8 VfpuWriteMask() const {
|
||||
_assert_(prefixDFlag & JitState::PREFIX_KNOWN);
|
||||
return (prefixD >> 8) & 0xF;
|
||||
return VFPUPrefixWriteMask(prefixD);
|
||||
}
|
||||
|
||||
bool VfpuWriteMask(int i) const {
|
||||
_assert_(prefixDFlag & JitState::PREFIX_KNOWN);
|
||||
return (prefixD >> (8 + i)) & 1;
|
||||
return VFPUPrefixWriteMask(prefixD, i);
|
||||
}
|
||||
|
||||
void LogPrefix() {
|
||||
|
||||
@@ -209,7 +209,7 @@ void LoongArch64JitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int lane = (inst.src2 >> (i * 2)) & 3;
|
||||
int lane = Vec4ShuffleLane(inst.src2, i);
|
||||
FMOV_S(regs_.F(inst.dest + i), regs_.F(inst.src1 + lane));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,6 +369,8 @@ void MIPSState::Init() {
|
||||
UpdateBlockShadowHook();
|
||||
}
|
||||
|
||||
const float vfpuPrefixConstants[8] = { 0.f, 1.f, 2.f, 0.5f, 3.f, 1.f / 3.f, 0.25f, 1.f / 6.f };
|
||||
|
||||
bool MIPSState::HasDefaultPrefix() const {
|
||||
return vfpuCtrl[VFPU_CTRL_SPREFIX] == 0xe4 && vfpuCtrl[VFPU_CTRL_TPREFIX] == 0xe4 && vfpuCtrl[VFPU_CTRL_DPREFIX] == 0;
|
||||
}
|
||||
|
||||
+22
-2
@@ -109,6 +109,26 @@ enum {
|
||||
//unknown....
|
||||
};
|
||||
|
||||
// The fields of the VFPU prefixes. Lane i of an S or T prefix takes the lane its swizzle names, or
|
||||
// with the constant bit, the constant the swizzle and abs bit pick, then the abs and negate. Lane i
|
||||
// of a D prefix has a saturation (1 = [0:1], 3 = [-1:1]) and a write mask bit, set to keep the lane.
|
||||
inline int VFPUPrefixSwizzle(u32 prefix, int i) { return (prefix >> (i * 2)) & 3; }
|
||||
inline bool VFPUPrefixAbs(u32 prefix, int i) { return (prefix >> (8 + i)) & 1; }
|
||||
inline bool VFPUPrefixIsConstant(u32 prefix, int i) { return (prefix >> (12 + i)) & 1; }
|
||||
inline bool VFPUPrefixNegate(u32 prefix, int i) { return (prefix >> (16 + i)) & 1; }
|
||||
inline int VFPUPrefixSat(u32 prefix, int i) { return (prefix >> (i * 2)) & 3; }
|
||||
inline u8 VFPUPrefixWriteMask(u32 prefix) { return (prefix >> 8) & 0xF; }
|
||||
inline bool VFPUPrefixWriteMask(u32 prefix, int i) { return (prefix >> (8 + i)) & 1; }
|
||||
|
||||
// The constants of an S or T prefix, by swizzle + 4 * abs.
|
||||
extern const float vfpuPrefixConstants[8];
|
||||
|
||||
// The value of a lane with the constant bit, negate and all.
|
||||
inline float VFPUPrefixConstant(u32 prefix, int i) {
|
||||
const float c = vfpuPrefixConstants[VFPUPrefixSwizzle(prefix, i) + (VFPUPrefixAbs(prefix, i) << 2)];
|
||||
return VFPUPrefixNegate(prefix, i) ? -c : c;
|
||||
}
|
||||
|
||||
enum VCondition
|
||||
{
|
||||
VC_FL,
|
||||
@@ -239,10 +259,10 @@ public:
|
||||
#endif
|
||||
|
||||
u8 VfpuWriteMask() const {
|
||||
return (vfpuCtrl[VFPU_CTRL_DPREFIX] >> 8) & 0xF;
|
||||
return VFPUPrefixWriteMask(vfpuCtrl[VFPU_CTRL_DPREFIX]);
|
||||
}
|
||||
bool VfpuWriteMask(int i) const {
|
||||
return (vfpuCtrl[VFPU_CTRL_DPREFIX] >> (8 + i)) & 1;
|
||||
return VFPUPrefixWriteMask(vfpuCtrl[VFPU_CTRL_DPREFIX], i);
|
||||
}
|
||||
|
||||
bool HasDefaultPrefix() const;
|
||||
|
||||
@@ -159,10 +159,10 @@ namespace MIPSDis
|
||||
static const char * const constan[8] = {"0","1","2","1/2","3","1/3","1/4","1/6"};
|
||||
for (int i=0; i<4; i++)
|
||||
{
|
||||
int regnum = (data>>(i*2)) & 3;
|
||||
int abs = (data>>(8+i)) & 1;
|
||||
int negate = (data>>(16+i)) & 1;
|
||||
int constants = (data>>(12+i)) & 1;
|
||||
int regnum = VFPUPrefixSwizzle(data, i);
|
||||
bool abs = VFPUPrefixAbs(data, i);
|
||||
bool negate = VFPUPrefixNegate(data, i);
|
||||
bool constants = VFPUPrefixIsConstant(data, i);
|
||||
if (negate && outpos < outSize)
|
||||
outpos += truncate_cpy(out + outpos, outSize - outpos, "-");
|
||||
if (abs && !constants && outpos < outSize)
|
||||
@@ -191,8 +191,8 @@ namespace MIPSDis
|
||||
static const char * const satNames[4] = {"", "[0:1]", "X", "[-1:1]"};
|
||||
for (int i=0; i<4; i++)
|
||||
{
|
||||
int sat = (data>>i*2)&3;
|
||||
int mask = (data>>(8+i))&1;
|
||||
int sat = VFPUPrefixSat(data, i);
|
||||
bool mask = VFPUPrefixWriteMask(data, i);
|
||||
if (sat && outpos < outSize)
|
||||
outpos += truncate_cpy(out + outpos, outSize - outpos, satNames[sat]);
|
||||
if (mask && outpos < outSize)
|
||||
|
||||
@@ -194,7 +194,7 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int lane = (inst.src2 >> (i * 2)) & 3;
|
||||
int lane = Vec4ShuffleLane(inst.src2, i);
|
||||
FMV(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + lane));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user