// Copyright (c) 2014- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "base/logging.h" #include #include #include #include #include #include #include "MemoryUtil.h" #include "MipsEmitter.h" #include "CPUDetect.h" namespace MIPSGen { void MIPSXEmitter::SetCodePtr(u8 *ptr) { code_ = ptr; lastCacheFlushEnd_ = ptr; } void MIPSXEmitter::ReserveCodeSpace(u32 bytes) { for (u32 i = 0; i < bytes / 4; ++i) { BREAK(0); } } const u8 *MIPSXEmitter::AlignCode16() { ReserveCodeSpace((-(intptr_t)code_) & 15); return code_; } const u8 *MIPSXEmitter::AlignCodePage() { // TODO: Assuming code pages ought to be 4K? ReserveCodeSpace((-(intptr_t)code_) & 4095); return code_; } const u8 *MIPSXEmitter::GetCodePtr() const { return code_; } u8 *MIPSXEmitter::GetWritableCodePtr() { return code_; } void MIPSXEmitter::FlushIcache() { FlushIcacheSection(lastCacheFlushEnd_, code_); lastCacheFlushEnd_ = code_; } void MIPSXEmitter::FlushIcacheSection(u8 *start, u8 *end) { #if defined(MIPS) #ifdef __clang__ __clear_cache(start, end); #else __builtin___clear_cache(start, end); #endif #endif } void MIPSXEmitter::BREAK(u32 code) { // 000000 iiiiiiiiiiiiiiiiiiii 001101 _dbg_assert_msg_(JIT, code <= 0xfffff, "Bad emitter arguments"); Write32Fields(26, 0x00, 6, code & 0xfffff, 0, 0x0d); } FixupBranch MIPSXEmitter::J() { // 000010 iiiiiiiiiiiiiiiiiiiiiiiii (fix up) FixupBranch b = MakeFixupBranch(BRANCH_26); Write32Fields(26, 0x02); return b; } void MIPSXEmitter::J(const void *func) { SetJumpTarget(J(), func); } FixupBranch MIPSXEmitter::JAL() { // 000011 iiiiiiiiiiiiiiiiiiiiiiiii (fix up) FixupBranch b = MakeFixupBranch(BRANCH_26); Write32Fields(26, 0x03); return b; } void MIPSXEmitter::JAL(const void *func) { SetJumpTarget(JAL(), func); } FixupBranch MIPSXEmitter::BEQ(MIPSReg rs, MIPSReg rt) { // 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); return b; } void MIPSXEmitter::BEQ(MIPSReg rs, MIPSReg rt, const void *func) { SetJumpTarget(BEQ(rs, rt), func); } FixupBranch MIPSXEmitter::BNE(MIPSReg rs, MIPSReg rt) { // 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); return b; } void MIPSXEmitter::BNE(MIPSReg rs, MIPSReg rt, const void *func) { SetJumpTarget(BNE(rs, rt), func); } FixupBranch MIPSXEmitter::BLEZ(MIPSReg rs) { // 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); return b; } void MIPSXEmitter::BLEZ(MIPSReg rs, const void *func) { SetJumpTarget(BLEZ(rs), func); } FixupBranch MIPSXEmitter::BGTZ(MIPSReg rs) { // 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); return b; } void MIPSXEmitter::BGTZ(MIPSReg rs, const void *func) { SetJumpTarget(BGTZ(rs), func); } void MIPSXEmitter::SetJumpTarget(const FixupBranch &branch) { SetJumpTarget(branch, code_); } bool MIPSXEmitter::BInRange(const void *func) { return BInRange(code_, func); } bool MIPSXEmitter::JInRange(const void *func) { return JInRange(code_, func); } void MIPSXEmitter::SetJumpTarget(const FixupBranch &branch, const void *dst) { const intptr_t srcp = (intptr_t)branch.ptr; const intptr_t dstp = (intptr_t)dst; u32 *fixup = (u32 *)branch.ptr; _dbg_assert_msg_(JIT, (dstp & 3) == 0, "Destination should be aligned"); if (branch.type == BRANCH_16) { // The distance is encoded as words from the delay slot. ptrdiff_t distance = (dstp - srcp - 4) >> 2; _dbg_assert_msg_(JIT, BInRange(branch.ptr, dst), "Destination is too far away (%p -> %p)", branch.ptr, dst); *fixup = (*fixup & 0xffff0000) | (distance & 0x0000ffff); } else { // Absolute, easy. _dbg_assert_msg_(JIT, JInRange(branch.ptr, dst), "Destination is too far away (%p -> %p)", branch.ptr, dst); *fixup = (*fixup & 0xfc000000) | ((dstp >> 2) & 0x03ffffff); } } bool MIPSXEmitter::BInRange(const void *src, const void *dst) { const intptr_t srcp = (intptr_t)src; const intptr_t dstp = (intptr_t)dst; // The distance is encoded as words from the delay slot. ptrdiff_t distance = (dstp - srcp - 4) >> 2; return distance >= -0x8000 && distance < 0x8000; } bool MIPSXEmitter::JInRange(const void *src, const void *dst) { const intptr_t srcp = (intptr_t)src; const intptr_t dstp = (intptr_t)dst; return (srcp - (srcp & 0x0fffffff)) == (dstp - (dstp & 0x0fffffff)); } void MIPSXEmitter::QuickCallFunction(MIPSReg scratchreg, const void *func) { _dbg_assert_msg_(JIT, scratchreg < F_BASE, "Bad emitter arguments"); if (BInRange(func)) { B(func); } else if (JInRange(func)) { J(func); } else { // This may never happen. MOVP2R(reg, func); JR(reg); } } FixupBranch MIPSXEmitter::MakeFixupBranch(FixupBranchType type) { FixupBranch b; b.ptr = code_; b.type = type; return b; } void MIPSXEmitter::LB(MIPSReg value, MIPSReg base, s16 offset) { // 100000 sssss ttttt iiiiiiiiiiiiiiii - rs = base, rt = value _dbg_assert_msg_(JIT, value < F_BASE && base < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x20, 21, base, 16, value, 0, (u16)offset); } void MIPSXEmitter::LW(MIPSReg value, MIPSReg base, s16 offset) { // 100011 sssss ttttt iiiiiiiiiiiiiiii - rs = base, rt = value _dbg_assert_msg_(JIT, value < F_BASE && base < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x23, 21, base, 16, value, 0, (u16)offset); } void MIPSXEmitter::SB(MIPSReg value, MIPSReg base, s16 offset) { // 101000 sssss ttttt iiiiiiiiiiiiiiii - rs = base, rt = value _dbg_assert_msg_(JIT, value < F_BASE && base < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x28, 21, base, 16, value, 0, (u16)offset); } void MIPSXEmitter::SW(MIPSReg value, MIPSReg base, s16 offset) { // 101011 sssss ttttt iiiiiiiiiiiiiiii - rs = base, rt = value _dbg_assert_msg_(JIT, value < F_BASE && base < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x2b, 21, base, 16, value, 0, (u16)offset); } void MIPSXEmitter::SLL(MIPSReg rd, MIPSReg rt, u8 sa) { // 000000 xxxxx ttttt ddddd aaaaa 000000 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && sa <= 0x1f, "Bad emitter arguments"); Write32Fields(26, 0x00, 16, rt, 11, rd, 6, sa & 0x1f, 0, 0x00); } void MIPSXEmitter::SRL(MIPSReg rd, MIPSReg rt, u8 sa) { // 000000 xxxxx ttttt ddddd aaaaa 000010 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && sa <= 0x1f, "Bad emitter arguments"); Write32Fields(26, 0x00, 16, rt, 11, rd, 6, sa & 0x1f, 0, 0x02); } void MIPSXEmitter::SRA(MIPSReg rd, MIPSReg rt, u8 sa) { // 000000 xxxxx ttttt ddddd aaaaa 000011 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && sa <= 0x1f, "Bad emitter arguments"); Write32Fields(26, 0x00, 16, rt, 11, rd, 6, sa & 0x1f, 0, 0x03); } void MIPSXEmitter::SLLV(MIPSReg rd, MIPSReg rt, MIPSReg rs) { // 000000 sssss ttttt ddddd xxxxx 000100 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x04); } void MIPSXEmitter::SRLV(MIPSReg rd, MIPSReg rt, MIPSReg rs) { // 000000 sssss ttttt ddddd xxxxx 000110 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x06); } void MIPSXEmitter::SRAV(MIPSReg rd, MIPSReg rt, MIPSReg rs) { // 000000 sssss ttttt ddddd xxxxx 000111 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x07); } void MIPSXEmitter::SLT(MIPSReg rd, MIPSReg rs, MIPSReg rt) { // 000000 sssss ttttt ddddd xxxxx 101010 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x2a); } void MIPSXEmitter::SLTU(MIPSReg rd, MIPSReg rs, MIPSReg rt) { // 000000 sssss ttttt ddddd xxxxx 101011 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x2b); } void MIPSXEmitter::SLTI(MIPSReg rt, MIPSReg rs, s16 imm) { // 001010 sssss ttttt iiiiiiiiiiiiiiii _dbg_assert_msg_(JIT, rs < F_BASE && rt < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x0a, 21, rs, 16, rt, 0, (u16)imm); } void MIPSXEmitter::SLTIU(MIPSReg rt, MIPSReg rs, s16 imm) { // 001011 sssss ttttt iiiiiiiiiiiiiiii _dbg_assert_msg_(JIT, rs < F_BASE && rt < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x0b, 21, rs, 16, rt, 0, (u16)imm); } void MIPSXEmitter::ADDU(MIPSReg rd, MIPSReg rs, MIPSReg rt) { // 000000 sssss ttttt ddddd 00000100001 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x21); } void MIPSXEmitter::SUBU(MIPSReg rd, MIPSReg rs, MIPSReg rt) { // 000000 sssss ttttt ddddd 00000100011 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x23); } void MIPSXEmitter::ADDIU(MIPSReg rt, MIPSReg rs, s16 imm) { // 001001 sssss ttttt iiiiiiiiiiiiiiii _dbg_assert_msg_(JIT, rs < F_BASE && rt < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x09, 21, rs, 16, rt, 0, (u16)imm); } void MIPSXEmitter::AND(MIPSReg rd, MIPSReg rs, MIPSReg rt) { // 000000 sssss ttttt ddddd 00000100100 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x24); } void MIPSXEmitter::OR(MIPSReg rd, MIPSReg rs, MIPSReg rt) { // 000000 sssss ttttt ddddd 00000100101 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x25); } void MIPSXEmitter::XOR(MIPSReg rd, MIPSReg rs, MIPSReg rt) { // 000000 sssss ttttt ddddd 00000100110 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && rs < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x00, 21, rs, 16, rt, 11, rd, 0, 0x26); } void MIPSXEmitter::ANDI(MIPSReg rt, MIPSReg rs, s16 imm) { // 001100 sssss ttttt iiiiiiiiiiiiiiii _dbg_assert_msg_(JIT, rs < F_BASE && rt < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x0c, 21, rs, 16, rt, 0, (u16)imm); } void MIPSXEmitter::ORI(MIPSReg rt, MIPSReg rs, s16 imm) { // 001101 sssss ttttt iiiiiiiiiiiiiiii _dbg_assert_msg_(JIT, rs < F_BASE && rt < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x0d, 21, rs, 16, rt, 0, (u16)imm); } void MIPSXEmitter::XORI(MIPSReg rt, MIPSReg rs, s16 imm) { // 001110 sssss ttttt iiiiiiiiiiiiiiii _dbg_assert_msg_(JIT, rs < F_BASE && rt < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x0e, 21, rs, 16, rt, 0, (u16)imm); } void MIPSXEmitter::LUI(MIPSReg rt, s16 imm) { // 001111 sssss ttttt iiiiiiiiiiiiiiii _dbg_assert_msg_(JIT, rt < F_BASE, "Bad emitter arguments"); Write32Fields(26, 0x0f, 21, rt, 0, (u16)imm); } void MIPSXEmitter::DSLL(MIPSReg rd, MIPSReg rt, u8 sa) { // 000000 xxxxx ttttt ddddd aaaaa 111000 DSLL // 000000 xxxxx ttttt ddddd aaaaa 111100 DSLL32 _dbg_assert_msg_(JIT, rd < F_BASE && rt < F_BASE && sa <= 0x3f, "Bad emitter arguments"); // TODO: Assert MIPS64. if (sa >= 32) { Write32Fields(26, 0x00, 16, rt, 11, rd, 6, (sa - 32) & 0x1f, 0, 0x3c); } else { Write32Fields(26, 0x00, 16, rt, 11, rd, 6, sa & 0x1f, 0, 0x38); } } void MIPSXEmitter::MOVI2R(MIPSReg reg, u64 imm) { _dbg_assert_msg_(JIT, reg < F_BASE, "Bad emitter arguments"); // TODO: Assert MIPS64. // Probably better to use a literal pool and load. LUI(reg, imm >> 48); ORI(reg, reg, (imm >> 32) & 0x0000ffff); DSLL(reg, reg, 16); ORI(reg, reg, (imm >> 16) & 0x0000ffff); DSLL(reg, reg, 16); ORI(reg, reg, (imm >> 0) & 0x0000ffff); } void MIPSXEmitter::MOVI2R(MIPSReg reg, u32 imm) { _dbg_assert_msg_(JIT, reg < F_BASE, "Bad emitter arguments"); if ((imm & 0xffff0000) != 0) { #if 0 // TODO: CPUDetect MIPS64. Ideally allow emitter to emit MIPS32 on x64. ORI(reg, R_ZERO, imm >> 16); DSLL(reg, reg, 16); ORI(reg, reg, imm & 0x0000ffff); #else LUI(reg, imm >> 16); ORI(reg, reg, imm & 0x0000ffff); #endif } else { ORI(reg, R_ZERO, imm & 0x0000ffff); } } void MIPSXCodeBlock::AllocCodeSpace(int size) { region_size = size; region = (u8 *)AllocateExecutableMemory(region_size); SetCodePtr(region); } // Always clear code space with breakpoints, so that if someone accidentally executes // uninitialized, it just breaks into the debugger. void MIPSXCodeBlock::ClearCodeSpace() { // Set BREAK instructions on all of it. u32 *region32 = (u32 *)region; for (u32 i = 0; i < region_size / 4; ++i) { *region32++ = 0x0000000d; } ResetCodePtr(); } void MIPSXCodeBlock::FreeCodeSpace() { FreeMemoryPages(region, region_size); region = NULL; region_size = 0; } void MIPSXCodeBlock::WriteProtect() { WriteProtectMemory(region, region_size, true); } void MIPSXCodeBlock::UnWriteProtect() { UnWriteProtectMemory(region, region_size, false); } }