mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 05:19:56 +00:00
riscv: Initial untested dispatcher.
The minimum to actually, probably, running code. Pretty slow.
This commit is contained in:
parent
e271e43ec5
commit
47b81985bd
@ -1609,8 +1609,10 @@ list(APPEND CoreExtra
|
||||
)
|
||||
|
||||
list(APPEND CoreExtra
|
||||
Core/MIPS/RiscV/RiscVAsm.cpp
|
||||
Core/MIPS/RiscV/RiscVJit.cpp
|
||||
Core/MIPS/RiscV/RiscVJit.h
|
||||
Core/MIPS/RiscV/RiscVRegCache.h
|
||||
GPU/Common/VertexDecoderRiscV.cpp
|
||||
)
|
||||
|
||||
|
@ -1180,6 +1180,16 @@ bool RiscVEmitter::CJInRange(const void *src, const void *dst) const {
|
||||
return BJInRange(src, dst, 12);
|
||||
}
|
||||
|
||||
void RiscVEmitter::QuickCallFunction(RiscVReg scratchreg, const u8 *func) {
|
||||
if (!JInRange(GetCodePointer(), func)) {
|
||||
// TODO: Might be able to optimize a tiny bit by taking advantage of simm12.
|
||||
LI(scratchreg, (uintptr_t)func);
|
||||
JALR(R_RA, scratchreg, 0);
|
||||
} else {
|
||||
JAL(R_RA, func);
|
||||
}
|
||||
}
|
||||
|
||||
void RiscVEmitter::SetRegToImmediate(RiscVReg rd, uint64_t value, RiscVReg temp) {
|
||||
int64_t svalue = (int64_t)value;
|
||||
_assert_msg_(IsGPR(rd) && IsGPR(temp), "SetRegToImmediate only supports GPRs");
|
||||
|
@ -213,6 +213,13 @@ public:
|
||||
bool BInRange(const void *func) const;
|
||||
bool JInRange(const void *func) const;
|
||||
|
||||
void QuickCallFunction(RiscVReg scratchreg, const u8 *func);
|
||||
template <typename T>
|
||||
void QuickCallFunction(RiscVReg scratchreg, T *func) {
|
||||
static_assert(std::is_function<T>::value, "QuickCallFunction without function");
|
||||
QuickCallFunction(scratchreg, (const u8 *)func);
|
||||
}
|
||||
|
||||
void LUI(RiscVReg rd, s32 simm32);
|
||||
void AUIPC(RiscVReg rd, s32 simm32);
|
||||
|
||||
|
@ -593,6 +593,7 @@
|
||||
<ClCompile Include="MIPS\IR\IRPassSimplify.cpp" />
|
||||
<ClCompile Include="MIPS\IR\IRRegCache.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSVFPUFallbacks.cpp" />
|
||||
<ClCompile Include="MIPS\RiscV\RiscVAsm.cpp" />
|
||||
<ClCompile Include="MIPS\RiscV\RiscVJit.cpp" />
|
||||
<ClCompile Include="Replay.cpp" />
|
||||
<ClCompile Include="Compatibility.cpp" />
|
||||
@ -1163,6 +1164,7 @@
|
||||
<ClInclude Include="MIPS\IR\IRRegCache.h" />
|
||||
<ClInclude Include="MIPS\MIPSVFPUFallbacks.h" />
|
||||
<ClInclude Include="MIPS\RiscV\RiscVJit.h" />
|
||||
<ClInclude Include="MIPS\RiscV\RiscVRegCache.h" />
|
||||
<ClInclude Include="Replay.h" />
|
||||
<ClInclude Include="Compatibility.h" />
|
||||
<ClInclude Include="Config.h" />
|
||||
|
@ -1210,6 +1210,9 @@
|
||||
<ClCompile Include="MIPS\RiscV\RiscVJit.cpp">
|
||||
<Filter>MIPS\RiscV</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\RiscV\RiscVAsm.cpp">
|
||||
<Filter>MIPS\RiscV</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
@ -1959,6 +1962,9 @@
|
||||
<ClInclude Include="MIPS\RiscV\RiscVJit.h">
|
||||
<Filter>MIPS\RiscV</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\RiscV\RiscVRegCache.h">
|
||||
<Filter>MIPS\RiscV</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\LICENSE.TXT" />
|
||||
|
@ -213,7 +213,9 @@ private:
|
||||
|
||||
bool ReplaceJalTo(u32 dest);
|
||||
|
||||
// Clobbers SCRATCH2.
|
||||
void SaveStaticRegisters();
|
||||
// Clobbers SCRATCH2.
|
||||
void LoadStaticRegisters();
|
||||
|
||||
void WriteExit(u32 destination, int exit_num);
|
||||
|
281
Core/MIPS/RiscV/RiscVAsm.cpp
Normal file
281
Core/MIPS/RiscV/RiscVAsm.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright (c) 2023- 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 "Common/Log.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/RiscV/RiscVJit.h"
|
||||
#include "Core/MIPS/RiscV/RiscVRegCache.h"
|
||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||
#include "Core/MIPS/JitCommon/JitState.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace RiscVGen;
|
||||
using namespace RiscVJitConstants;
|
||||
|
||||
static const bool enableDebug = false;
|
||||
static const bool enableDisasm = false;
|
||||
|
||||
static void ShowPC(u32 downcount, void *membase, void *jitbase) {
|
||||
static int count = 0;
|
||||
if (currentMIPS) {
|
||||
ERROR_LOG(JIT, "ShowPC : %08x Downcount : %08x %d %p %p", currentMIPS->pc, downcount, count, membase, jitbase);
|
||||
} else {
|
||||
ERROR_LOG(JIT, "Universe corrupt?");
|
||||
}
|
||||
//if (count > 2000)
|
||||
// exit(0);
|
||||
count++;
|
||||
}
|
||||
|
||||
void RiscVJit::GenerateFixedCode(const JitOptions &jo) {
|
||||
BeginWrite(GetMemoryProtectPageSize());
|
||||
const u8 *start = AlignCodePage();
|
||||
|
||||
// TODO
|
||||
if (jo.useStaticAlloc) {
|
||||
//saveStaticRegisters = AlignCode16();
|
||||
//SW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
|
||||
//gpr.EmitSaveStaticRegisters();
|
||||
//RET();
|
||||
|
||||
//loadStaticRegisters = AlignCode16();
|
||||
//gpr.EmitLoadStaticRegisters();
|
||||
//LW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
|
||||
//RET();
|
||||
|
||||
//start = saveStaticRegisters;
|
||||
} else {
|
||||
//saveStaticRegisters = nullptr;
|
||||
//loadStaticRegisters = nullptr;
|
||||
}
|
||||
|
||||
// TODO: Do we actually need updateRoundingMode_? Hm.
|
||||
//applyRoundingMode_ = AlignCode16();
|
||||
if (false) {
|
||||
// Not sure if RISC-V has any flush to zero capability? Leaving it off for now...
|
||||
LWU(SCRATCH2, CTXREG, offsetof(MIPSState, fcr31));
|
||||
|
||||
// We can skip if the rounding mode is nearest (0) and flush is not set.
|
||||
// (as restoreRoundingMode cleared it out anyway)
|
||||
FixupBranch skip = BEQ(SCRATCH2, R_ZERO);
|
||||
|
||||
// MIPS Rounding Mode: RISC-V
|
||||
// 0: Round nearest 0
|
||||
// 1: Round to zero 1
|
||||
// 2: Round up (ceil) 3
|
||||
// 3: Round down (floor) 2
|
||||
if (cpu_info.RiscV_Zbs) {
|
||||
BEXTI(SCRATCH1, SCRATCH2, 1);
|
||||
} else {
|
||||
ANDI(SCRATCH1, SCRATCH2, 2);
|
||||
SRLI(SCRATCH1, SCRATCH1, 1);
|
||||
}
|
||||
// Swap the lowest bit by the second bit.
|
||||
XOR(SCRATCH2, SCRATCH2, SCRATCH1);
|
||||
|
||||
FSRM(SCRATCH2);
|
||||
|
||||
SetJumpTarget(skip);
|
||||
RET();
|
||||
}
|
||||
|
||||
//updateRoundingMode_ = AlignCode16();
|
||||
if (false) {
|
||||
LWU(SCRATCH2, CTXREG, offsetof(MIPSState, fcr31));
|
||||
|
||||
// Set SCRATCH2 to FZ:RM (FZ is bit 24, and RM are lowest 2 bits.)
|
||||
ANDI(SCRATCH1, SCRATCH2, 1 << 24);
|
||||
ANDI(SCRATCH2, SCRATCH2, 3);
|
||||
SRLI(SCRATCH1, SCRATCH1, 22);
|
||||
OR(SCRATCH2, SCRATCH2, SCRATCH1);
|
||||
|
||||
// Let's update js.currentRoundingFunc with the right convertS0ToSCRATCH1 func.
|
||||
//LI(SCRATCH1, convertS0ToSCRATCH1);
|
||||
if (cpu_info.RiscV_Zba) {
|
||||
SH_ADD(3, SCRATCH1, SCRATCH2, SCRATCH1);
|
||||
} else {
|
||||
SLLI(SCRATCH2, SCRATCH2, 3);
|
||||
ADD(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
}
|
||||
LD(SCRATCH2, SCRATCH1, 0);
|
||||
//LI(SCRATCH1, &js.currentRoundingFunc);
|
||||
SW(SCRATCH2, SCRATCH1, 0);
|
||||
RET();
|
||||
}
|
||||
|
||||
enterDispatcher_ = AlignCode16();
|
||||
|
||||
// Start by saving some regs on the stack. There are 12 GPs and 12 FPs we want.
|
||||
// Note: we leave R_SP as, well, SP, so it doesn't need to be saved.
|
||||
_assert_msg_(cpu_info.Mode64bit, "RiscVAsm currently assumes RV64, not RV32 or RV128");
|
||||
static constexpr RiscVReg regs_to_save[]{ R_RA, X8, X9, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27 };
|
||||
// TODO: Maybe we shouldn't regalloc all of these? Is it worth it?
|
||||
static constexpr RiscVReg regs_to_save_fp[]{ F8, F9, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27 };
|
||||
int saveSize = 8 * (int)(ARRAY_SIZE(regs_to_save) + ARRAY_SIZE(regs_to_save_fp));
|
||||
if (saveSize & 0xF)
|
||||
saveSize += 8;
|
||||
_assert_msg_((saveSize & 0xF) == 0, "Stack must be kept aligned");
|
||||
int saveOffset = 0;
|
||||
ADDI(R_SP, R_SP, -saveSize);
|
||||
for (RiscVReg r : regs_to_save) {
|
||||
SD(r, R_SP, saveOffset);
|
||||
saveOffset += 8;
|
||||
}
|
||||
for (RiscVReg r : regs_to_save_fp) {
|
||||
FS(64, r, R_SP, saveOffset);
|
||||
saveOffset += 8;
|
||||
}
|
||||
_assert_(saveOffset <= saveSize);
|
||||
|
||||
// Fixed registers, these are always kept when in Jit context.
|
||||
LI(MEMBASEREG, Memory::base, SCRATCH1);
|
||||
LI(CTXREG, mips_, SCRATCH1);
|
||||
LI(JITBASEREG, blockStartAddrs_, SCRATCH1);
|
||||
|
||||
LoadStaticRegisters();
|
||||
MovFromPC(SCRATCH1);
|
||||
outerLoopPCInSCRATCH1_ = GetCodePtr();
|
||||
MovToPC(SCRATCH1);
|
||||
outerLoop_ = GetCodePtr();
|
||||
// Advance can change the downcount (or thread), so must save/restore around it.
|
||||
SaveStaticRegisters();
|
||||
RestoreRoundingMode(true);
|
||||
QuickCallFunction(SCRATCH1, &CoreTiming::Advance);
|
||||
ApplyRoundingMode(true);
|
||||
LoadStaticRegisters();
|
||||
|
||||
dispatcherCheckCoreState_ = GetCodePtr();
|
||||
LI(SCRATCH1, &coreState, SCRATCH2);
|
||||
LW(SCRATCH1, SCRATCH1, 0);
|
||||
FixupBranch badCoreState = BNE(SCRATCH1, R_ZERO);
|
||||
|
||||
// We just checked coreState, so go to advance if downcount is negative.
|
||||
BLT(DOWNCOUNTREG, R_ZERO, outerLoop_);
|
||||
FixupBranch skipToRealDispatch = J();
|
||||
|
||||
dispatcherPCInSCRATCH1_ = GetCodePtr();
|
||||
MovToPC(SCRATCH1);
|
||||
|
||||
dispatcher_ = GetCodePtr();
|
||||
FixupBranch bail = BLT(DOWNCOUNTREG, R_ZERO);
|
||||
SetJumpTarget(skipToRealDispatch);
|
||||
|
||||
dispatcherNoCheck_ = GetCodePtr();
|
||||
|
||||
// Debug
|
||||
if (enableDebug) {
|
||||
MV(X10, DOWNCOUNTREG);
|
||||
MV(X11, MEMBASEREG);
|
||||
MV(X12, JITBASEREG);
|
||||
QuickCallFunction(X13, &ShowPC);
|
||||
}
|
||||
|
||||
LWU(SCRATCH1, CTXREG, offsetof(MIPSState, pc));
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
LI(SCRATCH2, 0x3FFFFFFF);
|
||||
AND(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
#endif
|
||||
ADD(SCRATCH1, SCRATCH1, MEMBASEREG);
|
||||
dispatcherFetch_ = GetCodePtr();
|
||||
LWU(SCRATCH1, SCRATCH1, 0);
|
||||
SRLI(SCRATCH2, SCRATCH1, 24);
|
||||
// We're in other words comparing to the top 8 bits of MIPS_EMUHACK_OPCODE by subtracting.
|
||||
ADDI(SCRATCH2, SCRATCH2, -(MIPS_EMUHACK_OPCODE >> 24));
|
||||
FixupBranch skipJump = BNE(SCRATCH2, R_ZERO);
|
||||
// Use a wall to mask by 0x00FFFFFF and extract the block number.
|
||||
SLLI(SCRATCH1, SCRATCH1, XLEN - 24);
|
||||
// But actually, we want * 8, so skip shifting back just a bit.
|
||||
_assert_msg_(sizeof(blockStartAddrs_[0]) == 8, "RiscVAsm currently assumes pointers are 64-bit");
|
||||
SRLI(SCRATCH1, SCRATCH1, XLEN - 24 - 3);
|
||||
ADD(SCRATCH1, JITBASEREG, SCRATCH1);
|
||||
// TODO: Consider replacing the block nums after all, just trying to use IR block cache.
|
||||
LD(SCRATCH1, SCRATCH1, 0);
|
||||
JR(SCRATCH1);
|
||||
SetJumpTarget(skipJump);
|
||||
|
||||
// No block found, let's jit. Might be able to optimize reg/frm saving later.
|
||||
SaveStaticRegisters();
|
||||
RestoreRoundingMode(true);
|
||||
QuickCallFunction(SCRATCH1, &MIPSComp::JitAt);
|
||||
ApplyRoundingMode(true);
|
||||
LoadStaticRegisters();
|
||||
|
||||
// Try again, the block index should be set now.
|
||||
J(dispatcherNoCheck_);
|
||||
|
||||
SetJumpTarget(bail);
|
||||
|
||||
LI(SCRATCH1, &coreState, SCRATCH2);
|
||||
LW(SCRATCH1, SCRATCH1, 0);
|
||||
BEQ(SCRATCH1, R_ZERO, outerLoop_);
|
||||
|
||||
const uint8_t *quitLoop = GetCodePtr();
|
||||
SetJumpTarget(badCoreState);
|
||||
|
||||
SaveStaticRegisters();
|
||||
RestoreRoundingMode(true);
|
||||
|
||||
_assert_msg_(cpu_info.Mode64bit, "RiscVAsm currently assumes RV64, not RV32 or RV128");
|
||||
saveOffset = 0;
|
||||
for (RiscVReg r : regs_to_save) {
|
||||
LD(r, R_SP, saveOffset);
|
||||
saveOffset += 8;
|
||||
}
|
||||
for (RiscVReg r : regs_to_save_fp) {
|
||||
FL(64, r, R_SP, saveOffset);
|
||||
saveOffset += 8;
|
||||
}
|
||||
ADDI(R_SP, R_SP, saveSize);
|
||||
|
||||
RET();
|
||||
|
||||
// TODO
|
||||
crashHandler_ = GetCodePtr();
|
||||
LI(SCRATCH1, &coreState, SCRATCH2);
|
||||
LI(SCRATCH2, CORE_RUNTIME_ERROR);
|
||||
SW(SCRATCH2, SCRATCH1, 0);
|
||||
J(quitLoop);
|
||||
|
||||
// TODO: Do we need this?
|
||||
static const Round roundModes[8] = { Round::NEAREST_EVEN, Round::TOZERO, Round::UP, Round::DOWN, Round::NEAREST_EVEN, Round::TOZERO, Round::UP, Round::DOWN };
|
||||
for (size_t i = 0; i < ARRAY_SIZE(roundModes); ++i) {
|
||||
//convertS0ToSCRATCH1[i] = AlignCode16();
|
||||
|
||||
//FCVT(FConv::W, FConv::S, SCRATCH1, F0, roundModes[i]);
|
||||
//RET();
|
||||
}
|
||||
|
||||
// Leave this at the end, add more stuff above.
|
||||
if (enableDisasm) {
|
||||
std::vector<std::string> lines = DisassembleRV64(start, GetCodePtr() - start);
|
||||
for (auto s : lines) {
|
||||
INFO_LOG(JIT, "%s", s.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Let's spare the pre-generated code from unprotect-reprotect.
|
||||
AlignCodePage();
|
||||
jitStartOffset_ = (int)(GetCodePtr() - start);
|
||||
// Don't forget to zap the instruction cache! This must stay at the end of this function.
|
||||
FlushIcache();
|
||||
EndWrite();
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
@ -15,15 +15,37 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/RiscV/RiscVJit.h"
|
||||
#include "Core/MIPS/RiscV/RiscVRegCache.h"
|
||||
#include "Common/Profiler/Profiler.h"
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace RiscVGen;
|
||||
using namespace RiscVJitConstants;
|
||||
|
||||
static constexpr int MAX_ALLOWED_JIT_BLOCKS = 262144;
|
||||
|
||||
RiscVJit::RiscVJit(MIPSState *mipsState) : IRJit(mipsState) {
|
||||
// Automatically disable incompatible options.
|
||||
if (((intptr_t)Memory::base & 0x00000000FFFFFFFFUL) != 0) {
|
||||
jo.enablePointerify = false;
|
||||
}
|
||||
|
||||
AllocCodeSpace(1024 * 1024 * 16);
|
||||
|
||||
// TODO: gpr, fpr, GenerateFixedCode(jo);
|
||||
// TODO: Consider replacing block num method form IRJit - this is 2MB.
|
||||
blockStartAddrs_ = new const u8 *[MAX_ALLOWED_JIT_BLOCKS];
|
||||
|
||||
// TODO: gpr, fpr
|
||||
|
||||
GenerateFixedCode(jo);
|
||||
}
|
||||
|
||||
RiscVJit::~RiscVJit() {
|
||||
delete [] blockStartAddrs_;
|
||||
}
|
||||
|
||||
void RiscVJit::RunLoopUntil(u64 globalticks) {
|
||||
@ -32,16 +54,117 @@ void RiscVJit::RunLoopUntil(u64 globalticks) {
|
||||
}
|
||||
|
||||
bool RiscVJit::CompileBlock(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes, bool preload) {
|
||||
// Check that we're not full (we allow less blocks than IR itself.)
|
||||
if (blocks_.GetNumBlocks() >= MAX_ALLOWED_JIT_BLOCKS - 1)
|
||||
return false;
|
||||
|
||||
if (!IRJit::CompileBlock(em_address, instructions, mipsBytes, preload))
|
||||
return false;
|
||||
|
||||
// TODO: Compile native.
|
||||
// TODO: Block linking, checked entries and such.
|
||||
|
||||
int block_num = blocks_.GetBlockNumberFromStartAddress(em_address);
|
||||
_assert_msg_(blockStartAddrs_[block_num] == nullptr, "Block reused before clear");
|
||||
blockStartAddrs_[block_num] = GetCodePointer();
|
||||
|
||||
// TODO: gpr, fpr.
|
||||
for (const IRInst &inst : instructions) {
|
||||
CompileIRInst(inst);
|
||||
|
||||
// TODO
|
||||
if (jo.Disabled(JitDisable::REGALLOC_GPR)) {
|
||||
//gpr.FlushAll();
|
||||
}
|
||||
if (jo.Disabled(JitDisable::REGALLOC_FPR)) {
|
||||
//fpr.FlushAll();
|
||||
}
|
||||
|
||||
// Safety check, in case we get a bunch of really large jit ops without a lot of branching.
|
||||
if (GetSpaceLeft() < 0x800) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: a properly constructed block should never get here.
|
||||
// TODO: Need to do more than just this? Call a func to set an exception?
|
||||
LI(SCRATCH2, crashHandler_);
|
||||
JALR(R_ZERO, SCRATCH2, 0);
|
||||
|
||||
FlushIcache();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 DoIRInst(uint64_t value) {
|
||||
IRInst inst;
|
||||
memcpy(&inst, &value, sizeof(inst));
|
||||
|
||||
return IRInterpret(currentMIPS, &inst, 1);
|
||||
}
|
||||
|
||||
void RiscVJit::CompileIRInst(IRInst inst) {
|
||||
// For now, we're gonna do it the slow and ugly way.
|
||||
uint64_t value;
|
||||
memcpy(&value, &inst, sizeof(inst));
|
||||
|
||||
LI(X10, value, SCRATCH2);
|
||||
SaveStaticRegisters();
|
||||
QuickCallFunction(SCRATCH2, &DoIRInst);
|
||||
LoadStaticRegisters();
|
||||
// Result in X10 aka SCRATCH1.
|
||||
_assert_(X10 == SCRATCH1);
|
||||
if (BInRange(dispatcherPCInSCRATCH1_)) {
|
||||
BNE(X10, R_ZERO, dispatcherPCInSCRATCH1_);
|
||||
} else {
|
||||
FixupBranch skip = BEQ(X10, R_ZERO);
|
||||
LI(SCRATCH2, dispatcherPCInSCRATCH1_);
|
||||
JALR(R_ZERO, SCRATCH2, 0);
|
||||
SetJumpTarget(skip);
|
||||
}
|
||||
}
|
||||
|
||||
bool RiscVJit::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
// TODO: Describe for debugging / profiling.
|
||||
return false;
|
||||
// Used in disassembly viewer.
|
||||
if (ptr == dispatcher_) {
|
||||
name = "dispatcher";
|
||||
} else if (ptr == dispatcherPCInSCRATCH1_) {
|
||||
name = "dispatcher (PC in SCRATCH1)";
|
||||
} else if (ptr == dispatcherNoCheck_) {
|
||||
name = "dispatcherNoCheck";
|
||||
} else if (ptr == enterDispatcher_) {
|
||||
name = "enterDispatcher";
|
||||
} else if (!IsInSpace(ptr)) {
|
||||
return false;
|
||||
} else {
|
||||
uintptr_t uptr = (uintptr_t)ptr;
|
||||
int block_num = -1;
|
||||
for (int i = 0; i < MAX_ALLOWED_JIT_BLOCKS; ++i) {
|
||||
uintptr_t blockptr = (uintptr_t)blockStartAddrs_[i];
|
||||
// Out of allocated blocks.
|
||||
if (uptr == 0)
|
||||
break;
|
||||
|
||||
if (uptr >= blockptr)
|
||||
block_num = i;
|
||||
if (uptr < blockptr)
|
||||
break;
|
||||
}
|
||||
|
||||
if (block_num == -1) {
|
||||
name = "(unknown or deleted block)";
|
||||
return true;
|
||||
}
|
||||
|
||||
const IRBlock *block = blocks_.GetBlock(block_num);
|
||||
if (block) {
|
||||
u32 start = 0, size = 0;
|
||||
block->GetRange(start, size);
|
||||
name = StringFromFormat("(block %d at %08x)", block_num, start);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RiscVJit::CodeInRange(const u8 *ptr) const {
|
||||
@ -49,18 +172,15 @@ bool RiscVJit::CodeInRange(const u8 *ptr) const {
|
||||
}
|
||||
|
||||
bool RiscVJit::IsAtDispatchFetch(const u8 *ptr) const {
|
||||
// TODO
|
||||
return false;
|
||||
return ptr == dispatcherFetch_;
|
||||
}
|
||||
|
||||
const u8 *RiscVJit::GetDispatcher() const {
|
||||
// TODO
|
||||
return nullptr;
|
||||
return dispatcher_;
|
||||
}
|
||||
|
||||
const u8 *RiscVJit::GetCrashHandler() const {
|
||||
// TODO: Implement a crash handler
|
||||
return nullptr;
|
||||
return crashHandler_;
|
||||
}
|
||||
|
||||
void RiscVJit::ClearCache() {
|
||||
@ -68,12 +188,51 @@ void RiscVJit::ClearCache() {
|
||||
|
||||
ClearCodeSpace(jitStartOffset_);
|
||||
FlushIcacheSection(region + jitStartOffset_, region + region_size - jitStartOffset_);
|
||||
|
||||
memset(blockStartAddrs_, 0, sizeof(blockStartAddrs_[0]) * MAX_ALLOWED_JIT_BLOCKS);
|
||||
}
|
||||
|
||||
void RiscVJit::UpdateFCR31() {
|
||||
IRJit::UpdateFCR31();
|
||||
|
||||
// TODO: Handle rounding modes.
|
||||
// TODO: Handle rounding modes?
|
||||
}
|
||||
|
||||
void RiscVJit::RestoreRoundingMode(bool force) {
|
||||
// TODO: Could maybe skip if not hasSetRounding? But that's on IRFrontend...
|
||||
FSRMI(Round::NEAREST_EVEN);
|
||||
}
|
||||
|
||||
void RiscVJit::ApplyRoundingMode(bool force) {
|
||||
// TODO: Also could maybe sometimes skip?
|
||||
//QuickCallFunction(SCRATCH2, applyRoundingMode_);
|
||||
}
|
||||
|
||||
void RiscVJit::MovFromPC(RiscVGen::RiscVReg r) {
|
||||
LWU(r, CTXREG, offsetof(MIPSState, pc));
|
||||
}
|
||||
|
||||
void RiscVJit::MovToPC(RiscVGen::RiscVReg r) {
|
||||
SW(r, CTXREG, offsetof(MIPSState, pc));
|
||||
}
|
||||
|
||||
void RiscVJit::SaveStaticRegisters() {
|
||||
// TODO
|
||||
//if (jo.useStaticAlloc) {
|
||||
// QuickCallFunction(SCRATCH2, saveStaticRegisters_);
|
||||
//} else {
|
||||
// Inline the single operation
|
||||
SW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
|
||||
//}
|
||||
}
|
||||
|
||||
void RiscVJit::LoadStaticRegisters() {
|
||||
// TODO
|
||||
//if (jo.useStaticAlloc) {
|
||||
// QuickCallFunction(SCRATCH2, loadStaticRegisters_);
|
||||
//} else {
|
||||
LW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
|
||||
//}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
@ -17,14 +17,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Common/RiscVEmitter.h"
|
||||
#include "Core/MIPS/IR/IRJit.h"
|
||||
#include "Core/MIPS/JitCommon/JitState.h"
|
||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
class RiscVJit : public RiscVGen::RiscVCodeBlock, public IRJit {
|
||||
public:
|
||||
RiscVJit(MIPSState *mipsState);
|
||||
~RiscVJit();
|
||||
|
||||
void RunLoopUntil(u64 globalticks) override;
|
||||
|
||||
@ -42,12 +47,33 @@ public:
|
||||
protected:
|
||||
bool CompileBlock(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes, bool preload) override;
|
||||
|
||||
void CompileIRInst(IRInst inst);
|
||||
|
||||
private:
|
||||
void GenerateFixedCode(const JitOptions &jo);
|
||||
|
||||
void RestoreRoundingMode(bool force = false);
|
||||
void ApplyRoundingMode(bool force = false);
|
||||
void MovFromPC(RiscVGen::RiscVReg r);
|
||||
void MovToPC(RiscVGen::RiscVReg r);
|
||||
|
||||
void SaveStaticRegisters();
|
||||
void LoadStaticRegisters();
|
||||
|
||||
const u8 *enterDispatcher_ = nullptr;
|
||||
|
||||
const u8 *outerLoop_ = nullptr;
|
||||
const u8 *outerLoopPCInSCRATCH1_ = nullptr;
|
||||
const u8 *dispatcherCheckCoreState_ = nullptr;
|
||||
const u8 *dispatcherPCInSCRATCH1_ = nullptr;
|
||||
const u8 *dispatcher_ = nullptr;
|
||||
const u8 *dispatcherNoCheck_ = nullptr;
|
||||
const u8 *dispatcherFetch_ = nullptr;
|
||||
|
||||
const u8 *crashHandler_ = nullptr;
|
||||
|
||||
int jitStartOffset_ = 0;
|
||||
const u8 **blockStartAddrs_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
35
Core/MIPS/RiscV/RiscVRegCache.h
Normal file
35
Core/MIPS/RiscV/RiscVRegCache.h
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2023- 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/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/RiscVEmitter.h"
|
||||
|
||||
namespace RiscVJitConstants {
|
||||
|
||||
// Note: we don't support 32-bit or 128-bit CPUs currently.
|
||||
constexpr int XLEN = 64;
|
||||
|
||||
const RiscVGen::RiscVReg DOWNCOUNTREG = RiscVGen::X24;
|
||||
const RiscVGen::RiscVReg JITBASEREG = RiscVGen::X25;
|
||||
const RiscVGen::RiscVReg CTXREG = RiscVGen::X26;
|
||||
const RiscVGen::RiscVReg MEMBASEREG = RiscVGen::X27;
|
||||
// TODO: Experiment. X7-X13 are compressed regs. X8/X9 are saved so nice for static alloc, though.
|
||||
const RiscVGen::RiscVReg SCRATCH1 = RiscVGen::X10;
|
||||
const RiscVGen::RiscVReg SCRATCH2 = RiscVGen::X11;
|
||||
|
||||
} // namespace RiscVJitConstants
|
Loading…
Reference in New Issue
Block a user