ARM64: Some more instructions, func replacements

This commit is contained in:
Henrik Rydgard 2015-03-18 18:23:39 +01:00
parent da2f4147da
commit 4233921ab7
8 changed files with 203 additions and 21 deletions

View file

@ -2181,7 +2181,7 @@ void ARM64FloatEmitter::EmitPermute(u32 size, u32 op, ARM64Reg Rd, ARM64Reg Rn,
(1 << 11) | (Rn << 5) | Rd);
}
void ARM64FloatEmitter::EmitScalarImm(bool M, bool S, u32 type, u32 imm5, ARM64Reg Rd, u32 imm)
void ARM64FloatEmitter::EmitScalarImm(bool M, bool S, u32 type, u32 imm5, ARM64Reg Rd, u32 imm8)
{
_assert_msg_(DYNA_REC, !IsQuad(Rd), "%s doesn't support vector!", __FUNCTION__);
@ -2190,7 +2190,7 @@ void ARM64FloatEmitter::EmitScalarImm(bool M, bool S, u32 type, u32 imm5, ARM64R
Rd = DecodeReg(Rd);
Write32((M << 31) | (S << 29) | (0xF1 << 21) | (is_double << 22) | (type << 22) | \
(imm << 13) | (1 << 12) | (imm5 << 5) | Rd);
(imm8 << 13) | (1 << 12) | (imm5 << 5) | Rd);
}
void ARM64FloatEmitter::EmitShiftImm(bool U, u32 immh, u32 immb, u32 opcode, ARM64Reg Rd, ARM64Reg Rn)

View file

@ -780,7 +780,7 @@ public:
// One source
void FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn);
// Conversion between float and integer
// Conversion or movement between float and integer
void FMOV(u8 size, bool top, ARM64Reg Rd, ARM64Reg Rn);
void SCVTF(ARM64Reg Rd, ARM64Reg Rn);
void UCVTF(ARM64Reg Rd, ARM64Reg Rn);
@ -841,7 +841,7 @@ private:
void EmitCompare(bool M, bool S, u32 op, u32 opcode2, ARM64Reg Rn, ARM64Reg Rm);
void EmitCondSelect(bool M, bool S, CCFlags cond, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void EmitPermute(u32 size, u32 op, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void EmitScalarImm(bool M, bool S, u32 type, u32 imm5, ARM64Reg Rd, u32 imm);
void EmitScalarImm(bool M, bool S, u32 type, u32 imm5, ARM64Reg Rd, u32 imm8);
void EmitShiftImm(bool U, u32 immh, u32 immb, u32 opcode, ARM64Reg Rd, ARM64Reg Rn);
void EmitLoadStoreMultipleStructure(u32 size, bool L, u32 opcode, ARM64Reg Rt, ARM64Reg Rn);
void EmitScalar1Source(bool M, bool S, u32 type, u32 opcode, ARM64Reg Rd, ARM64Reg Rn);

View file

@ -204,12 +204,14 @@ void Arm64Jit::GenerateFixedCode() {
// Don't forget to zap the instruction cache!
FlushIcache();
INFO_LOG(JIT, "THE DISASM : %p ========================", enterCode);
std::vector<std::string> lines = DisassembleArm64(enterCode, GetCodePtr() - enterCode);
for (auto s : lines) {
INFO_LOG(JIT, "%s", s.c_str());
if (false) {
INFO_LOG(JIT, "THE DISASM : %p ========================", enterCode);
std::vector<std::string> lines = DisassembleArm64(enterCode, GetCodePtr() - enterCode);
for (auto s : lines) {
INFO_LOG(JIT, "%s", s.c_str());
}
INFO_LOG(JIT, "END OF THE DISASM : %p ========================", GetCodePtr());
}
INFO_LOG(JIT, "END OF THE DISASM : %p ========================", GetCodePtr());
}
} // namespace MIPSComp

View file

@ -383,11 +383,85 @@ void Arm64Jit::Comp_Special3(MIPSOpcode op) {
}
void Arm64Jit::Comp_Allegrex(MIPSOpcode op) {
DISABLE;
CONDITIONAL_DISABLE;
MIPSGPReg rt = _RT;
MIPSGPReg rd = _RD;
// Don't change $zr.
if (rd == 0)
return;
switch ((op >> 6) & 31) {
/*
case 16: // seb // R(rd) = (u32)(s32)(s8)(u8)R(rt);
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, (s32)(s8)(u8)gpr.GetImm(rt));
return;
}
gpr.MapDirtyIn(rd, rt);
SXTB(gpr.R(rd), gpr.R(rt));
break;
case 24: // seh
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, (s32)(s16)(u16)gpr.GetImm(rt));
return;
}
gpr.MapDirtyIn(rd, rt);
SXTH(gpr.R(rd), gpr.R(rt));
break;*/
case 20: //bitrev
if (gpr.IsImm(rt)) {
// http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
u32 v = gpr.GetImm(rt);
v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); // odd<->even
v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); // pair<->pair
v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); // nibb<->nibb
v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); // byte<->byte
v = (v >> 16) | (v << 16); // hword<->hword
gpr.SetImm(rd, v);
return;
}
gpr.MapDirtyIn(rd, rt);
RBIT(gpr.R(rd), gpr.R(rt));
break;
default:
Comp_Generic(op);
return;
}
}
void Arm64Jit::Comp_Allegrex2(MIPSOpcode op) {
DISABLE;
CONDITIONAL_DISABLE;
MIPSGPReg rt = _RT;
MIPSGPReg rd = _RD;
// Don't change $zr.
if (rd == 0)
return;
switch (op & 0x3ff) {
case 0xA0: //wsbh
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, ((gpr.GetImm(rt) & 0xFF00FF00) >> 8) | ((gpr.GetImm(rt) & 0x00FF00FF) << 8));
} else {
gpr.MapDirtyIn(rd, rt);
REV16(gpr.R(rd), gpr.R(rt));
}
break;
case 0xE0: //wsbw
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, swap32(gpr.GetImm(rt)));
} else {
gpr.MapDirtyIn(rd, rt);
REV32(gpr.R(rd), gpr.R(rt));
}
break;
default:
Comp_Generic(op);
break;
}
}
void Arm64Jit::Comp_MulDivType(MIPSOpcode op) {

View file

@ -249,16 +249,16 @@ void Arm64Jit::Comp_mxc1(MIPSOpcode op)
MIPSGPReg rt = _RT;
switch ((op >> 21) & 0x1f) {
/*
case 0: // R(rt) = FI(fs); break; //mfc1
gpr.MapReg(rt, MAP_DIRTY | MAP_NOINIT);
if (fpr.IsMapped(fs)) {
MOV(gpr.R(rt), fpr.R(fs));
fp.FMOV(32, false, gpr.R(rt), fpr.R(fs));
} else {
LDR(INDEX_UNSIGNED, gpr.R(rt), CTXREG, fpr.GetMipsRegOffset(fs));
}
return;
/*
case 2: //cfc1
if (fs == 31) {
if (gpr.IsImm(MIPS_REG_FPCOND)) {
@ -284,18 +284,20 @@ void Arm64Jit::Comp_mxc1(MIPSOpcode op)
gpr.SetImm(rt, 0);
}
return;
*/
case 4: //FI(fs) = R(rt); break; //mtc1
if (gpr.IsImm(rt) && gpr.GetImm(rt) == 0) {
fpr.MapReg(fs, MAP_NOINIT);
MOVI2F(fpr.R(fs), 0.0f, SCRATCH1);
fp.FMOV(fpr.R(fs), 0); // Immediate form
} else {
gpr.MapReg(rt);
fpr.MapReg(fs, MAP_NOINIT);
VMOV(fpr.R(fs), gpr.R(rt));
fp.FMOV(32, false, fpr.R(fs), gpr.R(rt));
}
return;
/*
case 6: //ctc1
if (fs == 31) {
// Must clear before setting, since ApplyRoundingMode() assumes it was cleared.

View file

@ -24,9 +24,8 @@
namespace MIPSComp {
int Arm64Jit::Replace_fabsf() {
// TODO ARM64
// fpr.MapDirtyIn(0, 12);
// VABS(fpr.R(0), fpr.R(12));
fpr.MapDirtyIn(0, 12);
fp.FABS(fpr.R(0), fpr.R(12));
return 4; // Number of instructions in the MIPS function
}

View file

@ -353,13 +353,110 @@ void Arm64Jit::Comp_RunBlock(MIPSOpcode op) {
}
bool Arm64Jit::ReplaceJalTo(u32 dest) {
return false;
#ifdef ARM64
MIPSOpcode op(Memory::Read_Opcode_JIT(dest));
if (!MIPS_IS_REPLACEMENT(op.encoding))
return false;
int index = op.encoding & MIPS_EMUHACK_VALUE_MASK;
const ReplacementTableEntry *entry = GetReplacementFunc(index);
if (!entry) {
ERROR_LOG(HLE, "ReplaceJalTo: Invalid replacement op %08x at %08x", op.encoding, dest);
return false;
}
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT | REPFLAG_DISABLED)) {
// If it's a hook, we can't replace the jal, we have to go inside the func.
return false;
}
INFO_LOG(HLE, "ReplaceJalTo to %s", entry->name);
// Warning - this might be bad if the code at the destination changes...
if (entry->flags & REPFLAG_ALLOWINLINE) {
// Jackpot! Just do it, no flushing. The code will be entirely inlined.
// First, compile the delay slot. It's unconditional so no issues.
CompileDelaySlot(DELAYSLOT_NICE);
// Technically, we should write the unused return address to RA, but meh.
MIPSReplaceFunc repl = entry->jitReplaceFunc;
int cycles = (this->*repl)();
js.downcountAmount += cycles;
} else {
gpr.SetImm(MIPS_REG_RA, js.compilerPC + 8);
CompileDelaySlot(DELAYSLOT_NICE);
FlushAll();
RestoreRoundingMode();
QuickCallFunction(SCRATCH1_64, (const void *)(entry->replaceFunc));
ApplyRoundingMode();
WriteDownCountR(W0);
}
js.compilerPC += 4;
// No writing exits, keep going!
// Add a trigger so that if the inlined code changes, we invalidate this block.
blocks.ProxyBlock(js.blockStart, dest, symbolMap.GetFunctionSize(dest) / sizeof(u32), GetCodePtr());
#endif
return true;
}
void Arm64Jit::Comp_ReplacementFunc(MIPSOpcode op)
{
ERROR_LOG(JIT, "Comp_ReplacementFunc not implemented");
// TODO ARM64
// We get here if we execute the first instruction of a replaced function. This means
// that we do need to return to RA.
// Inlined function calls (caught in jal) are handled differently.
int index = op.encoding & MIPS_EMUHACK_VALUE_MASK;
const ReplacementTableEntry *entry = GetReplacementFunc(index);
if (!entry) {
ERROR_LOG(HLE, "Invalid replacement op %08x", op.encoding);
return;
}
if (entry->flags & REPFLAG_DISABLED) {
MIPSCompileOp(Memory::Read_Instruction(js.compilerPC, true));
} else if (entry->jitReplaceFunc) {
INFO_LOG(HLE, "JitReplaceFunc to %s", entry->name);
MIPSReplaceFunc repl = entry->jitReplaceFunc;
int cycles = (this->*repl)();
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
// Compile the original instruction at this address. We ignore cycles for hooks.
MIPSCompileOp(Memory::Read_Instruction(js.compilerPC, true));
} else {
FlushAll();
// Flushed, so R1 is safe.
LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, MIPS_REG_RA * 4);
js.downcountAmount += cycles;
WriteExitDestInR(SCRATCH1);
js.compiling = false;
}
} else if (entry->replaceFunc) {
INFO_LOG(HLE, "ReplaceFunc to %s", entry->name);
FlushAll();
RestoreRoundingMode();
gpr.SetRegImm(SCRATCH1, js.compilerPC);
MovToPC(SCRATCH1);
// Standard function call, nothing fancy.
// The function returns the number of cycles it took in EAX.
QuickCallFunction(SCRATCH1_64, (const void *)(entry->replaceFunc));
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
// Compile the original instruction at this address. We ignore cycles for hooks.
ApplyRoundingMode();
MIPSCompileOp(Memory::Read_Instruction(js.compilerPC, true));
} else {
ApplyRoundingMode();
LDR(INDEX_UNSIGNED, W1, CTXREG, MIPS_REG_RA * 4);
WriteDownCountR(W0);
WriteExitDestInR(W1);
js.compiling = false;
}
} else {
ERROR_LOG(HLE, "Replacement function %s has neither jit nor regular impl", entry->name);
}
}
void Arm64Jit::Comp_Generic(MIPSOpcode op)

View file

@ -35,6 +35,14 @@ bool TestArm64Emitter() {
u32 code[512];
ARM64XEmitter emitter((u8 *)code);
ARM64FloatEmitter fp(&emitter);
//fp.FMOV(32, false, S1, X3);
//RET(CheckLast(emitter, "aa023be1 fmov s1, w3"));
//fp.FMOV(32, false, X1, S3);
//RET(CheckLast(emitter, "aa023be1 fmov x1, s3"));
emitter.MOV(X1, X2, ArithOption(X1, ST_LSL, 14));
RET(CheckLast(emitter, "aa023be1 mov x1, x2, lsl #14"));
emitter.LSLV(X1, X2, X30);
RET(CheckLast(emitter, "9ade2041 lslv x1, x2, x30"));
emitter.CMP(W2, W30);
RET(CheckLast(emitter, "6b1e005f cmp w2, w30"));
emitter.ADD(X1, X2, X30);