Permalink
Browse files

irjit: Convert lwr and friends to easier code.

This makes it easier to write a (working) jit backend from IR, since these
ops are always annoying to get right.
  • Loading branch information...
unknownbrackets committed Jan 8, 2018
1 parent b11f00c commit d27e42865977d40d2b90258976b2400e707b1952
Showing with 92 additions and 117 deletions.
  1. +2 −107 Core/MIPS/IR/IRCompLoadStore.cpp
  2. +90 −10 Core/MIPS/IR/IRPassSimplify.cpp
@@ -46,113 +46,8 @@
namespace MIPSComp {
void IRFrontend::Comp_ITypeMemLR(MIPSOpcode op, bool load) {
CONDITIONAL_DISABLE;
int offset = _IMM16;
MIPSGPReg rt = _RT;
MIPSGPReg rs = _RS;
int o = op >> 26;
if (!js.inDelaySlot && opts.unalignedLoadStore) {
// Optimisation: Combine to single unaligned load/store.
const bool isLeft = (o == 34 || o == 42);
MIPSOpcode nextOp = GetOffsetInstruction(1);
// Find a matching shifted load/store in opposite direction with opposite offset.
if (nextOp == (isLeft ? (op.encoding + (4 << 26) - 3) : (op.encoding - (4 << 26) + 3))) {
EatInstruction(nextOp);
if (isLeft) {
// Get the unaligned base offset from the lwr/swr instruction.
offset = (signed short)(nextOp & 0xFFFF);
// Already checked it if we're on the lwr.
CheckMemoryBreakpoint(rs, offset);
}
if (load) {
ir.Write(IROp::Load32, rt, rs, ir.AddConstant(offset));
} else {
ir.Write(IROp::Store32, rt, rs, ir.AddConstant(offset));
}
return;
}
}
int addrReg = IRTEMP_0;
int valueReg = IRTEMP_1;
int maskReg = IRTEMP_2;
int shiftReg = IRTEMP_3;
// addrReg = rs + imm
ir.Write(IROp::AddConst, addrReg, rs, ir.AddConstant(offset));
// shiftReg = (addr & 3) * 8
ir.Write(IROp::AndConst, shiftReg, addrReg, ir.AddConstant(3));
ir.Write(IROp::ShlImm, shiftReg, shiftReg, 3);
// addrReg = addr & 0xfffffffc (for stores, later)
ir.Write(IROp::AndConst, addrReg, addrReg, ir.AddConstant(0xFFFFFFFC));
// valueReg = RAM(addrReg)
ir.Write(IROp::Load32, valueReg, addrReg, ir.AddConstant(0));
switch (o) {
case 34: //lwl
// rt &= (0x00ffffff >> shift)
// Alternatively, could shift to a wall and back (but would require two shifts each way.)
ir.WriteSetConstant(maskReg, 0x00ffffff);
ir.Write(IROp::Shr, maskReg, maskReg, shiftReg);
ir.Write(IROp::And, rt, rt, maskReg);
// valueReg <<= (24 - shift)
ir.Write(IROp::Neg, shiftReg, shiftReg);
ir.Write(IROp::AddConst, shiftReg, shiftReg, ir.AddConstant(24));
ir.Write(IROp::Shl, valueReg, valueReg, shiftReg);
// rt |= valueReg
ir.Write(IROp::Or, rt, rt, valueReg);
break;
case 38: //lwr
// valueReg >>= shift
ir.Write(IROp::Shr, valueReg, valueReg, shiftReg);
// shiftReg = 24 - shift
ir.Write(IROp::Neg, shiftReg, shiftReg);
ir.Write(IROp::AddConst, shiftReg, shiftReg, ir.AddConstant(24));
// rt &= (0xffffff00 << (24 - shift))
// Alternatively, could shift to a wall and back (but would require two shifts each way.)
ir.WriteSetConstant(maskReg, 0xffffff00);
ir.Write(IROp::Shl, maskReg, maskReg, shiftReg);
ir.Write(IROp::And, rt, rt, maskReg);
// rt |= valueReg
ir.Write(IROp::Or, rt, rt, valueReg);
break;
case 42: //swl
// valueReg &= 0xffffff00 << shift
ir.WriteSetConstant(maskReg, 0xffffff00);
ir.Write(IROp::Shl, maskReg, maskReg, shiftReg);
ir.Write(IROp::And, valueReg, valueReg, maskReg);
// shiftReg = 24 - shift
ir.Write(IROp::Neg, shiftReg, shiftReg);
ir.Write(IROp::AddConst, shiftReg, shiftReg, ir.AddConstant(24));
// valueReg |= rt >> (24 - shift)
ir.Write(IROp::Shr, maskReg, rt, shiftReg);
ir.Write(IROp::Or, valueReg, valueReg, maskReg);
break;
case 46: //swr
// valueReg &= 0x00ffffff << (24 - shift)
ir.WriteSetConstant(maskReg, 0x00ffffff);
ir.Write(IROp::Neg, shiftReg, shiftReg);
ir.Write(IROp::AddConst, shiftReg, shiftReg, ir.AddConstant(24));
ir.Write(IROp::Shr, maskReg, maskReg, shiftReg);
ir.Write(IROp::And, valueReg, valueReg, maskReg);
ir.Write(IROp::Neg, shiftReg, shiftReg);
ir.Write(IROp::AddConst, shiftReg, shiftReg, ir.AddConstant(24));
// valueReg |= rt << shift
ir.Write(IROp::Shl, maskReg, rt, shiftReg);
ir.Write(IROp::Or, valueReg, valueReg, maskReg);
break;
default:
INVALIDOP;
return;
}
if (!load) {
// RAM(addrReg) = valueReg
ir.Write(IROp::Store32, valueReg, addrReg, ir.AddConstant(0));
}
// Should never get here, done in Comp_ITypeMem().
INVALIDOP;
}
void IRFrontend::Comp_ITypeMem(MIPSOpcode op) {
@@ -208,6 +208,7 @@ bool RemoveLoadStoreLeftRight(const IRWriter &in, IRWriter &out, const IROptions
const IRInst &next = i + 1 < n ? in.GetInstructions()[i + 1] : dummy;
// TODO: Reorder or look ahead to combine?
auto combineOpposite = [&](IROp matchOp, int matchOff, IROp replaceOp, int replaceOff) {
if (!opts.unalignedLoadStore)
return false;
@@ -223,32 +224,90 @@ bool RemoveLoadStoreLeftRight(const IRWriter &in, IRWriter &out, const IROptions
return true;
};
auto addCommonProlog = [&]() {
// IRTEMP_LR_ADDR = rs + imm
out.Write(IROp::AddConst, IRTEMP_LR_ADDR, inst.src1, out.AddConstant(inst.constant));
// IRTEMP_LR_SHIFT = (addr & 3) * 8
out.Write(IROp::AndConst, IRTEMP_LR_SHIFT, IRTEMP_LR_ADDR, out.AddConstant(3));
out.Write(IROp::ShlImm, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT, 3);
// IRTEMP_LR_ADDR = addr & 0xfffffffc (for stores, later)
out.Write(IROp::AndConst, IRTEMP_LR_ADDR, IRTEMP_LR_ADDR, out.AddConstant(0xFFFFFFFC));
// IRTEMP_LR_VALUE = RAM(IRTEMP_LR_ADDR)
out.Write(IROp::Load32, IRTEMP_LR_VALUE, IRTEMP_LR_ADDR, out.AddConstant(0));
};
auto addCommonStore = [&](int off = 0) {
// RAM(IRTEMP_LR_ADDR) = IRTEMP_LR_VALUE
out.Write(IROp::Store32, IRTEMP_LR_VALUE, IRTEMP_LR_ADDR, out.AddConstant(off));
};
switch (inst.op) {
case IROp::Load32Left:
if (!combineOpposite(IROp::Load32Right, -3, IROp::Load32, -3)) {
// TODO: Flatten.
out.Write(inst);
addCommonProlog();
// dest &= (0x00ffffff >> shift)
// Alternatively, could shift to a wall and back (but would require two shifts each way.)
out.WriteSetConstant(IRTEMP_LR_MASK, 0x00ffffff);
out.Write(IROp::Shr, IRTEMP_LR_MASK, IRTEMP_LR_MASK, IRTEMP_LR_SHIFT);
out.Write(IROp::And, inst.dest, inst.dest, IRTEMP_LR_MASK);
// IRTEMP_LR_VALUE <<= (24 - shift)
out.Write(IROp::Neg, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT);
out.Write(IROp::AddConst, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT, out.AddConstant(24));
out.Write(IROp::Shl, IRTEMP_LR_VALUE, IRTEMP_LR_VALUE, IRTEMP_LR_SHIFT);
// dest |= IRTEMP_LR_VALUE
out.Write(IROp::Or, inst.dest, inst.dest, IRTEMP_LR_VALUE);
}
break;
case IROp::Load32Right:
if (!combineOpposite(IROp::Load32Left, 3, IROp::Load32, 0)) {
// TODO: Flatten.
out.Write(inst);
addCommonProlog();
// IRTEMP_LR_VALUE >>= shift
out.Write(IROp::Shr, IRTEMP_LR_VALUE, IRTEMP_LR_VALUE, IRTEMP_LR_SHIFT);
// IRTEMP_LR_SHIFT = 24 - shift
out.Write(IROp::Neg, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT);
out.Write(IROp::AddConst, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT, out.AddConstant(24));
// dest &= (0xffffff00 << (24 - shift))
// Alternatively, could shift to a wall and back (but would require two shifts each way.)
out.WriteSetConstant(IRTEMP_LR_MASK, 0xffffff00);
out.Write(IROp::Shl, IRTEMP_LR_MASK, IRTEMP_LR_MASK, IRTEMP_LR_SHIFT);
out.Write(IROp::And, inst.dest, inst.dest, IRTEMP_LR_MASK);
// dest |= IRTEMP_LR_VALUE
out.Write(IROp::Or, inst.dest, inst.dest, IRTEMP_LR_VALUE);
}
break;
case IROp::Store32Left:
if (!combineOpposite(IROp::Store32Right, -3, IROp::Store32, -3)) {
// TODO: Flatten.
out.Write(inst);
addCommonProlog();
// IRTEMP_LR_VALUE &= 0xffffff00 << shift
out.WriteSetConstant(IRTEMP_LR_MASK, 0xffffff00);
out.Write(IROp::Shl, IRTEMP_LR_MASK, IRTEMP_LR_MASK, IRTEMP_LR_SHIFT);
out.Write(IROp::And, IRTEMP_LR_VALUE, IRTEMP_LR_VALUE, IRTEMP_LR_MASK);
// IRTEMP_LR_SHIFT = 24 - shift
out.Write(IROp::Neg, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT);
out.Write(IROp::AddConst, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT, out.AddConstant(24));
// IRTEMP_LR_VALUE |= src3 >> (24 - shift)
out.Write(IROp::Shr, IRTEMP_LR_MASK, inst.src3, IRTEMP_LR_SHIFT);
out.Write(IROp::Or, IRTEMP_LR_VALUE, IRTEMP_LR_VALUE, IRTEMP_LR_MASK);
addCommonStore(0);
}
break;
case IROp::Store32Right:
if (!combineOpposite(IROp::Store32Left, 3, IROp::Store32, 0)) {
// TODO: Flatten.
out.Write(inst);
addCommonProlog();
// IRTEMP_LR_VALUE &= 0x00ffffff << (24 - shift)
out.WriteSetConstant(IRTEMP_LR_MASK, 0x00ffffff);
out.Write(IROp::Neg, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT);
out.Write(IROp::AddConst, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT, out.AddConstant(24));
out.Write(IROp::Shr, IRTEMP_LR_MASK, IRTEMP_LR_MASK, IRTEMP_LR_SHIFT);
out.Write(IROp::And, IRTEMP_LR_VALUE, IRTEMP_LR_VALUE, IRTEMP_LR_MASK);
out.Write(IROp::Neg, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT);
out.Write(IROp::AddConst, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT, out.AddConstant(24));
// IRTEMP_LR_VALUE |= src3 << shift
out.Write(IROp::Shl, IRTEMP_LR_MASK, inst.src3, IRTEMP_LR_SHIFT);
out.Write(IROp::Or, IRTEMP_LR_VALUE, IRTEMP_LR_VALUE, IRTEMP_LR_MASK);
addCommonStore(0);
}
break;
@@ -289,6 +348,10 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
}
if (gpr.IsImm(inst.src1) && gpr.IsImm(inst.src2)) {
gpr.SetImm(inst.dest, Evaluate(gpr.GetImm(inst.src1), gpr.GetImm(inst.src2), inst.op));
} else if (inst.op == IROp::And && gpr.IsImm(inst.src1) && gpr.GetImm(inst.src1) == 0) {
gpr.SetImm(inst.dest, 0);
} else if (inst.op == IROp::And && gpr.IsImm(inst.src2) && gpr.GetImm(inst.src2) == 0) {
gpr.SetImm(inst.dest, 0);
} else if (gpr.IsImm(inst.src2)) {
const u32 imm2 = gpr.GetImm(inst.src2);
gpr.MapDirtyIn(inst.dest, inst.src1);
@@ -338,7 +401,10 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
case IROp::XorConst:
case IROp::SltConst:
case IROp::SltUConst:
if (gpr.IsImm(inst.src1)) {
// And 0 is otherwise set to 0. Happens when optimizing lwl.
if (inst.op == IROp::AndConst && inst.constant == 0) {
gpr.SetImm(inst.dest, 0);
} else if (gpr.IsImm(inst.src1)) {
gpr.SetImm(inst.dest, Evaluate(gpr.GetImm(inst.src1), inst.constant, inst.op));
} else {
gpr.MapDirtyIn(inst.dest, inst.src1);
@@ -657,6 +723,16 @@ int IRDestGPR(const IRInst &inst) {
return -1;
}
IRInst IRReplaceDestGPR(const IRInst &inst, int fromReg, int toReg) {
IRInst newInst = inst;
const IRMeta *m = GetIRMeta(inst.op);
if ((m->flags & IRFLAG_SRC3) == 0 && m->types[0] == 'G' && inst.dest == fromReg) {
newInst.dest = toReg;
}
return newInst;
}
bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
std::vector<IRInst> insts;
insts.reserve(in.GetInstructions().size());
@@ -715,6 +791,10 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
case IRTEMP_3:
case IRTEMP_LHS:
case IRTEMP_RHS:
case IRTEMP_LR_ADDR:
case IRTEMP_LR_VALUE:
case IRTEMP_LR_MASK:
case IRTEMP_LR_SHIFT:
// Unlike other ops, these don't need to persist between blocks.
// So we consider them not read unless proven read.
lastWrittenTo[dest] = i;
@@ -730,7 +810,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
default:
lastWrittenTo[dest] = i;
if (dest > IRTEMP_RHS) {
if (dest > IRTEMP_LR_SHIFT) {
// These might sometimes be implicitly read/written by other instructions.
break;
}

0 comments on commit d27e428

Please sign in to comment.