mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
ARM64: Some more instructions, func replacements
This commit is contained in:
parent
da2f4147da
commit
4233921ab7
8 changed files with 203 additions and 21 deletions
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue