mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
riscv: Add initial emitter shell.
This commit is contained in:
parent
fc81b76b98
commit
9fcad83940
7 changed files with 333 additions and 0 deletions
|
@ -475,6 +475,8 @@ source_group(MIPS FILES ${CommonMIPS})
|
|||
set(CommonRISCV64
|
||||
${CommonJIT}
|
||||
Common/RiscVCPUDetect.cpp
|
||||
Common/RiscVEmitter.cpp
|
||||
Common/RiscVEmitter.h
|
||||
Core/MIPS/fake/FakeJit.cpp
|
||||
Core/MIPS/fake/FakeJit.h
|
||||
)
|
||||
|
|
|
@ -475,6 +475,7 @@
|
|||
<ClInclude Include="Render\Text\draw_text_uwp.h" />
|
||||
<ClInclude Include="Render\Text\draw_text_win.h" />
|
||||
<ClInclude Include="LogReporting.h" />
|
||||
<ClInclude Include="RiscVEmitter.h" />
|
||||
<ClInclude Include="Serialize\SerializeDeque.h" />
|
||||
<ClInclude Include="Serialize\SerializeFuncs.h" />
|
||||
<ClInclude Include="Serialize\SerializeList.h" />
|
||||
|
@ -896,6 +897,7 @@
|
|||
<ClCompile Include="Render\Text\draw_text_win.cpp" />
|
||||
<ClCompile Include="LogReporting.cpp" />
|
||||
<ClCompile Include="RiscVCPUDetect.cpp" />
|
||||
<ClCompile Include="RiscVEmitter.cpp" />
|
||||
<ClCompile Include="Serialize\Serializer.cpp" />
|
||||
<ClCompile Include="Data\Convert\ColorConv.cpp" />
|
||||
<ClCompile Include="ConsoleListener.cpp" />
|
||||
|
|
|
@ -421,6 +421,7 @@
|
|||
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RiscVEmitter.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABI.cpp" />
|
||||
|
@ -798,6 +799,7 @@
|
|||
<ClCompile Include="GPU\Vulkan\VulkanBarrier.cpp">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RiscVEmitter.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Crypto">
|
||||
|
|
216
Common/RiscVEmitter.cpp
Normal file
216
Common/RiscVEmitter.cpp
Normal file
|
@ -0,0 +1,216 @@
|
|||
// Copyright (c) 2022- 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 "ppsspp_config.h"
|
||||
#include "Common/RiscVEmitter.h"
|
||||
|
||||
namespace RiscVGen {
|
||||
|
||||
static inline bool SupportsCompressed() {
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
enum class Opcode32 {
|
||||
// Note: invalid, just used for FixupBranch.
|
||||
ZERO = 0b0000000,
|
||||
SYSTEM = 0b1110011,
|
||||
};
|
||||
|
||||
enum class Funct3 {
|
||||
// Note: invalid, just used for FixupBranch.
|
||||
ZERO = 0b000,
|
||||
PRIV = 0b000,
|
||||
};
|
||||
|
||||
enum class Funct2 {
|
||||
// TODO: 0b00,
|
||||
};
|
||||
|
||||
enum class Funct7 {
|
||||
// TODO: 0b0000000,
|
||||
};
|
||||
|
||||
enum class Funct12 {
|
||||
ECALL = 0b000000000000,
|
||||
EBREAK = 0b000000000001,
|
||||
};
|
||||
|
||||
static inline u32 EncodeR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct7 funct7) {
|
||||
return (u32)opcode | ((u32)rd << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)funct7 << 25);
|
||||
}
|
||||
|
||||
static inline u32 EncodeR4(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct2 funct2, RiscVReg rs3) {
|
||||
return (u32)opcode | ((u32)rd << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)funct2 << 25) | ((u32)rs3 << 27);
|
||||
}
|
||||
|
||||
static inline u32 EncodeI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, s32 simm12) {
|
||||
_assert_msg_(((simm12 << 20) >> 20) == simm12, "I immediate must be signed s11.0");
|
||||
return (u32)opcode | ((u32)rd << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)simm12 << 20);
|
||||
}
|
||||
|
||||
static inline u32 EncodeI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, Funct12 funct12) {
|
||||
return EncodeI(opcode, rd, funct3, rs1, (s32)funct12);
|
||||
}
|
||||
|
||||
static inline u32 EncodeS(Opcode32 opcode, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, s32 simm12) {
|
||||
_assert_msg_(((simm12 << 20) >> 20) == simm12, "S immediate must be signed s11.0");
|
||||
u32 imm4_0 = simm12 & 0x1F;
|
||||
u32 imm11_5 = (simm12 >> 5) & 0x7F;
|
||||
return (u32)opcode | ((u32)imm4_0 << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)imm11_5 << 25);
|
||||
}
|
||||
|
||||
static inline u32 EncodeB(Opcode32 opcode, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, s32 simm13) {
|
||||
_assert_msg_(((simm13 << 19) >> 19) == simm13, "B immediate must be signed s12.0");
|
||||
_assert_msg_((simm13 & 1) == 0, "B immediate must be even");
|
||||
u32 imm11 = (simm13 >> 11) & 1;
|
||||
u32 imm12 = (simm13 >> 12) & 1;
|
||||
// This weird encoding scheme is to keep most bits the same as S, but keep sign at 31.
|
||||
u32 imm4_1_11 = (simm13 & 0x1E) | imm11;
|
||||
u32 imm12_10_5 = (imm12 << 6) | ((simm13 >> 5) & 0x3F);
|
||||
return (u32)opcode | ((u32)imm4_1_11 << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)imm12_10_5 << 25);
|
||||
}
|
||||
|
||||
static inline u32 EncodeU(Opcode32 opcode, RiscVReg rd, s32 simm32) {
|
||||
_assert_msg_((simm32 & 0x0FFF) == 0, "U immediate must not have lower 12 bits set");
|
||||
return (u32)opcode | ((u32)rd << 7) | (u32)simm32;
|
||||
}
|
||||
|
||||
static inline u32 EncodeJ(Opcode32 opcode, RiscVReg rd, s32 simm21) {
|
||||
_assert_msg_(((simm21 << 11) >> 11) == simm21, "J immediate must be signed s20.0");
|
||||
_assert_msg_((simm21 & 1) == 0, "J immediate must be even");
|
||||
u32 imm11 = (simm21 >> 11) & 1;
|
||||
u32 imm20 = (simm21 >> 20) & 1;
|
||||
u32 imm10_1 = (simm21 >> 1) & 0x03FF;
|
||||
u32 imm19_12 = (simm21 >> 12) & 0x00FF;
|
||||
// This encoding scheme tries to keep the bits from B in the same places, plus sign.
|
||||
u32 imm20_10_1_11_19_12 = (imm20 << 19) | (imm10_1 << 9) | (imm11 << 8) | imm19_12;
|
||||
return (u32)opcode | ((u32)rd << 7) | ((u32)imm20_10_1_11_19_12 << 12);
|
||||
}
|
||||
|
||||
RiscVEmitter::RiscVEmitter(const u8 *ptr, u8 *writePtr) {
|
||||
SetCodePointer(ptr, writePtr);
|
||||
}
|
||||
|
||||
void RiscVEmitter::SetCodePointer(const u8 *ptr, u8 *writePtr) {
|
||||
code_ = ptr;
|
||||
writable_ = writePtr;
|
||||
lastCacheFlushEnd_ = ptr;
|
||||
}
|
||||
|
||||
const u8 *RiscVEmitter::GetCodePointer() const {
|
||||
return code_;
|
||||
}
|
||||
|
||||
u8 *RiscVEmitter::GetWritableCodePtr() {
|
||||
return writable_;
|
||||
}
|
||||
|
||||
void RiscVEmitter::ReserveCodeSpace(u32 bytes) {
|
||||
_assert_msg_((bytes & 1) == 0, "Code space should be aligned");
|
||||
_assert_msg_((bytes & 3) == 0 || SupportsCompressed(), "Code space should be aligned (no compressed)");
|
||||
for (u32 i = 0; i < bytes / 4; i++)
|
||||
EBREAK();
|
||||
if (bytes & 2)
|
||||
Write16(0);
|
||||
}
|
||||
|
||||
const u8 *RiscVEmitter::AlignCode16() {
|
||||
int c = int((u64)code_ & 15);
|
||||
if (c)
|
||||
ReserveCodeSpace(16 - c);
|
||||
return code_;
|
||||
}
|
||||
|
||||
const u8 *RiscVEmitter::AlignCodePage() {
|
||||
int page_size = GetMemoryProtectPageSize();
|
||||
int c = int((u64)code_ & (page_size - 1));
|
||||
if (c)
|
||||
ReserveCodeSpace(page_size - c);
|
||||
return code_;
|
||||
}
|
||||
|
||||
void RiscVEmitter::FlushIcache() {
|
||||
FlushIcacheSection(lastCacheFlushEnd_, code_);
|
||||
lastCacheFlushEnd_ = code_;
|
||||
}
|
||||
|
||||
void RiscVEmitter::FlushIcacheSection(const u8 *start, const u8 *end) {
|
||||
#if PPSSPP_ARCH(RISCV64)
|
||||
__builtin___clear_cache(start, end);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RiscVEmitter::SetJumpTarget(const FixupBranch &branch) {
|
||||
SetJumpTarget(branch, code_);
|
||||
}
|
||||
|
||||
void RiscVEmitter::SetJumpTarget(const FixupBranch &branch, const void *dst) {
|
||||
const intptr_t srcp = (intptr_t)branch.ptr;
|
||||
const intptr_t dstp = (intptr_t)dst;
|
||||
const ptrdiff_t writable_delta = writable_ - code_;
|
||||
u32 *fixup = (u32 *)(branch.ptr + writable_delta);
|
||||
|
||||
_assert_msg_((dstp & 1) == 0, "Destination should be aligned");
|
||||
_assert_msg_((dstp & 3) == 0 || SupportsCompressed(), "Destination should be aligned (no compressed)");
|
||||
|
||||
ptrdiff_t distance = dstp - srcp;
|
||||
_assert_msg_((distance & 1) == 0, "Distance should be aligned");
|
||||
_assert_msg_((distance & 3) == 0 || SupportsCompressed(), "Distance should be aligned (no compressed)");
|
||||
|
||||
switch (branch.type) {
|
||||
case FixupBranchType::B:
|
||||
_assert_msg_(BInRange(branch.ptr, dst), "B destination is too far away (%p -> %p)", branch.ptr, dst);
|
||||
*fixup = (*fixup & 0x01FFF07F) | EncodeB(Opcode32::ZERO, Funct3::ZERO, R_ZERO, R_ZERO, (s32)distance);
|
||||
break;
|
||||
|
||||
case FixupBranchType::J:
|
||||
_assert_msg_(JInRange(branch.ptr, dst), "J destination is too far away (%p -> %p)", branch.ptr, dst);
|
||||
*fixup = (*fixup & 0x00000FFF) | EncodeJ(Opcode32::ZERO, R_ZERO, (s32)distance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool RiscVEmitter::BInRange(const void *func) const {
|
||||
return BInRange(code_, func);
|
||||
}
|
||||
|
||||
bool RiscVEmitter::JInRange(const void *func) const {
|
||||
return JInRange(code_, func);
|
||||
}
|
||||
|
||||
bool RiscVEmitter::BInRange(const void *src, const void *dst) const {
|
||||
const intptr_t srcp = (intptr_t)src;
|
||||
const intptr_t dstp = (intptr_t)dst;
|
||||
ptrdiff_t distance = dstp - srcp;
|
||||
|
||||
return distance <= 0x00000FFE && -distance <= 0x00000FFE;
|
||||
}
|
||||
|
||||
bool RiscVEmitter::JInRange(const void *src, const void *dst) const {
|
||||
const intptr_t srcp = (intptr_t)src;
|
||||
const intptr_t dstp = (intptr_t)dst;
|
||||
ptrdiff_t distance = dstp - srcp;
|
||||
|
||||
return distance <= 0x000FFFFE && -distance <= 0x000FFFFE;
|
||||
}
|
||||
|
||||
void RiscVEmitter::EBREAK() {
|
||||
Write32(EncodeI(Opcode32::SYSTEM, R_ZERO, Funct3::PRIV, R_ZERO, Funct12::EBREAK));
|
||||
}
|
||||
|
||||
};
|
107
Common/RiscVEmitter.h
Normal file
107
Common/RiscVEmitter.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) 2022- 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 <cstdint>
|
||||
#include "Common/CodeBlock.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace RiscVGen {
|
||||
|
||||
enum RiscVReg {
|
||||
X0 = 0, X1, X2, X3, X4, X5, X6, X7,
|
||||
X8, X9, X10, X11, X12, X13, X14, X15,
|
||||
X16, X17, X18, X19, X20, X21, X22, X23,
|
||||
X24, X25, X26, X27, X28, X29, X30, X31,
|
||||
|
||||
R_ZERO = 0,
|
||||
R_RA = 1,
|
||||
R_SP = 2,
|
||||
R_GP = 3,
|
||||
R_TP = 4,
|
||||
R_FP = 8,
|
||||
|
||||
F0 = 0x20, F1, F2, F3, F4, F5, F6, F7,
|
||||
F8, F9, F10, F11, F12, F13, F14, F15,
|
||||
F16, F17, F18, F19, F20, F21, F22, F23,
|
||||
F24, F25, F26, F27, F28, F29, F30, F31,
|
||||
};
|
||||
|
||||
inline RiscVReg DecodeReg(RiscVReg reg) { return (RiscVReg)(reg & 0x1F); }
|
||||
|
||||
enum class FixupBranchType {
|
||||
B,
|
||||
J,
|
||||
};
|
||||
|
||||
struct FixupBranch {
|
||||
FixupBranch(const u8 *p, FixupBranchType t) : ptr(p), type(t) {}
|
||||
|
||||
const u8 *ptr;
|
||||
FixupBranchType type;
|
||||
};
|
||||
|
||||
class RiscVEmitter {
|
||||
public:
|
||||
RiscVEmitter() {}
|
||||
RiscVEmitter(const u8 *codePtr, u8 *writablePtr);
|
||||
virtual ~RiscVEmitter() {}
|
||||
|
||||
void SetCodePointer(const u8 *ptr, u8 *writePtr);
|
||||
const u8 *GetCodePointer() const;
|
||||
u8 *GetWritableCodePtr();
|
||||
|
||||
void ReserveCodeSpace(u32 bytes);
|
||||
const u8 *AlignCode16();
|
||||
const u8 *AlignCodePage();
|
||||
void FlushIcache();
|
||||
void FlushIcacheSection(const u8 *start, const u8 *end);
|
||||
|
||||
void SetJumpTarget(const FixupBranch &branch);
|
||||
bool BInRange(const void *func) const;
|
||||
bool JInRange(const void *func) const;
|
||||
|
||||
void EBREAK();
|
||||
|
||||
private:
|
||||
void SetJumpTarget(const FixupBranch &branch, const void *dst);
|
||||
bool BInRange(const void *src, const void *dst) const;
|
||||
bool JInRange(const void *src, const void *dst) const;
|
||||
|
||||
inline void Write32(u32 value) {
|
||||
*(u32 *)writable_ = value;
|
||||
code_ += 4;
|
||||
writable_ += 4;
|
||||
}
|
||||
inline void Write16(u16 value) {
|
||||
*(u16 *)writable_ = value;
|
||||
code_ += 2;
|
||||
writable_ += 2;
|
||||
}
|
||||
|
||||
const u8 *code_ = nullptr;
|
||||
u8 *writable_ = nullptr;
|
||||
const u8 *lastCacheFlushEnd_ = nullptr;
|
||||
};
|
||||
|
||||
class MIPSCodeBlock : public CodeBlock<RiscVEmitter> {
|
||||
private:
|
||||
void PoisonMemory(int offset) override;
|
||||
};
|
||||
|
||||
};
|
|
@ -479,6 +479,7 @@
|
|||
<ClInclude Include="..\..\Common\MemArena.h" />
|
||||
<ClInclude Include="..\..\Common\MemoryUtil.h" />
|
||||
<ClInclude Include="..\..\Common\MipsEmitter.h" />
|
||||
<ClInclude Include="..\..\Common\RiscVEmitter.h" />
|
||||
<ClInclude Include="..\..\Common\OSVersion.h" />
|
||||
<ClInclude Include="..\..\Common\StringUtils.h" />
|
||||
<ClInclude Include="..\..\Common\Swap.h" />
|
||||
|
@ -594,6 +595,7 @@
|
|||
<ClCompile Include="..\..\Common\MemoryUtil.cpp" />
|
||||
<ClCompile Include="..\..\Common\MipsCPUDetect.cpp" />
|
||||
<ClCompile Include="..\..\Common\MipsEmitter.cpp" />
|
||||
<ClCompile Include="..\..\Common\RiscVEmitter.cpp" />
|
||||
<ClCompile Include="..\..\Common\SysError.cpp" />
|
||||
<ClCompile Include="..\..\Common\OSVersion.cpp" />
|
||||
<ClCompile Include="..\..\Common\StringUtils.cpp" />
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
<ClCompile Include="..\..\Common\MemoryUtil.cpp" />
|
||||
<ClCompile Include="..\..\Common\MipsCPUDetect.cpp" />
|
||||
<ClCompile Include="..\..\Common\MipsEmitter.cpp" />
|
||||
<ClCompile Include="..\..\Common\RiscVEmitter.cpp" />
|
||||
<ClCompile Include="..\..\Common\SysError.cpp" />
|
||||
<ClCompile Include="..\..\Common\OSVersion.cpp" />
|
||||
<ClCompile Include="..\..\Common\StringUtils.cpp" />
|
||||
|
@ -404,6 +405,7 @@
|
|||
<ClInclude Include="..\..\Common\MemArena.h" />
|
||||
<ClInclude Include="..\..\Common\MemoryUtil.h" />
|
||||
<ClInclude Include="..\..\Common\MipsEmitter.h" />
|
||||
<ClInclude Include="..\..\Common\RiscVEmitter.h" />
|
||||
<ClInclude Include="..\..\Common\OSVersion.h" />
|
||||
<ClInclude Include="..\..\Common\StringUtils.h" />
|
||||
<ClInclude Include="..\..\Common\Swap.h" />
|
||||
|
|
Loading…
Add table
Reference in a new issue