ppsspp/Core/MIPS/IR/IRInst.cpp
2024-09-14 19:46:05 +03:00

383 lines
12 KiB
C++

#include "Common/CommonFuncs.h"
#include "Common/Log.h"
#include "Core/MIPS/IR/IRInst.h"
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Core/HLE/ReplaceTables.h"
// Legend
// ======================
// _ = ignore
// G = GPR register
// C = 32-bit constant from array
// c = 8-bit constant from array
// I = immediate value from instruction
// F = FPR register, single
// V = FPR register, Vec4. Reg number always divisible by 4.
// 2 = FPR register, Vec2 (uncommon)
// v = Vec4Init constant, chosen by immediate
// s = Shuffle immediate (4 2-bit fields, choosing a xyzw shuffle)
// r = Replacement function (in constant field)
//
// WARNING: The IRJit compiler also uses these letters for semantic information!
// So if you add new letters, don't forget to add them to IRNativeRegCacheBase::MappingFromInst.
static const IRMeta irMeta[] = {
{ IROp::Nop, "Nop", "" },
{ IROp::SetConst, "SetConst", "GC" },
{ IROp::SetConstF, "SetConstF", "FC" },
{ IROp::Mov, "Mov", "GG" },
{ IROp::Add, "Add", "GGG" },
{ IROp::Sub, "Sub", "GGG" },
{ IROp::Neg, "Neg", "GG" },
{ IROp::Not, "Not", "GG" },
{ IROp::And, "And", "GGG" },
{ IROp::Or, "Or", "GGG" },
{ IROp::Xor, "Xor", "GGG" },
{ IROp::AddConst, "AddConst", "GGC" },
{ IROp::OptAddConst, "OptAddConst", "GC" },
{ IROp::SubConst, "SubConst", "GGC" },
{ IROp::AndConst, "AndConst", "GGC" },
{ IROp::OrConst, "OrConst", "GGC" },
{ IROp::XorConst, "XorConst", "GGC" },
{ IROp::OptAndConst, "OptAndConst", "GC" },
{ IROp::OptOrConst, "OptOrConst", "GC" },
{ IROp::Shl, "Shl", "GGG" },
{ IROp::Shr, "Shr", "GGG" },
{ IROp::Sar, "Sar", "GGG" },
{ IROp::Ror, "Ror", "GGG" },
{ IROp::ShlImm, "ShlImm", "GGI" },
{ IROp::ShrImm, "ShrImm", "GGI" },
{ IROp::SarImm, "SarImm", "GGI" },
{ IROp::RorImm, "RorImm", "GGI" },
{ IROp::Slt, "Slt", "GGG" },
{ IROp::SltConst, "SltConst", "GGC" },
{ IROp::SltU, "SltU", "GGG" },
{ IROp::SltUConst, "SltUConst", "GGC" },
{ IROp::Clz, "Clz", "GG" },
{ IROp::MovZ, "MovZ", "GGG", IRFLAG_SRC3DST },
{ IROp::MovNZ, "MovNZ", "GGG", IRFLAG_SRC3DST },
{ IROp::Max, "Max", "GGG" },
{ IROp::Min, "Min", "GGG" },
{ IROp::BSwap16, "BSwap16", "GG" },
{ IROp::BSwap32, "BSwap32", "GG" },
{ IROp::Mult, "Mult", "_GG" },
{ IROp::MultU, "MultU", "_GG" },
{ IROp::Madd, "Madd", "_GG" },
{ IROp::MaddU, "MaddU", "_GG" },
{ IROp::Msub, "Msub", "_GG" },
{ IROp::MsubU, "MsubU", "_GG" },
{ IROp::Div, "Div", "_GG" },
{ IROp::DivU, "DivU", "_GG" },
{ IROp::MtLo, "MtLo", "_G" },
{ IROp::MtHi, "MtHi", "_G" },
{ IROp::MfLo, "MfLo", "G" },
{ IROp::MfHi, "MfHi", "G" },
{ IROp::Ext8to32, "Ext8to32", "GG" },
{ IROp::Ext16to32, "Ext16to32", "GG" },
{ IROp::ReverseBits, "ReverseBits", "GG" },
{ IROp::Load8, "Load8", "GGC" },
{ IROp::Load8Ext, "Load8", "GGC" },
{ IROp::Load16, "Load16", "GGC" },
{ IROp::Load16Ext, "Load16Ext", "GGC" },
{ IROp::Load32, "Load32", "GGC" },
{ IROp::Load32Left, "Load32Left", "GGC", IRFLAG_SRC3DST },
{ IROp::Load32Right, "Load32Right", "GGC", IRFLAG_SRC3DST },
{ IROp::Load32Linked, "Load32Linked", "GGC" },
{ IROp::LoadFloat, "LoadFloat", "FGC" },
{ IROp::LoadVec4, "LoadVec4", "VGC" },
{ IROp::Store8, "Store8", "GGC", IRFLAG_SRC3 },
{ IROp::Store16, "Store16", "GGC", IRFLAG_SRC3 },
{ IROp::Store32, "Store32", "GGC", IRFLAG_SRC3 },
{ IROp::Store32Left, "Store32Left", "GGC", IRFLAG_SRC3 },
{ IROp::Store32Right, "Store32Right", "GGC", IRFLAG_SRC3 },
{ IROp::Store32Conditional, "Store32Conditional", "GGC", IRFLAG_SRC3DST },
{ IROp::StoreFloat, "StoreFloat", "FGC", IRFLAG_SRC3 },
{ IROp::StoreVec4, "StoreVec4", "VGC", IRFLAG_SRC3 },
{ IROp::FAdd, "FAdd", "FFF" },
{ IROp::FSub, "FSub", "FFF" },
{ IROp::FMul, "FMul", "FFF" },
{ IROp::FDiv, "FDiv", "FFF" },
{ IROp::FMin, "FMin", "FFF" },
{ IROp::FMax, "FMax", "FFF" },
{ IROp::FMov, "FMov", "FF" },
{ IROp::FSqrt, "FSqrt", "FF" },
{ IROp::FSin, "FSin", "FF" },
{ IROp::FCos, "FCos", "FF" },
{ IROp::FSqrt, "FSqrt", "FF" },
{ IROp::FRSqrt, "FRSqrt", "FF" },
{ IROp::FRecip, "FRecip", "FF" },
{ IROp::FAsin, "FAsin", "FF" },
{ IROp::FNeg, "FNeg", "FF" },
{ IROp::FSign, "FSign", "FF" },
{ IROp::FAbs, "FAbs", "FF" },
{ IROp::FRound, "FRound", "FF" },
{ IROp::FTrunc, "FTrunc", "FF" },
{ IROp::FCeil, "FCeil", "FF" },
{ IROp::FFloor, "FFloor", "FF" },
{ IROp::FCvtWS, "FCvtWS", "FF" },
{ IROp::FCvtSW, "FCvtSW", "FF" },
{ IROp::FCvtScaledWS, "FCvtScaledWS", "FFI" },
{ IROp::FCvtScaledSW, "FCvtScaledSW", "FFI" },
{ IROp::FCmp, "FCmp", "mFF" },
{ IROp::FSat0_1, "FSat(0 - 1)", "FF" },
{ IROp::FSatMinus1_1, "FSat(-1 - 1)", "FF" },
{ IROp::FMovFromGPR, "FMovFromGPR", "FG" },
{ IROp::FMovToGPR, "FMovToGPR", "GF" },
{ IROp::OptFMovToGPRShr8, "OptFMovToGPRShr8", "GF" },
{ IROp::OptFCvtSWFromGPR, "OptFCvtSWFromGPR", "FG" },
{ IROp::FpCondFromReg, "FpCondFromReg", "_G" },
{ IROp::FpCondToReg, "FpCondToReg", "G" },
{ IROp::FpCtrlFromReg, "FpCtrlFromReg", "_G" },
{ IROp::FpCtrlToReg, "FpCtrlToReg", "G" },
{ IROp::VfpuCtrlToReg, "VfpuCtrlToReg", "GT" },
{ IROp::SetCtrlVFPU, "SetCtrlVFPU", "TC" },
{ IROp::SetCtrlVFPUReg, "SetCtrlVFPUReg", "TG" },
{ IROp::SetCtrlVFPUFReg, "SetCtrlVFPUFReg", "TF" },
{ IROp::FCmovVfpuCC, "FCmovVfpuCC", "FFI", IRFLAG_SRC3DST },
{ IROp::FCmpVfpuBit, "FCmpVfpuBit", "IFF" },
{ IROp::FCmpVfpuAggregate, "FCmpVfpuAggregate", "I" },
{ IROp::Vec4Init, "Vec4Init", "Vv" },
{ IROp::Vec4Shuffle, "Vec4Shuffle", "VVs" },
{ IROp::Vec4Blend, "Vec4Blend", "VVVc" },
{ IROp::Vec4Mov, "Vec4Mov", "VV" },
{ IROp::Vec4Add, "Vec4Add", "VVV" },
{ IROp::Vec4Sub, "Vec4Sub", "VVV" },
{ IROp::Vec4Div, "Vec4Div", "VVV" },
{ IROp::Vec4Mul, "Vec4Mul", "VVV" },
{ IROp::Vec4Scale, "Vec4Scale", "VVF" },
{ IROp::Vec4Dot, "Vec4Dot", "FVV" },
{ IROp::Vec4Neg, "Vec4Neg", "VV" },
{ IROp::Vec4Abs, "Vec4Abs", "VV" },
// Pack/Unpack
{ IROp::Vec2Unpack16To31, "Vec2Unpack16To31", "2F" }, // Note that the result is shifted down by 1, hence 31
{ IROp::Vec2Unpack16To32, "Vec2Unpack16To32", "2F" },
{ IROp::Vec4Unpack8To32, "Vec4Unpack8To32", "VF" },
{ IROp::Vec4DuplicateUpperBitsAndShift1, "Vec4DuplicateUpperBitsAndShift1", "VV" },
{ IROp::Vec4ClampToZero, "Vec4ClampToZero", "VV" },
{ IROp::Vec2ClampToZero, "Vec2ClampToZero", "22" },
{ IROp::Vec4Pack32To8, "Vec4Pack32To8", "FV" },
{ IROp::Vec4Pack31To8, "Vec4Pack31To8", "FV" },
{ IROp::Vec2Pack32To16, "Vec2Pack32To16", "F2" },
{ IROp::Vec2Pack31To16, "Vec2Pack31To16", "F2" },
{ IROp::Interpret, "Interpret", "_C", IRFLAG_BARRIER },
{ IROp::Downcount, "Downcount", "_C" },
{ IROp::ExitToPC, "ExitToPC", "", IRFLAG_EXIT },
{ IROp::ExitToConst, "Exit", "C", IRFLAG_EXIT },
{ IROp::ExitToConstIfEq, "ExitIfEq", "CGG", IRFLAG_EXIT },
{ IROp::ExitToConstIfNeq, "ExitIfNeq", "CGG", IRFLAG_EXIT },
{ IROp::ExitToConstIfGtZ, "ExitIfGtZ", "CG", IRFLAG_EXIT },
{ IROp::ExitToConstIfGeZ, "ExitIfGeZ", "CG", IRFLAG_EXIT },
{ IROp::ExitToConstIfLeZ, "ExitIfLeZ", "CG", IRFLAG_EXIT },
{ IROp::ExitToConstIfLtZ, "ExitIfLtZ", "CG", IRFLAG_EXIT },
{ IROp::ExitToReg, "ExitToReg", "_G", IRFLAG_EXIT },
{ IROp::Syscall, "Syscall", "_C", IRFLAG_EXIT },
{ IROp::Break, "Break", "", IRFLAG_EXIT },
{ IROp::SetPC, "SetPC", "_G" },
{ IROp::SetPCConst, "SetPC", "_C" },
{ IROp::CallReplacement, "CallRepl", "Gr", IRFLAG_BARRIER },
{ IROp::Breakpoint, "Breakpoint", "_C", IRFLAG_BARRIER },
{ IROp::MemoryCheck, "MemoryCheck", "IGC", IRFLAG_BARRIER },
{ IROp::ValidateAddress8, "ValidAddr8", "_GC", IRFLAG_BARRIER },
{ IROp::ValidateAddress16, "ValidAddr16", "_GC", IRFLAG_BARRIER },
{ IROp::ValidateAddress32, "ValidAddr32", "_GC", IRFLAG_BARRIER },
{ IROp::ValidateAddress128, "ValidAddr128", "_GC", IRFLAG_BARRIER },
{ IROp::RestoreRoundingMode, "RestoreRoundingMode", "" },
{ IROp::ApplyRoundingMode, "ApplyRoundingMode", "" },
{ IROp::UpdateRoundingMode, "UpdateRoundingMode", "" },
{IROp::LogIRBlock, "LogIRBlock", ""}
};
const IRMeta *metaIndex[256];
// Is there a way to constexpr this?
void InitIR() {
if (metaIndex[0])
return;
for (size_t i = 0; i < ARRAY_SIZE(irMeta); i++) {
metaIndex[(int)irMeta[i].op] = &irMeta[i];
}
}
void IRWriter::Write(IROp op, u8 dst, u8 src1, u8 src2) {
IRInst inst;
inst.op = op;
inst.dest = dst;
inst.src1 = src1;
inst.src2 = src2;
inst.constant = nextConst_;
insts_.push_back(inst);
nextConst_ = 0;
}
void IRWriter::WriteSetConstant(u8 dst, u32 value) {
Write(IROp::SetConst, dst, AddConstant(value));
}
int IRWriter::AddConstant(u32 value) {
nextConst_ = value;
return 255;
}
int IRWriter::AddConstantFloat(float value) {
u32 val;
memcpy(&val, &value, 4);
return AddConstant(val);
}
void IRWriter::ReplaceConstant(size_t instNumber, u32 newConstant) {
_dbg_assert_(instNumber < insts_.size());
insts_[instNumber].constant = newConstant;
}
static std::string GetGPRName(int r) {
if (r < 32) {
return currentDebugMIPS->GetRegName(0, r);
}
switch (r) {
case IRTEMP_0: return "irtemp0";
case IRTEMP_1: return "irtemp1";
case IRTEMP_2: return "irtemp2";
case IRTEMP_3: return "irtemp3";
case IRTEMP_LHS: return "irtemp_lhs";
case IRTEMP_RHS: return "irtemp_rhs";
case IRTEMP_LR_ADDR: return "irtemp_addr";
case IRTEMP_LR_VALUE: return "irtemp_value";
case IRTEMP_LR_MASK: return "irtemp_mask";
case IRTEMP_LR_SHIFT: return "irtemp_shift";
default: return "(unk)";
}
}
void DisassembleParam(char *buf, int bufSize, u8 param, char type, u32 constant) {
static const char * const vfpuCtrlNames[VFPU_CTRL_MAX] = {
"SPFX",
"TPFX",
"DPFX",
"CC",
"INF4",
"RSV5",
"RSV6",
"REV",
"RCX0",
"RCX1",
"RCX2",
"RCX3",
"RCX4",
"RCX5",
"RCX6",
"RCX7",
};
static const char * const initVec4Names[8] = {
"[0 0 0 0]",
"[1 1 1 1]",
"[-1 -1 -1 -1]",
"[1 0 0 0]",
"[0 1 0 0]",
"[0 0 1 0]",
"[0 0 0 1]",
};
static const char * const xyzw = "xyzw";
switch (type) {
case 'G':
snprintf(buf, bufSize, "%s", GetGPRName(param).c_str());
break;
case 'F':
if (param >= 32) {
snprintf(buf, bufSize, "vf%d", param - 32);
} else {
snprintf(buf, bufSize, "f%d", param);
}
break;
case 'V':
if (param >= 32) {
snprintf(buf, bufSize, "vf%d..vf%d", param - 32, param - 32 + 3);
} else {
snprintf(buf, bufSize, "f%d..f%d", param, param + 3);
}
break;
case '2':
if (param >= 32) {
snprintf(buf, bufSize, "vf%d,vf%d", param - 32, param - 32 + 1);
} else {
snprintf(buf, bufSize, "f%d,f%d", param, param + 1);
}
break;
case 'C':
snprintf(buf, bufSize, "0x%08x", constant);
break;
case 'c':
snprintf(buf, bufSize, "0x%02x", constant);
break;
case 'I':
snprintf(buf, bufSize, "0x%02x", param);
break;
case 'm':
snprintf(buf, bufSize, "%d", param);
break;
case 'T':
snprintf(buf, bufSize, "%s", vfpuCtrlNames[param]);
break;
case 'v':
snprintf(buf, bufSize, "%s", initVec4Names[param]);
break;
case 's':
snprintf(buf, bufSize, "%c%c%c%c", xyzw[param & 3], xyzw[(param >> 2) & 3], xyzw[(param >> 4) & 3], xyzw[(param >> 6) & 3]);
break;
case 'r':
{
const ReplacementTableEntry *entry = GetReplacementFunc(constant);
if (entry) {
snprintf(buf, bufSize, "%s", entry->name);
} else {
snprintf(buf, bufSize, "(unkn. repl %d)", constant);
}
break;
}
case '_':
case '\0':
buf[0] = 0;
break;
default:
snprintf(buf, bufSize, "?");
break;
}
}
const IRMeta *GetIRMeta(IROp op) {
return metaIndex[(int)op];
}
void DisassembleIR(char *buf, size_t bufsize, IRInst inst) {
const IRMeta *meta = GetIRMeta(inst.op);
if (!meta) {
snprintf(buf, bufsize, "Unknown %d", (int)inst.op);
return;
}
char bufDst[16];
char bufSrc1[16];
char bufSrc2[16];
// Only really used for constant.
char bufSrc3[16];
DisassembleParam(bufDst, sizeof(bufDst) - 2, inst.dest, meta->types[0], inst.constant);
DisassembleParam(bufSrc1, sizeof(bufSrc1) - 2, inst.src1, meta->types[1], inst.constant);
DisassembleParam(bufSrc2, sizeof(bufSrc2), inst.src2, meta->types[2], inst.constant);
DisassembleParam(bufSrc3, sizeof(bufSrc3), inst.src3, meta->types[3], inst.constant);
if (meta->types[1] && meta->types[0] != '_') {
strcat(bufDst, ", ");
}
if (meta->types[2] && meta->types[1] != '_') {
strcat(bufSrc1, ", ");
}
if (meta->types[3] && meta->types[2] != '_') {
strcat(bufSrc2, ", ");
}
snprintf(buf, bufsize, "%s %s%s%s%s", meta->name, bufDst, bufSrc1, bufSrc2, bufSrc3);
}