mirror of
https://github.com/libretro/ppsspp.git
synced 2025-01-24 18:15:49 +00:00
Merge pull request #7101 from unknownbrackets/mips-emitter
Add delay slot handling to the mips emitter
This commit is contained in:
commit
fd27339ad7
@ -15,6 +15,11 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
// Symbian can't build this due to an old gcc/lib combination, and doesn't need to.
|
||||
// Kind programmer, if you want to translate this to a proper feature-detection
|
||||
// define, please feel free to.
|
||||
#ifndef __SYMBIAN32__
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
#include <assert.h>
|
||||
@ -80,86 +85,107 @@ void MIPSEmitter::BREAK(u32 code) {
|
||||
Write32Fields(26, 0x00, 6, code & 0xfffff, 0, 0x0d);
|
||||
}
|
||||
|
||||
FixupBranch MIPSEmitter::J() {
|
||||
FixupBranch MIPSEmitter::J(std::function<void ()> delaySlot) {
|
||||
// 000010 iiiiiiiiiiiiiiiiiiiiiiiiii (fix up)
|
||||
FixupBranch b = MakeFixupBranch(BRANCH_26);
|
||||
Write32Fields(26, 0x02);
|
||||
ApplyDelaySlot(delaySlot);
|
||||
return b;
|
||||
}
|
||||
|
||||
void MIPSEmitter::J(const void *func) {
|
||||
SetJumpTarget(J(), func);
|
||||
void MIPSEmitter::J(const void *func, std::function<void ()> delaySlot) {
|
||||
SetJumpTarget(J(delaySlot), func);
|
||||
}
|
||||
|
||||
FixupBranch MIPSEmitter::JAL() {
|
||||
FixupBranch MIPSEmitter::JAL(std::function<void ()> delaySlot) {
|
||||
// 000011 iiiiiiiiiiiiiiiiiiiiiiiiii (fix up)
|
||||
FixupBranch b = MakeFixupBranch(BRANCH_26);
|
||||
Write32Fields(26, 0x03);
|
||||
ApplyDelaySlot(delaySlot);
|
||||
return b;
|
||||
}
|
||||
|
||||
void MIPSEmitter::JAL(const void *func) {
|
||||
SetJumpTarget(JAL(), func);
|
||||
void MIPSEmitter::JAL(const void *func, std::function<void ()> delaySlot) {
|
||||
SetJumpTarget(JAL(delaySlot), func);
|
||||
}
|
||||
|
||||
void MIPSEmitter::JR(MIPSReg rs) {
|
||||
void MIPSEmitter::JR(MIPSReg rs, std::function<void ()> delaySlot) {
|
||||
// 000000 sssss xxxxxxxxxx hint- 001000 (hint must be 0.)
|
||||
_dbg_assert_msg_(JIT, rs < F_BASE, "Bad emitter arguments");
|
||||
Write32Fields(26, 0x00, 21, rs, 0, 0x08);
|
||||
ApplyDelaySlot(delaySlot);
|
||||
}
|
||||
|
||||
void MIPSEmitter::JALR(MIPSReg rd, MIPSReg rs) {
|
||||
void MIPSEmitter::JALR(MIPSReg rd, MIPSReg rs, std::function<void ()> delaySlot) {
|
||||
// 000000 sssss xxxxx ddddd hint- 001001 (hint must be 0.)
|
||||
_dbg_assert_msg_(JIT, rs < F_BASE, "Bad emitter arguments");
|
||||
Write32Fields(26, 0x00, 21, rs, 11, rd, 0, 0x09);
|
||||
ApplyDelaySlot(delaySlot);
|
||||
}
|
||||
|
||||
FixupBranch MIPSEmitter::BEQ(MIPSReg rs, MIPSReg rt) {
|
||||
FixupBranch MIPSEmitter::BLTZ(MIPSReg rs, std::function<void ()> delaySlot) {
|
||||
// 000001 sssss xxxxx iiiiiiiiiiiiiii (fix up)
|
||||
_dbg_assert_msg_(JIT, rs < F_BASE, "Bad emitter arguments");
|
||||
FixupBranch b = MakeFixupBranch(BRANCH_16);
|
||||
Write32Fields(26, 0x01, 21, rs);
|
||||
ApplyDelaySlot(delaySlot);
|
||||
return b;
|
||||
}
|
||||
|
||||
void MIPSEmitter::BLTZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot) {
|
||||
SetJumpTarget(BLTZ(rs, delaySlot), func);
|
||||
}
|
||||
|
||||
FixupBranch MIPSEmitter::BEQ(MIPSReg rs, MIPSReg rt, std::function<void ()> delaySlot) {
|
||||
// 000100 sssss ttttt iiiiiiiiiiiiiii (fix up)
|
||||
_dbg_assert_msg_(JIT, rs < F_BASE && rt < F_BASE, "Bad emitter arguments");
|
||||
FixupBranch b = MakeFixupBranch(BRANCH_16);
|
||||
Write32Fields(26, 0x04, 21, rs, 16, rt);
|
||||
ApplyDelaySlot(delaySlot);
|
||||
return b;
|
||||
}
|
||||
|
||||
void MIPSEmitter::BEQ(MIPSReg rs, MIPSReg rt, const void *func) {
|
||||
SetJumpTarget(BEQ(rs, rt), func);
|
||||
void MIPSEmitter::BEQ(MIPSReg rs, MIPSReg rt, const void *func, std::function<void ()> delaySlot) {
|
||||
SetJumpTarget(BEQ(rs, rt, delaySlot), func);
|
||||
}
|
||||
|
||||
FixupBranch MIPSEmitter::BNE(MIPSReg rs, MIPSReg rt) {
|
||||
FixupBranch MIPSEmitter::BNE(MIPSReg rs, MIPSReg rt, std::function<void ()> delaySlot) {
|
||||
// 000101 sssss ttttt iiiiiiiiiiiiiii (fix up)
|
||||
_dbg_assert_msg_(JIT, rs < F_BASE && rt < F_BASE, "Bad emitter arguments");
|
||||
FixupBranch b = MakeFixupBranch(BRANCH_16);
|
||||
Write32Fields(26, 0x05, 21, rs, 16, rt);
|
||||
ApplyDelaySlot(delaySlot);
|
||||
return b;
|
||||
}
|
||||
|
||||
void MIPSEmitter::BNE(MIPSReg rs, MIPSReg rt, const void *func) {
|
||||
SetJumpTarget(BNE(rs, rt), func);
|
||||
void MIPSEmitter::BNE(MIPSReg rs, MIPSReg rt, const void *func, std::function<void ()> delaySlot) {
|
||||
SetJumpTarget(BNE(rs, rt, delaySlot), func);
|
||||
}
|
||||
|
||||
FixupBranch MIPSEmitter::BLEZ(MIPSReg rs) {
|
||||
FixupBranch MIPSEmitter::BLEZ(MIPSReg rs, std::function<void ()> delaySlot) {
|
||||
// 000110 sssss xxxxx iiiiiiiiiiiiiii (fix up)
|
||||
_dbg_assert_msg_(JIT, rs < F_BASE, "Bad emitter arguments");
|
||||
FixupBranch b = MakeFixupBranch(BRANCH_16);
|
||||
Write32Fields(26, 0x06, 21, rs);
|
||||
ApplyDelaySlot(delaySlot);
|
||||
return b;
|
||||
}
|
||||
|
||||
void MIPSEmitter::BLEZ(MIPSReg rs, const void *func) {
|
||||
SetJumpTarget(BLEZ(rs), func);
|
||||
void MIPSEmitter::BLEZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot) {
|
||||
SetJumpTarget(BLEZ(rs, delaySlot), func);
|
||||
}
|
||||
|
||||
FixupBranch MIPSEmitter::BGTZ(MIPSReg rs) {
|
||||
FixupBranch MIPSEmitter::BGTZ(MIPSReg rs, std::function<void ()> delaySlot) {
|
||||
// 000111 sssss xxxxx iiiiiiiiiiiiiii (fix up)
|
||||
_dbg_assert_msg_(JIT, rs < F_BASE, "Bad emitter arguments");
|
||||
FixupBranch b = MakeFixupBranch(BRANCH_16);
|
||||
Write32Fields(26, 0x07, 21, rs);
|
||||
ApplyDelaySlot(delaySlot);
|
||||
return b;
|
||||
}
|
||||
|
||||
void MIPSEmitter::BGTZ(MIPSReg rs, const void *func) {
|
||||
SetJumpTarget(BGTZ(rs), func);
|
||||
void MIPSEmitter::BGTZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot) {
|
||||
SetJumpTarget(BGTZ(rs, delaySlot), func);
|
||||
}
|
||||
|
||||
void MIPSEmitter::SetJumpTarget(const FixupBranch &branch) {
|
||||
@ -209,6 +235,15 @@ bool MIPSEmitter::JInRange(const void *src, const void *dst) {
|
||||
return (srcp - (srcp & 0x0fffffff)) == (dstp - (dstp & 0x0fffffff));
|
||||
}
|
||||
|
||||
void MIPSEmitter::ApplyDelaySlot(std::function<void ()> delaySlot) {
|
||||
if (delaySlot) {
|
||||
delaySlot();
|
||||
} else {
|
||||
// We just insert a NOP if there's no delay slot provided. Safer.
|
||||
NOP();
|
||||
}
|
||||
}
|
||||
|
||||
void MIPSEmitter::QuickCallFunction(MIPSReg scratchreg, const void *func) {
|
||||
_dbg_assert_msg_(JIT, scratchreg < F_BASE, "Bad emitter arguments");
|
||||
if (JInRange(func)) {
|
||||
@ -366,9 +401,21 @@ void MIPSEmitter::XORI(MIPSReg rt, MIPSReg rs, s16 imm) {
|
||||
}
|
||||
|
||||
void MIPSEmitter::LUI(MIPSReg rt, s16 imm) {
|
||||
// 001111 sssss ttttt iiiiiiiiiiiiiiii
|
||||
// 001111 00000 ttttt iiiiiiiiiiiiiiii
|
||||
_dbg_assert_msg_(JIT, rt < F_BASE, "Bad emitter arguments");
|
||||
Write32Fields(26, 0x0f, 21, rt, 0, (u16)imm);
|
||||
Write32Fields(26, 0x0f, 16, rt, 0, (u16)imm);
|
||||
}
|
||||
|
||||
void MIPSEmitter::INS(MIPSReg rt, MIPSReg rs, s8 pos, s8 size) {
|
||||
// 011111 sssss ttttt xxxxx yyyyy 000100
|
||||
_dbg_assert_msg_(JIT, rt < F_BASE && rs < F_BASE && pos <= 0x1f && (size+pos+1) <= 0x1f, "Bad emitter arguments");
|
||||
Write32Fields(26, 0x1f, 21, rt, 16, rs, 11, (size+pos+1) & 0x1f, 6, pos & 0x1f, 0, 0x04);
|
||||
}
|
||||
|
||||
void MIPSEmitter::EXT(MIPSReg rt, MIPSReg rs, s8 pos, s8 size) {
|
||||
// 111111 sssss ttttt xxxxx yyyyy 000000
|
||||
_dbg_assert_msg_(JIT, rt < F_BASE && rs < F_BASE && pos <= 0x1f && size >= 1, "Bad emitter arguments");
|
||||
Write32Fields(26, 0x3f, 21, rt, 16, rs, 11, (size-1) & 0x1f, 6, pos & 0x1f, 0, 0x00);
|
||||
}
|
||||
|
||||
void MIPSEmitter::DSLL(MIPSReg rd, MIPSReg rt, u8 sa) {
|
||||
@ -445,4 +492,6 @@ void MIPSCodeBlock::UnWriteProtect() {
|
||||
UnWriteProtectMemory(region, region_size, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -18,7 +18,12 @@
|
||||
// WARNING - THIS LIBRARY IS NOT THREAD SAFE!!! (cargo culted but probably true)
|
||||
|
||||
#pragma once
|
||||
// Symbian can't build this due to an old gcc/lib combination, and doesn't need to.
|
||||
// Kind programmer, if you want to translate this to a proper feature-detection
|
||||
// define, please feel free to.
|
||||
#ifndef __SYMBIAN32__
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
@ -95,45 +100,62 @@ public:
|
||||
SLL(R_ZERO, R_ZERO, 0);
|
||||
}
|
||||
|
||||
FixupBranch J();
|
||||
void J(const void *func);
|
||||
FixupBranch JAL();
|
||||
void JAL(const void *func);
|
||||
void JR(MIPSReg rs);
|
||||
void JRRA() {
|
||||
JR(R_RA);
|
||||
// Note for all branches and jumps:
|
||||
// MIPS has DELAY SLOTS. This emitter makes it so if you forget that, you'll be safe.
|
||||
// If you want to run something inside a delay slot, emit the instruction inside a closure.
|
||||
//
|
||||
// Example: Translates to:
|
||||
// J(&myFunc); J(&myFunc);
|
||||
// ADDU(V0, V0, V1); NOP();
|
||||
// ADDU(V0, V0, V1);
|
||||
//
|
||||
// J(&myFunc, [&] { J(&myFunc);
|
||||
// ADDU(V0, V0, V1); ADDU(V0, V0, V1);
|
||||
// });
|
||||
//
|
||||
// This applies to all J*() and B*() functions (except BREAK(), which is not a branch func.)
|
||||
|
||||
FixupBranch J(std::function<void ()> delaySlot = nullptr);
|
||||
void J(const void *func, std::function<void ()> delaySlot = nullptr);
|
||||
FixupBranch JAL(std::function<void ()> delaySlot = nullptr);
|
||||
void JAL(const void *func, std::function<void ()> delaySlot = nullptr);
|
||||
void JR(MIPSReg rs, std::function<void ()> delaySlot = nullptr);
|
||||
void JRRA(std::function<void ()> delaySlot = nullptr) {
|
||||
JR(R_RA, delaySlot);
|
||||
}
|
||||
void JALR(MIPSReg rd, MIPSReg rs);
|
||||
void JALR(MIPSReg rs) {
|
||||
JALR(R_RA, rs);
|
||||
void JALR(MIPSReg rd, MIPSReg rs, std::function<void ()> delaySlot = nullptr);
|
||||
void JALR(MIPSReg rs, std::function<void ()> delaySlot = nullptr) {
|
||||
JALR(R_RA, rs, delaySlot);
|
||||
}
|
||||
|
||||
inline FixupBranch B() {
|
||||
return BEQ(R_ZERO, R_ZERO);
|
||||
inline FixupBranch B(std::function<void ()> delaySlot = nullptr) {
|
||||
return BEQ(R_ZERO, R_ZERO, delaySlot);
|
||||
}
|
||||
inline void B(const void *func) {
|
||||
return BEQ(R_ZERO, R_ZERO, func);
|
||||
inline void B(const void *func, std::function<void ()> delaySlot = nullptr) {
|
||||
return BEQ(R_ZERO, R_ZERO, func, delaySlot);
|
||||
}
|
||||
FixupBranch BEQ(MIPSReg rs, MIPSReg rt);
|
||||
void BEQ(MIPSReg rs, MIPSReg rt, const void *func);
|
||||
FixupBranch BNE(MIPSReg rs, MIPSReg rt);
|
||||
void BNE(MIPSReg rs, MIPSReg rt, const void *func);
|
||||
inline FixupBranch BEQZ(MIPSReg rs) {
|
||||
return BEQ(rs, R_ZERO);
|
||||
FixupBranch BLTZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr);
|
||||
void BLTZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr);
|
||||
FixupBranch BEQ(MIPSReg rs, MIPSReg rt, std::function<void ()> delaySlot = nullptr);
|
||||
void BEQ(MIPSReg rs, MIPSReg rt, const void *func, std::function<void ()> delaySlot = nullptr);
|
||||
FixupBranch BNE(MIPSReg rs, MIPSReg rt, std::function<void ()> delaySlot = nullptr);
|
||||
void BNE(MIPSReg rs, MIPSReg rt, const void *func, std::function<void ()> delaySlot = nullptr);
|
||||
inline FixupBranch BEQZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr) {
|
||||
return BEQ(rs, R_ZERO, delaySlot);
|
||||
}
|
||||
inline void BEQZ(MIPSReg rs, const void *func) {
|
||||
return BEQ(rs, R_ZERO, func);
|
||||
inline void BEQZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr) {
|
||||
return BEQ(rs, R_ZERO, func, delaySlot);
|
||||
}
|
||||
inline FixupBranch BNEZ(MIPSReg rs) {
|
||||
return BNE(rs, R_ZERO);
|
||||
inline FixupBranch BNEZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr) {
|
||||
return BNE(rs, R_ZERO, delaySlot);
|
||||
}
|
||||
inline void BNEZ(MIPSReg rs, const void *func) {
|
||||
return BNE(rs, R_ZERO, func);
|
||||
inline void BNEZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr) {
|
||||
return BNE(rs, R_ZERO, func, delaySlot);
|
||||
}
|
||||
FixupBranch BLEZ(MIPSReg rs);
|
||||
void BLEZ(MIPSReg rs, const void *func);
|
||||
FixupBranch BGTZ(MIPSReg rs);
|
||||
void BGTZ(MIPSReg rs, const void *func);
|
||||
FixupBranch BLEZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr);
|
||||
void BLEZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr);
|
||||
FixupBranch BGTZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr);
|
||||
void BGTZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr);
|
||||
|
||||
void SetJumpTarget(const FixupBranch &branch);
|
||||
bool BInRange(const void *func);
|
||||
@ -181,6 +203,9 @@ public:
|
||||
// Clears the lower bits. On MIPS64, the result is sign extended.
|
||||
void LUI(MIPSReg rt, s16 imm);
|
||||
|
||||
void INS(MIPSReg rt, MIPSReg rs, s8 pos, s8 size);
|
||||
void EXT(MIPSReg rt, MIPSReg rs, s8 pos, s8 size);
|
||||
|
||||
// MIPS64 only. Transparently uses DSLL32 to shift 32-63 bits.
|
||||
void DSLL(MIPSReg rd, MIPSReg rt, u8 sa);
|
||||
|
||||
@ -225,6 +250,7 @@ protected:
|
||||
static bool BInRange(const void *src, const void *dst);
|
||||
static bool JInRange(const void *src, const void *dst);
|
||||
FixupBranch MakeFixupBranch(FixupBranchType type);
|
||||
void ApplyDelaySlot(std::function<void ()> delaySlot);
|
||||
|
||||
private:
|
||||
union {
|
||||
@ -288,3 +314,5 @@ protected:
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user