mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
ARM64: Fold const-known EE ALU ops into the GPR cache as immediate Movs
This commit is contained in:
@@ -1255,6 +1255,194 @@ static bool recTryTranslateCachedStoreQuad(u32 rt, u32 rs, s32 imm, RecGprCacheS
|
||||
return true;
|
||||
}
|
||||
|
||||
// Constant folding into the register cache: when every source operand of an ALU op
|
||||
// is const-known, compute the result at compile time and emit a single immediate Mov
|
||||
// into the destination's cache register (dirty — flushed on demand like any cached
|
||||
// write). The folding formulas below are kept textually identical to the tracking
|
||||
// formulas in recConstApplyCachedEffects so the emitted value and the const state can
|
||||
// never diverge. Runs before recTryTranslateCachedOp in recTranslateOpOptimized;
|
||||
// returns false to fall through when any needed source is unknown.
|
||||
static bool recTryTranslateCachedConstOp(u32 op, RecGprConstState& const_state, RecGprCacheState& cache)
|
||||
{
|
||||
const u32 opcode = op >> 26;
|
||||
const u32 rs = (op >> 21) & 0x1f;
|
||||
const u32 rt = (op >> 16) & 0x1f;
|
||||
const u32 rd = (op >> 11) & 0x1f;
|
||||
const u32 sa = (op >> 6) & 0x1f;
|
||||
const u32 funct = op & 0x3f;
|
||||
const s32 imm = static_cast<s16>(op);
|
||||
const u32 imm_u = static_cast<u16>(op);
|
||||
|
||||
auto known = [&](u32 reg) -> bool {
|
||||
return const_state.known[reg];
|
||||
};
|
||||
auto value = [&](u32 reg) -> u64 {
|
||||
return const_state.value[reg];
|
||||
};
|
||||
auto emit_known = [&](u32 reg, u64 val) -> bool {
|
||||
if (reg != 0)
|
||||
{
|
||||
const a64::Register& dst = recCacheDest(cache, reg);
|
||||
armAsm->Mov(dst, val);
|
||||
}
|
||||
recConstSetKnown(const_state, reg, val);
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x08: // ADDI
|
||||
case 0x09: // ADDIU
|
||||
if (!known(rs))
|
||||
return false;
|
||||
return emit_known(rt, recSignExtend32(static_cast<u32>(value(rs)) + static_cast<u32>(imm)));
|
||||
|
||||
case 0x18: // DADDI
|
||||
case 0x19: // DADDIU
|
||||
if (!known(rs))
|
||||
return false;
|
||||
return emit_known(rt, value(rs) + static_cast<u64>(static_cast<s64>(imm)));
|
||||
|
||||
case 0x0A: // SLTI
|
||||
if (!known(rs))
|
||||
return false;
|
||||
return emit_known(rt, (static_cast<s64>(value(rs)) < static_cast<s64>(imm)) ? 1 : 0);
|
||||
|
||||
case 0x0B: // SLTIU
|
||||
if (!known(rs))
|
||||
return false;
|
||||
return emit_known(rt, (value(rs) < static_cast<u64>(static_cast<s64>(imm))) ? 1 : 0);
|
||||
|
||||
case 0x0C: // ANDI
|
||||
if (!known(rs))
|
||||
return false;
|
||||
return emit_known(rt, value(rs) & imm_u);
|
||||
|
||||
case 0x0D: // ORI
|
||||
if (!known(rs))
|
||||
return false;
|
||||
return emit_known(rt, value(rs) | imm_u);
|
||||
|
||||
case 0x0E: // XORI
|
||||
if (!known(rs))
|
||||
return false;
|
||||
return emit_known(rt, value(rs) ^ imm_u);
|
||||
|
||||
case 0x0F: // LUI
|
||||
return emit_known(rt, recSignExtend32(static_cast<u32>(imm_u) << 16));
|
||||
|
||||
case 0x00:
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (funct)
|
||||
{
|
||||
case 0x00: // SLL
|
||||
if (!known(rt)) return false;
|
||||
return emit_known(rd, recSignExtend32(static_cast<u32>(value(rt)) << sa));
|
||||
case 0x02: // SRL
|
||||
if (!known(rt)) return false;
|
||||
return emit_known(rd, recSignExtend32(static_cast<u32>(value(rt)) >> sa));
|
||||
case 0x03: // SRA
|
||||
if (!known(rt)) return false;
|
||||
return emit_known(rd, recSignExtend32(static_cast<u32>(static_cast<s32>(static_cast<u32>(value(rt))) >> sa)));
|
||||
case 0x04: // SLLV
|
||||
if (!known(rt) || !known(rs)) return false;
|
||||
return emit_known(rd, recSignExtend32(static_cast<u32>(value(rt)) << (value(rs) & 0x1f)));
|
||||
case 0x06: // SRLV
|
||||
if (!known(rt) || !known(rs)) return false;
|
||||
return emit_known(rd, recSignExtend32(static_cast<u32>(value(rt)) >> (value(rs) & 0x1f)));
|
||||
case 0x07: // SRAV
|
||||
if (!known(rt) || !known(rs)) return false;
|
||||
return emit_known(rd, recSignExtend32(static_cast<u32>(static_cast<s32>(static_cast<u32>(value(rt))) >> (value(rs) & 0x1f))));
|
||||
case 0x14: // DSLLV
|
||||
if (!known(rt) || !known(rs)) return false;
|
||||
return emit_known(rd, value(rt) << (value(rs) & 0x3f));
|
||||
case 0x16: // DSRLV
|
||||
if (!known(rt) || !known(rs)) return false;
|
||||
return emit_known(rd, value(rt) >> (value(rs) & 0x3f));
|
||||
case 0x17: // DSRAV
|
||||
if (!known(rt) || !known(rs)) return false;
|
||||
return emit_known(rd, static_cast<u64>(static_cast<s64>(value(rt)) >> (value(rs) & 0x3f)));
|
||||
case 0x38: // DSLL
|
||||
if (!known(rt)) return false;
|
||||
return emit_known(rd, value(rt) << sa);
|
||||
case 0x3A: // DSRL
|
||||
if (!known(rt)) return false;
|
||||
return emit_known(rd, value(rt) >> sa);
|
||||
case 0x3B: // DSRA
|
||||
if (!known(rt)) return false;
|
||||
return emit_known(rd, static_cast<u64>(static_cast<s64>(value(rt)) >> sa));
|
||||
case 0x3C: // DSLL32
|
||||
if (!known(rt)) return false;
|
||||
return emit_known(rd, value(rt) << (sa + 32));
|
||||
case 0x3E: // DSRL32
|
||||
if (!known(rt)) return false;
|
||||
return emit_known(rd, value(rt) >> (sa + 32));
|
||||
case 0x3F: // DSRA32
|
||||
if (!known(rt)) return false;
|
||||
return emit_known(rd, static_cast<u64>(static_cast<s64>(value(rt)) >> (sa + 32)));
|
||||
|
||||
case 0x20: // ADD
|
||||
case 0x21: // ADDU
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, recSignExtend32(static_cast<u32>(value(rs)) + static_cast<u32>(value(rt))));
|
||||
case 0x22: // SUB
|
||||
case 0x23: // SUBU
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, recSignExtend32(static_cast<u32>(value(rs)) - static_cast<u32>(value(rt))));
|
||||
case 0x2C: // DADD
|
||||
case 0x2D: // DADDU
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, value(rs) + value(rt));
|
||||
case 0x2E: // DSUB
|
||||
case 0x2F: // DSUBU
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, value(rs) - value(rt));
|
||||
case 0x24: // AND
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, value(rs) & value(rt));
|
||||
case 0x25: // OR
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, value(rs) | value(rt));
|
||||
case 0x26: // XOR
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, value(rs) ^ value(rt));
|
||||
case 0x27: // NOR
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, ~(value(rs) | value(rt)));
|
||||
case 0x2A: // SLT
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, (static_cast<s64>(value(rs)) < static_cast<s64>(value(rt))) ? 1 : 0);
|
||||
case 0x2B: // SLTU
|
||||
if (!known(rs) || !known(rt)) return false;
|
||||
return emit_known(rd, (value(rs) < value(rt)) ? 1 : 0);
|
||||
|
||||
case 0x0A: // MOVZ
|
||||
if (!known(rt))
|
||||
return false;
|
||||
if (value(rt) != 0) // condition false at compile time -> architectural no-op
|
||||
return true;
|
||||
if (!known(rs))
|
||||
return false;
|
||||
return emit_known(rd, value(rs));
|
||||
case 0x0B: // MOVN
|
||||
if (!known(rt))
|
||||
return false;
|
||||
if (value(rt) == 0) // condition false at compile time -> architectural no-op
|
||||
return true;
|
||||
if (!known(rs))
|
||||
return false;
|
||||
return emit_known(rd, value(rs));
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool recTryTranslateCachedOp(u32 op, RecGprCacheState& cache)
|
||||
{
|
||||
const u32 opcode = op >> 26;
|
||||
@@ -1576,6 +1764,12 @@ static bool recTryTranslateCachedOp(u32 op, RecGprCacheState& cache)
|
||||
|
||||
static bool recTranslateOpOptimized(u32 op, RecGprConstState& const_state, RecGprCacheState& cache)
|
||||
{
|
||||
// Fold ops with fully const-known sources first: emits one immediate Mov into the
|
||||
// destination's cache register and updates the const state itself, so neither the
|
||||
// generic cached emitter nor the apply-effects pass runs for them.
|
||||
if (recTryTranslateCachedConstOp(op, const_state, cache))
|
||||
return true;
|
||||
|
||||
if (recTryTranslateCachedOp(op, cache))
|
||||
{
|
||||
if (!recConstApplyCachedEffects(op, const_state))
|
||||
|
||||
Reference in New Issue
Block a user