Merge pull request #7101 from unknownbrackets/mips-emitter

Add delay slot handling to the mips emitter
This commit is contained in:
Henrik Rydgård 2014-11-20 09:45:41 +01:00
commit fd27339ad7
2 changed files with 130 additions and 53 deletions

View File

@ -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

View File

@ -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