From 99468c6fc157c173a537b53d9d514f8617460d94 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 9 May 2016 21:40:46 -0700 Subject: [PATCH 01/11] jit-ir: Optimize out unused temp regs. This way, if constants have made the temp obsolete (common with ins, for example), it won't even get set anymore. --- Core/MIPS/IR/IRFrontend.cpp | 1 + Core/MIPS/IR/IRInst.cpp | 10 ++--- Core/MIPS/IR/IRInst.h | 4 ++ Core/MIPS/IR/IRPassSimplify.cpp | 76 +++++++++++++++++++++++++++++++++ Core/MIPS/IR/IRPassSimplify.h | 1 + 5 files changed, 87 insertions(+), 5 deletions(-) diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index 0a3fc04329..3e6c0e755d 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -233,6 +233,7 @@ void IRFrontend::DoJit(u32 em_address, std::vector &instructions, std::v if (true) { static const IRPassFunc passes[] = { &PropagateConstants, + &PurgeTemps, }; if (IRApplyPasses(passes, ARRAY_SIZE(passes), ir, simplified)) logBlocks = 1; diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 38b8f54e2d..b95d5fafa3 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -60,11 +60,11 @@ static const IRMeta irMeta[] = { { IROp::Load32, "Load32", "GGC" }, { IROp::LoadFloat, "LoadFloat", "FGC" }, { IROp::LoadVec4, "LoadVec4", "FGC" }, - { IROp::Store8, "Store8", "GGC" }, - { IROp::Store16, "Store16", "GGC" }, - { IROp::Store32, "Store32", "GGC" }, - { IROp::StoreFloat, "StoreFloat", "FGC" }, - { IROp::StoreVec4, "StoreVec4", "FGC" }, + { IROp::Store8, "Store8", "GGC", IRFLAG_SRC3 }, + { IROp::Store16, "Store16", "GGC", IRFLAG_SRC3 }, + { IROp::Store32, "Store32", "GGC", IRFLAG_SRC3 }, + { IROp::StoreFloat, "StoreFloat", "FGC", IRFLAG_SRC3 }, + { IROp::StoreVec4, "StoreVec4", "FGC", IRFLAG_SRC3 }, { IROp::FAdd, "FAdd", "FFF" }, { IROp::FSub, "FSub", "FFF" }, { IROp::FMul, "FMul", "FFF" }, diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index df434eb113..b4e330aadc 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -281,6 +281,10 @@ enum { IRREG_FPCOND = 245, }; +enum IRFlags { + IRFLAG_SRC3 = 1, +}; + struct IRMeta { IROp op; const char *name; diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index d18c92761a..abb06fe89c 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -433,3 +433,79 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out) { } return logBlocks; } + +bool IRReadsFromGPR(const IRInst &inst, int reg) { + const IRMeta *m = GetIRMeta(inst.op); + + if (m->types[1] == 'G' && inst.src1 == reg) { + return true; + } + if (m->types[2] == 'G' && inst.src2 == reg) { + return true; + } + if ((m->flags & IRFLAG_SRC3) != 0 && m->types[0] == 'G' && inst.src3 == reg) { + return true; + } + if (inst.op == IROp::Interpret) { + return true; + } + return false; +} + +int IRDestGPR(const IRInst &inst) { + const IRMeta *m = GetIRMeta(inst.op); + + if ((m->flags & IRFLAG_SRC3) == 0 && m->types[0] == 'G') { + return inst.dest; + } + return -1; +} + +bool PurgeTemps(const IRWriter &in, IRWriter &out) { + IRRegCache gpr(&out); + + for (u32 value : in.GetConstants()) { + out.AddConstant(value); + } + + bool logBlocks = false; + for (int i = 0, n = (int)in.GetInstructions().size(); i < n; i++) { + const IRInst &inst = in.GetInstructions()[i]; + + int dest = IRDestGPR(inst); + bool read = true; + switch (dest) { + case IRTEMP_0: + case IRTEMP_1: + case IRTEMP_LHS: + case IRTEMP_RHS: + // Unlike other ops, these don't need to persist between blocks. + // So we consider them not read unless proven read. + read = false; + for (int j = i + 1; j < n; j++) { + const IRInst &laterInst = in.GetInstructions()[j]; + if (IRReadsFromGPR(laterInst, dest)) { + // Read from, so we can't optimize out. + read = true; + break; + } + if (IRDestGPR(laterInst) == dest) { + // Clobbered, we can optimize out. + break; + } + } + break; + + default: + break; + } + + // TODO: VFPU temps? + + if (read) { + out.Write(inst); + } + } + + return logBlocks; +} diff --git a/Core/MIPS/IR/IRPassSimplify.h b/Core/MIPS/IR/IRPassSimplify.h index 72e87ace21..496f4e6aad 100644 --- a/Core/MIPS/IR/IRPassSimplify.h +++ b/Core/MIPS/IR/IRPassSimplify.h @@ -6,3 +6,4 @@ typedef bool (*IRPassFunc)(const IRWriter &in, IRWriter &out); bool IRApplyPasses(const IRPassFunc *passes, size_t c, const IRWriter &in, IRWriter &out); bool PropagateConstants(const IRWriter &in, IRWriter &out); +bool PurgeTemps(const IRWriter &in, IRWriter &out); From d06c6c080cf9b7d53aba958efc30681722eba42d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 9 May 2016 22:25:35 -0700 Subject: [PATCH 02/11] jit-ir: Expand unused regs to regular GPRs. --- Core/MIPS/IR/IRInst.cpp | 26 +++++++++++++------------- Core/MIPS/IR/IRInst.h | 7 ++++++- Core/MIPS/IR/IRPassSimplify.cpp | 24 +++++++++++++++++++++--- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index b95d5fafa3..11db5bf1bf 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -33,8 +33,8 @@ static const IRMeta irMeta[] = { { IROp::SltU, "SltU", "GGG" }, { IROp::SltUConst, "SltUConst", "GGC" }, { IROp::Clz, "Clz", "GG" }, - { IROp::MovZ, "MovZ", "GGG" }, - { IROp::MovNZ, "MovNZ", "GGG" }, + { IROp::MovZ, "MovZ", "GGG", IRFLAG_SRC3DST }, + { IROp::MovNZ, "MovNZ", "GGG", IRFLAG_SRC3DST }, { IROp::Max, "Max", "GGG" }, { IROp::Min, "Min", "GGG" }, { IROp::BSwap16, "BSwap16", "GG" }, @@ -112,19 +112,19 @@ static const IRMeta irMeta[] = { { IROp::Interpret, "Interpret", "_C" }, { IROp::Downcount, "Downcount", "_II" }, - { IROp::ExitToConst, "Exit", "C" }, - { IROp::ExitToConstIfEq, "ExitIfEq", "CGG" }, - { IROp::ExitToConstIfNeq, "ExitIfNeq", "CGG" }, - { IROp::ExitToConstIfGtZ, "ExitIfGtZ", "CG" }, - { IROp::ExitToConstIfGeZ, "ExitIfGeZ", "CG" }, - { IROp::ExitToConstIfLeZ, "ExitIfLeZ", "CG" }, - { IROp::ExitToConstIfLtZ, "ExitIfLtZ", "CG" }, - { IROp::ExitToReg, "ExitToReg", "G" }, - { IROp::Syscall, "Syscall", "_C" }, - { IROp::Break, "Break", ""}, + { 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 | IRFLAG_SRC3 }, + { IROp::Syscall, "Syscall", "_C", IRFLAG_EXIT }, + { IROp::Break, "Break", "", IRFLAG_EXIT}, { IROp::SetPC, "SetPC", "_G" }, { IROp::SetPCConst, "SetPC", "_C" }, - { IROp::CallReplacement, "CallRepl", "_C"}, + { IROp::CallReplacement, "CallRepl", "_C" }, }; const IRMeta *metaIndex[256]; diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index b4e330aadc..d2195892ba 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -282,7 +282,12 @@ enum { }; enum IRFlags { - IRFLAG_SRC3 = 1, + // Uses src3, not dest. + IRFLAG_SRC3 = 0x0001, + // Uses src3 AND dest (i.e. mutates dest.) + IRFLAG_SRC3DST = 0x0002, + // Exit instruction (maybe conditional.) + IRFLAG_EXIT = 0x0004, }; struct IRMeta { diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index abb06fe89c..f33afef6ac 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -443,10 +443,10 @@ bool IRReadsFromGPR(const IRInst &inst, int reg) { if (m->types[2] == 'G' && inst.src2 == reg) { return true; } - if ((m->flags & IRFLAG_SRC3) != 0 && m->types[0] == 'G' && inst.src3 == reg) { + if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == 'G' && inst.src3 == reg) { return true; } - if (inst.op == IROp::Interpret) { + if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement) { return true; } return false; @@ -474,6 +474,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out) { int dest = IRDestGPR(inst); bool read = true; + bool readByExit = true; switch (dest) { case IRTEMP_0: case IRTEMP_1: @@ -482,21 +483,38 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out) { // Unlike other ops, these don't need to persist between blocks. // So we consider them not read unless proven read. read = false; + readByExit = false; + // Intentional fall-through. + + default: + if (dest > IRTEMP_RHS) { + // These might sometimes be implicitly read/written by other instructions. + break; + } for (int j = i + 1; j < n; j++) { const IRInst &laterInst = in.GetInstructions()[j]; + const IRMeta *m = GetIRMeta(laterInst.op); if (IRReadsFromGPR(laterInst, dest)) { // Read from, so we can't optimize out. read = true; break; } + if (readByExit && (m->flags & IRFLAG_EXIT) != 0) { + read = true; + break; + } + if (IRDestGPR(laterInst) == dest) { // Clobbered, we can optimize out. + // This happens sometimes with temporaries used for constant addresses. + read = false; break; } } break; - default: + // Not a GPR output. + case -1: break; } From 9f183c97ba53c392ea1fd4ac7bb43fad8dd2584b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 May 2016 18:27:52 -0700 Subject: [PATCH 03/11] jit-ir: Prevent reading ahead for each reg write. --- Core/MIPS/IR/IRPassSimplify.cpp | 139 +++++++++++++++++++++++++------- Core/MIPS/IR/IRPassSimplify.h | 1 + 2 files changed, 110 insertions(+), 30 deletions(-) diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index f33afef6ac..cdb766eece 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -462,19 +462,45 @@ int IRDestGPR(const IRInst &inst) { } bool PurgeTemps(const IRWriter &in, IRWriter &out) { - IRRegCache gpr(&out); + std::vector insts; + insts.reserve(in.GetInstructions().size()); - for (u32 value : in.GetConstants()) { - out.AddConstant(value); - } + struct Check { + Check(int r, int i, bool rbx) : reg(r), index(i), readByExit(rbx) { + } + + int reg; + int index; + bool readByExit; + }; + std::vector checks; bool logBlocks = false; for (int i = 0, n = (int)in.GetInstructions().size(); i < n; i++) { const IRInst &inst = in.GetInstructions()[i]; + const IRMeta *m = GetIRMeta(inst.op); + + for (Check &check : checks) { + if (check.reg == 0) { + continue; + } + + if (IRReadsFromGPR(inst, check.reg)) { + // Read from, so we can't optimize out. + check.reg = 0; + } else if (check.readByExit && (m->flags & IRFLAG_EXIT) != 0) { + check.reg = 0; + } else if (IRDestGPR(inst) == check.reg) { + // Clobbered, we can optimize out. + // This happens sometimes with temporaries used for constant addresses. + insts[check.index].op = IROp::Mov; + insts[check.index].dest = 0; + insts[check.index].src1 = 0; + check.reg = 0; + } + } int dest = IRDestGPR(inst); - bool read = true; - bool readByExit = true; switch (dest) { case IRTEMP_0: case IRTEMP_1: @@ -482,45 +508,98 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out) { case IRTEMP_RHS: // Unlike other ops, these don't need to persist between blocks. // So we consider them not read unless proven read. - read = false; - readByExit = false; - // Intentional fall-through. + checks.push_back(Check(dest, i, false)); + break; default: if (dest > IRTEMP_RHS) { // These might sometimes be implicitly read/written by other instructions. break; } - for (int j = i + 1; j < n; j++) { - const IRInst &laterInst = in.GetInstructions()[j]; - const IRMeta *m = GetIRMeta(laterInst.op); - if (IRReadsFromGPR(laterInst, dest)) { - // Read from, so we can't optimize out. - read = true; - break; - } - if (readByExit && (m->flags & IRFLAG_EXIT) != 0) { - read = true; - break; - } - - if (IRDestGPR(laterInst) == dest) { - // Clobbered, we can optimize out. - // This happens sometimes with temporaries used for constant addresses. - read = false; - break; - } - } + checks.push_back(Check(dest, i, true)); break; // Not a GPR output. + case 0: case -1: break; } // TODO: VFPU temps? - if (read) { + insts.push_back(inst); + } + + for (Check &check : checks) { + if (!check.readByExit && check.reg > 0) { + insts[check.index].op = IROp::Mov; + insts[check.index].dest = 0; + insts[check.index].src1 = 0; + } + } + + for (u32 value : in.GetConstants()) { + out.AddConstant(value); + } + for (const IRInst &inst : insts) { + if (inst.op != IROp::Mov || inst.dest != 0 || inst.src1 != 0) { + out.Write(inst); + } + } + + return logBlocks; +} + +bool ReduceLoads(const IRWriter &in, IRWriter &out) { + for (u32 value : in.GetConstants()) { + out.AddConstant(value); + } + + // This tells us to skip an AND op that has been optimized out. + // Maybe we could skip multiple, but that'd slow things down and is pretty uncommon. + int nextSkip = -1; + + bool logBlocks = false; + for (int i = 0, n = (int)in.GetInstructions().size(); i < n; i++) { + IRInst inst = in.GetInstructions()[i]; + + if (inst.op == IROp::Load32 || inst.op == IROp::Load16 || inst.op == IROp::Load16Ext) { + int dest = IRDestGPR(inst); + for (int j = i + 1; j < n; j++) { + const IRInst &laterInst = in.GetInstructions()[j]; + const IRMeta *m = GetIRMeta(laterInst.op); + + if ((m->flags & IRFLAG_EXIT) != 0) { + // Exit, so we can't do the optimization. + break; + } + if (IRReadsFromGPR(laterInst, dest)) { + if (IRDestGPR(laterInst) == dest && laterInst.op == IROp::AndConst) { + const u32 mask = in.GetConstants()[laterInst.src2]; + // Here we are, maybe we can reduce the load size based on the mask. + if ((mask & 0xffffff00) == 0) { + inst.op = IROp::Load8; + if (mask == 0xff) { + nextSkip = j; + } + } else if ((mask & 0xffff0000) == 0 && inst.op == IROp::Load32) { + inst.op = IROp::Load16; + if (mask == 0xffff) { + nextSkip = j; + } + } + } + // If it was read, we can't do the optimization. + break; + } + if (IRDestGPR(laterInst) == dest) { + // Someone else wrote, so we can't do the optimization. + break; + } + } + } + + if (i != nextSkip) { out.Write(inst); } } diff --git a/Core/MIPS/IR/IRPassSimplify.h b/Core/MIPS/IR/IRPassSimplify.h index 496f4e6aad..d6dd041259 100644 --- a/Core/MIPS/IR/IRPassSimplify.h +++ b/Core/MIPS/IR/IRPassSimplify.h @@ -7,3 +7,4 @@ bool IRApplyPasses(const IRPassFunc *passes, size_t c, const IRWriter &in, IRWri bool PropagateConstants(const IRWriter &in, IRWriter &out); bool PurgeTemps(const IRWriter &in, IRWriter &out); +bool ReduceLoads(const IRWriter &in, IRWriter &out); From 29ed8d22012342f0dc1c57190bf4c17447e18537 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 May 2016 18:34:27 -0700 Subject: [PATCH 04/11] jit-ir: ExitToReg doesn't write to registers. --- Core/MIPS/IR/IRCompBranch.cpp | 2 +- Core/MIPS/IR/IRFrontend.cpp | 2 +- Core/MIPS/IR/IRInst.cpp | 2 +- Core/MIPS/IR/IRInterpreter.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/IR/IRCompBranch.cpp b/Core/MIPS/IR/IRCompBranch.cpp index 76833bf329..3dd14cef51 100644 --- a/Core/MIPS/IR/IRCompBranch.cpp +++ b/Core/MIPS/IR/IRCompBranch.cpp @@ -363,7 +363,7 @@ void IRFrontend::Comp_JumpReg(MIPSOpcode op) { break; } - ir.Write(IROp::ExitToReg, destReg, 0, 0); + ir.Write(IROp::ExitToReg, 0, destReg, 0); js.compiling = false; } diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index 3e6c0e755d..5c154c7df9 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -143,7 +143,7 @@ void IRFrontend::Comp_ReplacementFunc(MIPSOpcode op) { } else { ApplyRoundingMode(); ir.Write(IROp::Downcount, 0, js.downcountAmount & 0xFF, js.downcountAmount >> 8); - ir.Write(IROp::ExitToReg, MIPS_REG_RA, 0, 0); + ir.Write(IROp::ExitToReg, 0, MIPS_REG_RA, 0); js.compiling = false; } } else { diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 11db5bf1bf..983a4e6a3a 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -119,7 +119,7 @@ static const IRMeta irMeta[] = { { IROp::ExitToConstIfGeZ, "ExitIfGeZ", "CG", IRFLAG_EXIT }, { IROp::ExitToConstIfLeZ, "ExitIfLeZ", "CG", IRFLAG_EXIT }, { IROp::ExitToConstIfLtZ, "ExitIfLtZ", "CG", IRFLAG_EXIT }, - { IROp::ExitToReg, "ExitToReg", "G", IRFLAG_EXIT | IRFLAG_SRC3 }, + { IROp::ExitToReg, "ExitToReg", "_G", IRFLAG_EXIT }, { IROp::Syscall, "Syscall", "_C", IRFLAG_EXIT }, { IROp::Break, "Break", "", IRFLAG_EXIT}, { IROp::SetPC, "SetPC", "_G" }, diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 1a719046cd..cd5ea34283 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -523,7 +523,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const u32 *constPool, int c return constPool[inst->dest]; case IROp::ExitToReg: - return mips->r[inst->dest]; + return mips->r[inst->src1]; case IROp::ExitToConstIfEq: if (mips->r[inst->src1] == mips->r[inst->src2]) From a8126ca1321e82ebf0aa2db358161e8cf0febd25 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 May 2016 20:56:47 -0700 Subject: [PATCH 05/11] jit-ir: Add some missing CONDITIONAL_DISABLEs. --- Core/MIPS/IR/IRCompVFPU.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index f4736be521..57433a4d11 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -223,6 +223,7 @@ namespace MIPSComp { } void IRFrontend::Comp_SV(MIPSOpcode op) { + CONDITIONAL_DISABLE; s32 offset = (signed short)(op & 0xFFFC); int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5); MIPSGPReg rs = _RS; @@ -241,6 +242,7 @@ namespace MIPSComp { } void IRFrontend::Comp_SVQ(MIPSOpcode op) { + CONDITIONAL_DISABLE; int imm = (signed short)(op & 0xFFFC); int vt = (((op >> 16) & 0x1f)) | ((op & 1) << 5); MIPSGPReg rs = _RS; @@ -280,6 +282,7 @@ namespace MIPSComp { } void IRFrontend::Comp_VVectorInit(MIPSOpcode op) { + CONDITIONAL_DISABLE; if (js.HasUnknownPrefix()) { DISABLE; } @@ -300,6 +303,7 @@ namespace MIPSComp { } void IRFrontend::Comp_VIdt(MIPSOpcode op) { + CONDITIONAL_DISABLE; if (js.HasUnknownPrefix()) { DISABLE; } @@ -320,6 +324,7 @@ namespace MIPSComp { } void IRFrontend::Comp_VMatrixInit(MIPSOpcode op) { + CONDITIONAL_DISABLE; MatrixSize sz = GetMtxSize(op); if (sz != M_4x4) { DISABLE; @@ -616,6 +621,7 @@ namespace MIPSComp { } void IRFrontend::Comp_VV2Op(MIPSOpcode op) { + CONDITIONAL_DISABLE; if (js.HasUnknownPrefix()) DISABLE; From 57b3dbff7e800550517f24fbf44fe511a42263e9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 May 2016 20:57:10 -0700 Subject: [PATCH 06/11] jit-ir: Avoid flushing on a few Vec4 ops. --- Core/MIPS/IR/IRCompVFPU.cpp | 2 +- Core/MIPS/IR/IRPassSimplify.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 57433a4d11..6caefc6e29 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -85,7 +85,7 @@ namespace MIPSComp { return IsOverlapSafeAllowS(dreg, -1, sn, sregs, tn, tregs); } - void IRFrontend::Comp_VPFX(MIPSOpcode op) { + void IRFrontend::Comp_VPFX(MIPSOpcode op) { CONDITIONAL_DISABLE; int data = op & 0xFFFFF; int regnum = (op >> 24) & 3; diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index cdb766eece..d604f65314 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -369,10 +369,12 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out) { break; case IROp::Vec4Init: + case IROp::Vec4Mov: case IROp::Vec4Add: case IROp::Vec4Sub: case IROp::Vec4Mul: case IROp::Vec4Div: + case IROp::Vec4Dot: case IROp::Vec4Scale: case IROp::Vec4Shuffle: out.Write(inst); @@ -392,6 +394,8 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out) { gpr.MapDirtyIn(inst.dest, IRREG_VFPU_CTRL_BASE + inst.src1); goto doDefault; + case IROp::CallReplacement: + case IROp::Break: case IROp::Syscall: case IROp::Interpret: case IROp::ExitToConst: From 1ddb2fbfb99f73cf8363a15ca018ff9993961235 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 May 2016 21:02:56 -0700 Subject: [PATCH 07/11] jit-ir: Fix non-SSE Vec4Scale. --- Core/MIPS/IR/IRInterpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index cd5ea34283..4141bc66da 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -208,7 +208,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const u32 *constPool, int c _mm_store_ps(&mips->f[inst->dest], _mm_mul_ps(_mm_load_ps(&mips->f[inst->src1]), _mm_set1_ps(mips->f[inst->src2]))); #else for (int i = 0; i < 4; i++) - mips->f[inst->dest + i] = mips->f[inst->src1 + i] * mips->f[inst->src2 + i]; + mips->f[inst->dest + i] = mips->f[inst->src1 + i] * mips->f[inst->src2]; #endif break; From c11c0465decc8f5e4bb26ce9ce478dc7a27f2de7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 May 2016 21:25:57 -0700 Subject: [PATCH 08/11] jir-ir: Correct vftm SIMD regs. --- Core/MIPS/IR/IRCompVFPU.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 6caefc6e29..3d22a608d3 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1082,25 +1082,25 @@ namespace MIPSComp { GetVectorRegs(tregs, sz, _VT); GetVectorRegs(dregs, sz, _VD); - // SIMD-optimized implementations - if (msz == M_4x4 && IsConsecutive4(tregs) && IsConsecutive4(dregs)) { + // SIMD-optimized implementations - if sregs[0..3] is consecutive, the rest are too. + if (msz == M_4x4 && IsConsecutive4(sregs) && IsConsecutive4(dregs)) { int s0 = IRVTEMP_0; int s1 = IRVTEMP_PFX_T; - if (!IsConsecutive4(sregs)) { + if (!IsConsecutive4(tregs)) { ir.Write(IROp::Vec4Scale, s0, sregs[0], tregs[0]); for (int i = 1; i < 4; i++) { if (!homogenous || (i != n - 1)) { - ir.Write(IROp::Vec4Scale, s1, sregs[i], tregs[i]); + ir.Write(IROp::Vec4Scale, s1, sregs[i * 4], tregs[i]); ir.Write(IROp::Vec4Add, s0, s0, s1); } else { - ir.Write(IROp::Vec4Add, s0, s0, sregs[i]); + ir.Write(IROp::Vec4Add, s0, s0, sregs[i * 4]); } } ir.Write(IROp::Vec4Mov, dregs[0], s0); return; } else if (!homogenous) { for (int i = 0; i < 4; i++) { - ir.Write(IROp::Vec4Dot, s0 + i, sregs[i], tregs[0]); + ir.Write(IROp::Vec4Dot, s0 + i, sregs[i * 4], tregs[0]); } ir.Write(IROp::Vec4Mov, dregs[0], s0); return; From f52120353b6ad4fc9b03bf79b6958f2749032f15 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 May 2016 22:29:31 -0700 Subject: [PATCH 09/11] jit-ir: Apply prefixes for vector init ops. Without this, Gods Eater Burst is horribly broken. --- Core/MIPS/IR/IRCompVFPU.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 3d22a608d3..9d2dc7c1fb 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -292,14 +292,16 @@ namespace MIPSComp { int vd = _VD; int n = GetNumVectorElements(sz); u8 dregs[4]; - GetVectorRegs(dregs, sz, vd); - if (sz == 4 && IsVectorColumn(vd)) { + GetVectorRegsPrefixD(dregs, sz, vd); + + if (sz == V_Quad && IsConsecutive4(dregs)) { ir.Write(IROp::Vec4Init, dregs[0], (int)(type == 6 ? Vec4Init::AllZERO : Vec4Init::AllONE)); } else { for (int i = 0; i < n; i++) { ir.Write(IROp::SetConstF, dregs[i], ir.AddConstantFloat(type == 6 ? 0.0f : 1.0f)); } } + ApplyPrefixD(dregs, sz); } void IRFrontend::Comp_VIdt(MIPSOpcode op) { @@ -313,14 +315,16 @@ namespace MIPSComp { if (sz != V_Quad) DISABLE; - if (!IsVectorColumn(vd)) - DISABLE; - u8 dregs[4]; - GetVectorRegs(dregs, sz, vd); + GetVectorRegsPrefixD(dregs, sz, vd); + if (!IsConsecutive4(dregs)) { + DISABLE; + } int row = vd & 3; + // Might not be consecutive if masked. Vec4Init init = Vec4Init((int)Vec4Init::Set_1000 + row); ir.Write(IROp::Vec4Init, dregs[0], (int)init); + ApplyPrefixD(dregs, sz); } void IRFrontend::Comp_VMatrixInit(MIPSOpcode op) { @@ -794,7 +798,7 @@ namespace MIPSComp { switch ((op >> 21) & 0x1f) { case 3: //mfv / mfvc // rt = 0, imm = 255 appears to be used as a CPU interlock by some games. - if (rt != 0) { + if (rt != MIPS_REG_ZERO) { if (imm < 128) { //R(rt) = VI(imm); ir.Write(IROp::FMovToGPR, rt, vfpuBase + voffset[imm]); } else { From 9e3572dc63b76c7eb06ad02efeb7e86786667e2a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 May 2016 22:38:56 -0700 Subject: [PATCH 10/11] jit-ir: Improve vidt to handle more cases. --- Core/MIPS/IR/IRCompVFPU.cpp | 30 +++++++++++++++++++++--------- Core/MIPS/IR/IRPassSimplify.cpp | 2 +- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 9d2dc7c1fb..ce41c19712 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -312,18 +312,30 @@ namespace MIPSComp { int vd = _VD; VectorSize sz = GetVecSize(op); - if (sz != V_Quad) - DISABLE; - u8 dregs[4]; GetVectorRegsPrefixD(dregs, sz, vd); - if (!IsConsecutive4(dregs)) { - DISABLE; + + if (sz == 4 && IsConsecutive4(dregs)) { + int row = vd & 3; + Vec4Init init = Vec4Init((int)Vec4Init::Set_1000 + row); + ir.Write(IROp::Vec4Init, dregs[0], (int)init); + } else { + switch (sz) { + case V_Pair: + ir.Write(IROp::SetConstF, dregs[0], ir.AddConstantFloat((vd & 1) == 0 ? 1.0f : 0.0f)); + ir.Write(IROp::SetConstF, dregs[1], ir.AddConstantFloat((vd & 1) == 1 ? 1.0f : 0.0f)); + break; + case V_Quad: + ir.Write(IROp::SetConstF, dregs[0], ir.AddConstantFloat((vd & 3) == 0 ? 1.0f : 0.0f)); + ir.Write(IROp::SetConstF, dregs[1], ir.AddConstantFloat((vd & 3) == 1 ? 1.0f : 0.0f)); + ir.Write(IROp::SetConstF, dregs[2], ir.AddConstantFloat((vd & 3) == 2 ? 1.0f : 0.0f)); + ir.Write(IROp::SetConstF, dregs[3], ir.AddConstantFloat((vd & 3) == 3 ? 1.0f : 0.0f)); + break; + default: + DISABLE; + } } - int row = vd & 3; - // Might not be consecutive if masked. - Vec4Init init = Vec4Init((int)Vec4Init::Set_1000 + row); - ir.Write(IROp::Vec4Init, dregs[0], (int)init); + ApplyPrefixD(dregs, sz); } diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index d604f65314..de97635ae8 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -529,7 +529,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out) { break; } - // TODO: VFPU temps? + // TODO: VFPU temps? Especially for masked dregs. insts.push_back(inst); } From 7b43e0e59d561c8b410e9224ceffc4b784ce0904 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 May 2016 22:53:21 -0700 Subject: [PATCH 11/11] jit-ir: Add nan/inf compares. Without this, Gods Eater Burst crashes before going in game. --- Core/MIPS/IR/IRInterpreter.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 4141bc66da..2b2368ec8c 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -226,6 +226,12 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const u32 *constPool, int c case VC_GE: result = mips->f[inst->src1] >= mips->f[inst->src2]; break; case VC_EZ: result = mips->f[inst->src1] == 0.0f; break; case VC_NZ: result = mips->f[inst->src1] != 0.0f; break; + case VC_EN: result = my_isnan(mips->f[inst->src1]); break; + case VC_NN: result = !my_isnan(mips->f[inst->src1]); break; + case VC_EI: result = my_isinf(mips->f[inst->src1]); break; + case VC_NI: result = !my_isinf(mips->f[inst->src1]); break; + case VC_ES: result = my_isnanorinf(mips->f[inst->src1]); break; + case VC_NS: result = !my_isnanorinf(mips->f[inst->src1]); break; case VC_TR: result = 1; break; case VC_FL: result = 0; break; default: