x86jit: Implement shifts.

This commit is contained in:
Unknown W. Brackets 2023-08-24 20:07:44 -07:00
parent 601bf344c1
commit 363f2b68e1
5 changed files with 144 additions and 14 deletions

View File

@ -698,6 +698,10 @@ void IRNativeRegCacheBase::ApplyMapping(const Mapping *mapping, int count) {
}
}
auto isNoinit = [](MIPSMap f) {
return (f & MIPSMap::NOINIT) == MIPSMap::NOINIT;
};
auto mapRegs = [&](int i) {
MIPSLoc type = MIPSLoc::MEM;
switch (mapping[i].type) {
@ -714,24 +718,39 @@ void IRNativeRegCacheBase::ApplyMapping(const Mapping *mapping, int count) {
return;
}
MIPSMap flags = mapping[i].flags;
for (int j = 0; j < count; ++j) {
if (mapping[j].type == mapping[i].type && mapping[j].reg == mapping[i].reg && i != j) {
_assert_msg_(mapping[j].lanes == mapping[i].lanes, "Lane aliasing not supported yet");
if (!isNoinit(mapping[j].flags) && isNoinit(flags)) {
flags = (flags & MIPSMap::BACKEND_MASK) | MIPSMap::DIRTY;
}
}
}
if (config_.mapFPUSIMD || mapping[i].type == 'G') {
MapNativeReg(type, mapping[i].reg, mapping[i].lanes, mapping[i].flags);
MapNativeReg(type, mapping[i].reg, mapping[i].lanes, flags);
return;
}
for (int j = 0; j < mapping[i].lanes; ++j)
MapNativeReg(type, mapping[i].reg + j, 1, mapping[i].flags);
MapNativeReg(type, mapping[i].reg + j, 1, flags);
};
auto mapFilteredRegs = [&](auto pred) {
for (int i = 0; i < count; ++i) {
if (pred(mapping[i].flags))
mapRegs(i);
}
};
// Do two passes: first any without NOINIT, then NOINIT.
for (int i = 0; i < count; ++i) {
if ((mapping[i].flags & MIPSMap::NOINIT) != MIPSMap::NOINIT)
mapRegs(i);
}
for (int i = 0; i < count; ++i) {
if ((mapping[i].flags & MIPSMap::NOINIT) == MIPSMap::NOINIT)
mapRegs(i);
}
// Do two passes: with backend special flags, and without.
mapFilteredRegs([](MIPSMap flags) {
return (flags & MIPSMap::BACKEND_MASK) != MIPSMap::INIT;
});
mapFilteredRegs([](MIPSMap flags) {
return (flags & MIPSMap::BACKEND_MASK) == MIPSMap::INIT;
});
}
void IRNativeRegCacheBase::CleanupMapping(const Mapping *mapping, int count) {

View File

@ -79,6 +79,8 @@ enum class MIPSMap : uint8_t {
INIT = 0,
DIRTY = 1,
NOINIT = 2 | DIRTY,
BACKEND_MASK = 0xF0,
};
static inline MIPSMap operator |(const MIPSMap &lhs, const MIPSMap &rhs) {
return MIPSMap((uint8_t)lhs | (uint8_t)rhs);

View File

@ -365,10 +365,74 @@ void X64JitBackend::CompIR_Shift(IRInst inst) {
switch (inst.op) {
case IROp::Shl:
if (cpu_info.bBMI2) {
regs_.Map(inst);
SHLX(32, regs_.RX(inst.dest), regs_.R(inst.src1), regs_.RX(inst.src2));
} else {
regs_.MapWithFlags(inst, X64Map::NONE, X64Map::NONE, X64Map::SHIFT);
if (inst.dest == inst.src1) {
SHL(32, regs_.R(inst.dest), regs_.R(inst.src2));
} else if (inst.dest == inst.src2) {
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
SHL(32, R(SCRATCH1), regs_.R(inst.src2));
MOV(32, regs_.R(inst.dest), R(SCRATCH1));
} else {
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
SHL(32, regs_.R(inst.dest), regs_.R(inst.src2));
}
}
break;
case IROp::Shr:
if (cpu_info.bBMI2) {
regs_.Map(inst);
SHRX(32, regs_.RX(inst.dest), regs_.R(inst.src1), regs_.RX(inst.src2));
} else {
regs_.MapWithFlags(inst, X64Map::NONE, X64Map::NONE, X64Map::SHIFT);
if (inst.dest == inst.src1) {
SHR(32, regs_.R(inst.dest), regs_.R(inst.src2));
} else if (inst.dest == inst.src2) {
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
SHR(32, R(SCRATCH1), regs_.R(inst.src2));
MOV(32, regs_.R(inst.dest), R(SCRATCH1));
} else {
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
SHR(32, regs_.R(inst.dest), regs_.R(inst.src2));
}
}
break;
case IROp::Sar:
if (cpu_info.bBMI2) {
regs_.Map(inst);
SARX(32, regs_.RX(inst.dest), regs_.R(inst.src1), regs_.RX(inst.src2));
} else {
regs_.MapWithFlags(inst, X64Map::NONE, X64Map::NONE, X64Map::SHIFT);
if (inst.dest == inst.src1) {
SAR(32, regs_.R(inst.dest), regs_.R(inst.src2));
} else if (inst.dest == inst.src2) {
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
SAR(32, R(SCRATCH1), regs_.R(inst.src2));
MOV(32, regs_.R(inst.dest), R(SCRATCH1));
} else {
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
SAR(32, regs_.R(inst.dest), regs_.R(inst.src2));
}
}
break;
case IROp::Ror:
CompIR_Generic(inst);
regs_.MapWithFlags(inst, X64Map::NONE, X64Map::NONE, X64Map::SHIFT);
if (inst.dest == inst.src1) {
ROR(32, regs_.R(inst.dest), regs_.R(inst.src2));
} else if (inst.dest == inst.src2) {
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
ROR(32, R(SCRATCH1), regs_.R(inst.src2));
MOV(32, regs_.R(inst.dest), R(SCRATCH1));
} else {
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
ROR(32, regs_.R(inst.dest), regs_.R(inst.src2));
}
break;
case IROp::ShlImm:
@ -431,6 +495,9 @@ void X64JitBackend::CompIR_Shift(IRInst inst) {
regs_.Map(inst);
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
}
} else if (cpu_info.bBMI2) {
regs_.Map(inst);
RORX(32, regs_.RX(inst.dest), regs_.R(inst.src1), inst.src2 & 31);
} else {
regs_.Map(inst);
if (inst.dest != inst.src1)

View File

@ -63,8 +63,14 @@ const int *X64IRRegCache::GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &c
#endif
};
if ((flags & X64Map::MASK) == X64Map::SHIFT) {
// It's a single option for shifts.
static const int shiftReg[] = { ECX };
count = 1;
return shiftReg;
}
#if PPSSPP_ARCH(X86)
if ((flags & X64Map::LOW_SUBREG) == X64Map::LOW_SUBREG) {
if ((flags & X64Map::MASK) == X64Map::LOW_SUBREG) {
static const int lowSubRegAllocationOrder[] = {
EDX, EBX, ECX,
};
@ -132,7 +138,17 @@ X64Reg X64IRRegCache::TryMapTempImm(IRReg r, X64Map flags) {
_dbg_assert_(IsValidGPR(r));
auto canUseReg = [flags](X64Reg r) {
return (flags & X64Map::LOW_SUBREG) != X64Map::LOW_SUBREG || HasLowSubregister(r);
switch (flags & X64Map::MASK) {
case X64Map::NONE:
return true;
case X64Map::LOW_SUBREG:
return HasLowSubregister(r);
case X64Map::SHIFT:
return r == RCX;
default:
_assert_msg_(false, "Unexpected flags");
}
return false;
};
// If already mapped, no need for a temporary.
@ -175,6 +191,30 @@ void X64IRRegCache::MapWithFlags(IRInst inst, X64Map destFlags, X64Map src1Flags
mapping[1].flags = mapping[1].flags | src1Flags;
mapping[2].flags = mapping[2].flags | src2Flags;
auto flushReg = [&](IRNativeReg nreg) {
for (int i = 0; i < 3; ++i) {
if (mapping[i].reg == nr[nreg].mipsReg && (mapping[i].flags & MIPSMap::NOINIT) == MIPSMap::NOINIT) {
DiscardNativeReg(nreg);
return;
}
}
FlushNativeReg(nreg);
};
// If there are any special rules, we might need to spill.
for (int i = 0; i < 3; ++i) {
switch (mapping[i].flags & X64Map::MASK) {
case X64Map::SHIFT:
if (nr[RCX].mipsReg != mapping[i].reg)
flushReg(RCX);
break;
default:
break;
}
}
ApplyMapping(mapping, 3);
CleanupMapping(mapping, 3);
}

View File

@ -44,6 +44,8 @@ static constexpr auto pcOffset = offsetof(MIPSState, pc) - 128;
enum class X64Map : uint8_t {
NONE = 0,
LOW_SUBREG = 0x10,
SHIFT = 0x20,
MASK = 0xF0,
};
static inline MIPSMap operator |(const MIPSMap &lhs, const X64Map &rhs) {
return MIPSMap((uint8_t)lhs | (uint8_t)rhs);