Common: Add LoongArch support

This commit is contained in:
Yang Liu
2026-03-02 15:53:10 +08:00
committed by Connor McLaughlin
parent c41cd7937a
commit f6d526712e
10 changed files with 190 additions and 5 deletions

View File

@@ -25,6 +25,8 @@ elseif(LINUX)
set(DEPS_PATH "${CMAKE_SOURCE_DIR}/dep/prebuilt/linux${DEPS_CROSS_PREFIX}-armhf")
elseif(CPU_ARCH_ARM64)
set(DEPS_PATH "${CMAKE_SOURCE_DIR}/dep/prebuilt/linux${DEPS_CROSS_PREFIX}-arm64")
elseif(CPU_ARCH_LOONGARCH64)
set(DEPS_PATH "${CMAKE_SOURCE_DIR}/dep/prebuilt/linux${DEPS_CROSS_PREFIX}-loongarch64")
else()
message(FATAL_ERROR "Unsupported architecture")
endif()

View File

@@ -110,6 +110,9 @@ function(detect_architecture)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS}" PARENT_SCOPE)
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "loongarch64")
message(STATUS "Building LoongArch 64 binaries.")
set(CPU_ARCH_LOONGARCH64 TRUE PARENT_SCOPE)
else()
message(FATAL_ERROR "Unknown system processor: ${CMAKE_SYSTEM_PROCESSOR}")
endif()

View File

@@ -182,6 +182,63 @@ asm(
jr ra
)");
#elif defined(__loongarch64)
asm(
"\t.global " PREFIX "fastjmp_set\n"
"\t.global " PREFIX "fastjmp_jmp\n"
"\t.text\n"
"\t.align 16\n"
"\t" PREFIX "fastjmp_set:" R"(
st.d $sp, $a0, 0
st.d $s0, $a0, 8
st.d $s1, $a0, 16
st.d $s2, $a0, 24
st.d $s3, $a0, 32
st.d $s4, $a0, 40
st.d $s5, $a0, 48
st.d $s6, $a0, 56
st.d $s7, $a0, 64
st.d $s8, $a0, 72
st.d $s9, $a0, 80
fst.d $fs0, $a0, 88
fst.d $fs1, $a0, 96
fst.d $fs2, $a0, 104
fst.d $fs3, $a0, 112
fst.d $fs4, $a0, 120
fst.d $fs5, $a0, 128
fst.d $fs6, $a0, 136
fst.d $fs7, $a0, 144
st.d $ra, $a0, 152
addi.d $a0, $zero, 0
jirl $zero, $ra, 0
)"
".align 16\n"
"\t" PREFIX "fastjmp_jmp:" R"(
ld.d $ra, $a0, 152
fld.d $fs7, $a0, 144
fld.d $fs6, $a0, 136
fld.d $fs5, $a0, 128
fld.d $fs4, $a0, 120
fld.d $fs3, $a0, 112
fld.d $fs2, $a0, 104
fld.d $fs1, $a0, 96
fld.d $fs0, $a0, 88
ld.d $s9, $a0, 80
ld.d $s8, $a0, 72
ld.d $s7, $a0, 64
ld.d $s6, $a0, 56
ld.d $s5, $a0, 48
ld.d $s4, $a0, 40
ld.d $s3, $a0, 32
ld.d $s2, $a0, 24
ld.d $s1, $a0, 16
ld.d $s0, $a0, 8
ld.d $sp, $a0, 0
move $a0, $a1
jirl $zero, $ra, 0
)");
#else

View File

@@ -22,6 +22,8 @@ struct fastjmp_buf
static constexpr std::size_t BUF_SIZE = 24;
#elif defined(__riscv) && __riscv_xlen == 64
static constexpr std::size_t BUF_SIZE = 216;
#elif defined(__loongarch64)
static constexpr std::size_t BUF_SIZE = 160;
#else
#error Unknown architecture.
#endif

View File

@@ -140,6 +140,15 @@ ALWAYS_INLINE static void MultiPause()
#elif defined(CPU_ARCH_RISCV64)
// Probably wrong... pause is optional :/
asm volatile("fence" ::: "memory");
#elif defined(CPU_ARCH_LOONGARCH64)
asm volatile("ibar 0" ::: "memory");
asm volatile("ibar 0" ::: "memory");
asm volatile("ibar 0" ::: "memory");
asm volatile("ibar 0" ::: "memory");
asm volatile("ibar 0" ::: "memory");
asm volatile("ibar 0" ::: "memory");
asm volatile("ibar 0" ::: "memory");
asm volatile("ibar 0" ::: "memory");
#else
#pragma warning("Missing implementation")
#endif

View File

@@ -164,7 +164,7 @@ void MemMap::ReleaseJITMemory(void* ptr, size_t size)
ERROR_LOG("Failed to free code pointer {}", static_cast<void*>(ptr));
}
#if defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64) || defined(CPU_ARCH_RISCV64)
#if defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64) || defined(CPU_ARCH_RISCV64) || defined(CPU_ARCH_LOONGARCH64)
void MemMap::FlushInstructionCache(void* address, size_t size)
{
@@ -532,7 +532,7 @@ void MemMap::ReleaseJITMemory(void* ptr, size_t size)
#endif
}
#if defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64) || defined(CPU_ARCH_RISCV64)
#if defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64) || defined(CPU_ARCH_RISCV64) || defined(CPU_ARCH_LOONGARCH64)
void MemMap::FlushInstructionCache(void* address, size_t size)
{
@@ -820,7 +820,7 @@ void MemMap::ReleaseJITMemory(void* ptr, size_t size)
ERROR_LOG("Failed to free code pointer {}", static_cast<void*>(ptr));
}
#if defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64) || defined(CPU_ARCH_RISCV64)
#if defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64) || defined(CPU_ARCH_RISCV64) || defined(CPU_ARCH_LOONGARCH64)
void MemMap::FlushInstructionCache(void* address, size_t size)
{
@@ -905,7 +905,7 @@ void* MemMap::AllocateJITMemory(size_t size)
static constexpr size_t assume_binary_size = 64 * 1024 * 1024;
static constexpr size_t step = 64 * 1024 * 1024;
static constexpr size_t max_displacement = 0x80000000u;
#elif defined(CPU_ARCH_ARM64) || defined(CPU_ARCH_RISCV64)
#elif defined(CPU_ARCH_ARM64) || defined(CPU_ARCH_RISCV64) || defined(CPU_ARCH_LOONGARCH64)
static constexpr size_t assume_binary_size = 16 * 1024 * 1024;
static constexpr size_t step = 8 * 1024 * 1024;
static constexpr size_t max_displacement =

View File

@@ -73,7 +73,7 @@ void ReleaseJITMemory(void* ptr, size_t size);
/// Flushes the instruction cache on the host for the specified range.
/// Only needed outside of X86, X86 has coherent D/I cache.
#if !defined(CPU_ARCH_ARM32) && !defined(CPU_ARCH_ARM64) && !defined(CPU_ARCH_RISCV64)
#if !defined(CPU_ARCH_ARM32) && !defined(CPU_ARCH_ARM64) && !defined(CPU_ARCH_RISCV64) && !defined(CPU_ARCH_LOONGARCH64)
// clang-format off
ALWAYS_INLINE static void FlushInstructionCache(void* address, size_t size) { }
// clang-format on

View File

@@ -162,6 +162,8 @@ struct dependent_int_false : std::false_type
#define CPU_ARCH_ARM32 1
#elif defined(__riscv) && __riscv_xlen == 64
#define CPU_ARCH_RISCV64 1
#elif defined(__loongarch64)
#define CPU_ARCH_LOONGARCH64 1
#else
#error Unknown architecture.
#endif
@@ -182,6 +184,8 @@ struct dependent_int_false : std::false_type
#define CPU_ARCH_STR "arm64"
#elif defined(CPU_ARCH_RISCV64)
#define CPU_ARCH_STR "riscv64"
#elif defined(CPU_ARCH_LOONGARCH64)
#define CPU_ARCH_STR "loongarch64"
#else
#define CPU_ARCH_STR "Unknown"
#endif

View File

@@ -89,6 +89,8 @@ static constexpr u32 HTTP_POLL_INTERVAL = 10;
#define UPDATER_ASSET_FILENAME "DuckStation-armhf.AppImage"
#elif defined(CPU_ARCH_RISCV64)
#define UPDATER_ASSET_FILENAME "DuckStation-riscv64.AppImage"
#elif defined(CPU_ARCH_LOONGARCH64)
#define UPDATER_ASSET_FILENAME "DuckStation-loongarch64.AppImage"
#endif
#endif
#ifndef UPDATER_ASSET_FILENAME

View File

@@ -73,6 +73,109 @@
return ((bits & 0x7Fu) == 0b0100011u);
}
#elif defined(CPU_ARCH_LOONGARCH64)
[[maybe_unused]] static bool IsStoreInstruction(const void* ptr)
{
u32 bits;
std::memcpy(&bits, ptr, sizeof(bits));
u32 opcode = bits >> 24;
u16 bits31_16 = bits >> 16;
// st.{b,h,w,d} and stptr.{b,h,w,d}
if (opcode == 0x27 || opcode == 0x29 || opcode == 0x2D)
{
return true;
}
// stx.{b,h,w,d}
if ((bits31_16 & 0xFFF0) == 0x3810)
{
return true;
}
// fst.{s,d}
if (opcode == 0x2B && (bits & (1 << 22)))
{
return true;
}
// fstx.{s,d} - bit 3 distinguishes from fldx
if ((bits31_16 & 0xFFF8) == 0x3838)
{
return true;
}
// vst, vstx - vst has bit 22 set; vstx has bit 2 set
if (opcode == 0x2C && (bits & (1 << 22)))
{
return true;
}
if ((bits31_16 & 0xFFFC) == 0x3844)
{
return true;
}
// xvst, xvstx - xvst has bits 23,22 set; xvstx has bit 2 set
if (opcode == 0x2C && (bits & (1 << 23)) && (bits & (1 << 22)))
{
return true;
}
if ((bits31_16 & 0xFFFC) == 0x384C)
{
return true;
}
// stgt.{b,h,w,d}, stle.{b,h,w,d}
if ((bits31_16 & 0xFFFC) >= 0x387C && (bits31_16 & 0xFFFC) <= 0x387F)
{
return true;
}
// fstgt.{s,d}, fstle.{s,d}
if ((bits31_16 & 0xFFFC) >= 0x3874 && (bits31_16 & 0xFFFC) <= 0x3877)
{
return true;
}
// sc.w, sc.d
if (opcode == 0x21 || opcode == 0x23)
{
return true;
}
// amswap, amadd, amand, amor, amxor, ammax, ammin, amcas, etc.
if (bits31_16 >= 0x3858 && bits31_16 <= 0x3871)
{
return true;
}
// stl.w, str.w, stl.d, str.d
if (opcode == 0x2F)
{
return true;
}
// sc.q, screl.w, screl.d
if (bits31_16 == 0x3857)
{
return true;
}
// vstelm.*, xvstelm.*
uint16_t bits31_24 = bits >> 24;
if (bits31_24 == 0x31 || bits31_24 == 0x33)
{
uint32_t bits23_20 = (bits >> 20) & 0xF;
// vstelm has sub-opcodes: .b=0x8, .h=0x4, .w=0x2, .d=0x1
if (bits23_20 == 0x1 || bits23_20 == 0x2 || bits23_20 == 0x4 || bits23_20 == 0x8)
{
return true;
}
}
return false;
}
#endif
#if defined(_WIN32)
@@ -161,6 +264,9 @@ void PageFaultHandler::SignalHandler(int sig, siginfo_t* info, void* ctx)
#elif defined(CPU_ARCH_RISCV64)
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.__gregs[REG_PC]);
const bool is_write = IsStoreInstruction(exception_pc);
#elif defined(CPU_ARCH_LOONGARCH64)
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.__pc);
const bool is_write = IsStoreInstruction(exception_pc);
#else
void* const exception_pc = nullptr;
const bool is_write = false;