diff --git a/common/sse2simde.h b/common/sse2simde.h new file mode 100644 index 0000000000..20a4ebebc5 --- /dev/null +++ b/common/sse2simde.h @@ -0,0 +1,22 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Provide the SSE intrinsics API on non-x86 / non-ARM targets +// (e.g. RISC-V) via SIMDe (SIMD Everywhere, https://github.com/simd-everywhere/simde). +// +// This is the counterpart of sse2neon.h for ARM: code written against +// / keeps working, and SIMDe maps it to the +// native vector extension where it can (RVV on RISC-V) or to portable C. +// +// Requires the SIMDe headers (Debian/Ubuntu: libsimde-dev, Fedora: simde-devel). +// +//===========================================================================// +#ifndef SSE2SIMDE_H +#define SSE2SIMDE_H + +#ifndef SIMDE_ENABLE_NATIVE_ALIASES +#define SIMDE_ENABLE_NATIVE_ALIASES +#endif + +#include + +#endif // SSE2SIMDE_H diff --git a/mathlib/sse.cpp b/mathlib/sse.cpp index 6122b66487..7587408e84 100644 --- a/mathlib/sse.cpp +++ b/mathlib/sse.cpp @@ -13,6 +13,8 @@ #include "mathlib/vector.h" #if defined(__arm__) || defined(__aarch64__) #include "sse2neon.h" +#elif defined(__riscv) +#include "sse2simde.h" #endif #include "sse.h" @@ -180,7 +182,7 @@ float _SSE_RSqrtFast(float x) Assert( s_bMathlibInitialized ); float rroot; -#if defined(__arm__) || defined(__aarch64__) +#if defined(__arm__) || defined(__aarch64__) || defined(__riscv) rroot = _SSE_RSqrtAccurate(x); #elif _WIN32 _asm @@ -217,7 +219,7 @@ float FASTCALL _SSE_VectorNormalize (Vector& vec) // be much of a performance win, considering you will very likely miss 3 branch predicts in a row. if ( v[0] || v[1] || v[2] ) { -#if defined(__arm__) || defined(__aarch64__) +#if defined(__arm__) || defined(__aarch64__) || defined(__riscv) float rsqrt = _SSE_RSqrtAccurate( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] ); r[0] = v[0] * rsqrt; r[1] = v[1] * rsqrt; @@ -296,7 +298,7 @@ void FASTCALL _SSE_VectorNormalizeFast (Vector& vec) float _SSE_InvRSquared(const float* v) { float inv_r2 = 1.f; -#if defined(__arm__) || defined(__aarch64__) +#if defined(__arm__) || defined(__aarch64__) || defined(__riscv) return _SSE_RSqrtAccurate( FLT_EPSILON + v[0] * v[0] + v[1] * v[1] + v[2] * v[2] ); #elif _WIN32 _asm { // Intel SSE only routine @@ -391,7 +393,7 @@ typedef __m64 v2si; // vector of 2 int (mmx) void _SSE_SinCos(float x, float* s, float* c) { -#if defined(__arm__) || defined(__aarch64__) +#if defined(__arm__) || defined(__aarch64__) || defined(__riscv) #if defined( OSX ) __sincosf(x, s, c); #elif defined( POSIX ) @@ -607,7 +609,7 @@ void _SSE_SinCos(float x, float* s, float* c) float _SSE_cos( float x ) { -#if defined(__arm__) || defined(__aarch64__) +#if defined(__arm__) || defined(__aarch64__) || defined(__riscv) return cos(x); #elif _WIN32 float temp; diff --git a/public/materialsystem/imesh.h b/public/materialsystem/imesh.h index 6a952e0099..e853a79032 100644 --- a/public/materialsystem/imesh.h +++ b/public/materialsystem/imesh.h @@ -1152,7 +1152,7 @@ inline void CVertexBuilder::FastAdvanceNVertices( int n ) //----------------------------------------------------------------------------- inline void CVertexBuilder::FastVertex( const ModelVertexDX7_t &vertex ) { -#if defined(__arm__) || defined(__aarch64__) || defined(PLATFORM_WINDOWS_PC64) +#if defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(PLATFORM_WINDOWS_PC64) FastVertexSSE( vertex ); #else Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed @@ -1309,7 +1309,7 @@ inline void CVertexBuilder::Fast4VerticesSSE( movntps [edi + 80+96], xmm5 } -#elif defined(__arm__) || defined(PLATFORM_WINDOWS_PC64) +#elif defined(__arm__) || defined(__riscv) || defined(PLATFORM_WINDOWS_PC64) const void *pReadA = &vtx_a; const void *pReadB = &vtx_b; const void *pReadC = &vtx_c; @@ -1354,7 +1354,7 @@ inline void CVertexBuilder::Fast4VerticesSSE( inline void CVertexBuilder::FastVertex( const ModelVertexDX8_t &vertex ) { -#if defined(__arm__) || defined(__aarch64__) || defined(PLATFORM_WINDOWS_PC64) +#if defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(PLATFORM_WINDOWS_PC64) FastVertexSSE( vertex ); #else Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed diff --git a/public/mathlib/mathlib.h b/public/mathlib/mathlib.h index 1e8e0266ea..da8e4d5262 100644 --- a/public/mathlib/mathlib.h +++ b/public/mathlib/mathlib.h @@ -1203,6 +1203,8 @@ FORCEINLINE int RoundFloatToInt(float f) return pResult[1]; #elif defined (__arm__) || defined (__aarch64__) return (int)(f + 0.5f); +#elif defined( PLATFORM_RISCV64 ) + return (int)lrintf( f ); // round to nearest (even), like cvtss2si on x86 #else #error Unknown architecture #endif @@ -1235,6 +1237,8 @@ FORCEINLINE unsigned long RoundFloatToUnsignedLong(float f) #else // !X360 #if defined(__arm__) || defined(__aarch64__) return (unsigned long)(f + 0.5f); +#elif defined( PLATFORM_RISCV64 ) + return (unsigned long)llrintf( f ); #elif defined( PLATFORM_WINDOWS_PC64 ) uint nRet = ( uint ) f; if ( nRet & 1 ) diff --git a/public/mathlib/ssemath.h b/public/mathlib/ssemath.h index 541419950f..98566a86e1 100644 --- a/public/mathlib/ssemath.h +++ b/public/mathlib/ssemath.h @@ -10,6 +10,8 @@ #include #elif defined(__arm__) || defined(__aarch64__) #include "sse2neon.h" +#elif defined(__riscv) +#include "sse2simde.h" #else #include #endif diff --git a/public/mathlib/vector4d.h b/public/mathlib/vector4d.h index 99e0995ff3..f3c68c7a8a 100644 --- a/public/mathlib/vector4d.h +++ b/public/mathlib/vector4d.h @@ -25,6 +25,8 @@ #if defined (__arm__) || defined(__aarch64__) #include "sse2neon.h" +#elif defined(__riscv) +#include "sse2simde.h" #endif // forward declarations diff --git a/public/saverestoretypes.h b/public/saverestoretypes.h index e470f662c9..fec300e254 100644 --- a/public/saverestoretypes.h +++ b/public/saverestoretypes.h @@ -512,7 +512,7 @@ inline const char *CSaveRestoreSegment::StringFromSymbol( int token ) /// compilers. Either way, there's no portable intrinsic. // Newer GCC versions provide this in this header, older did by default. -#if !defined( _rotr ) && defined( COMPILER_GCC ) && !defined( __arm__ ) && !defined( __aarch64__ ) +#if !defined( _rotr ) && defined( COMPILER_GCC ) && !defined( __arm__ ) && !defined( __aarch64__ ) && !defined( __riscv ) #include #endif diff --git a/public/steam/steamtypes.h b/public/steam/steamtypes.h index c32150e2b1..a443031166 100644 --- a/public/steam/steamtypes.h +++ b/public/steam/steamtypes.h @@ -24,7 +24,7 @@ typedef unsigned char uint8; #define POSIX 1 #endif -#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) +#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64) #define X64BITS #endif diff --git a/public/tier0/platform.h b/public/tier0/platform.h index 73047e1bfb..ea14c1a57a 100644 --- a/public/tier0/platform.h +++ b/public/tier0/platform.h @@ -9,7 +9,11 @@ #ifndef PLATFORM_H #define PLATFORM_H -#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) +#if defined(__riscv) && (__riscv_xlen == 64) +#define PLATFORM_RISCV64 1 +#endif + +#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) || defined(PLATFORM_RISCV64) #define PLATFORM_64BITS 1 #endif @@ -455,7 +459,7 @@ typedef void * HINSTANCE; // On OSX, SIGTRAP doesn't really stop the thread cold when debugging. // So if being debugged, use INT3 which is precise. #if defined(OSX) || defined(PLATFORM_BSD) -# if defined(__arm__) || defined(__aarch64__) +# if defined(__arm__) || defined(__aarch64__) || defined(PLATFORM_RISCV64) # ifdef __clang__ # define DebuggerBreak() do { if ( Plat_IsInDebugSession() ) { __builtin_debugtrap(); } else { raise(SIGTRAP); } } while(0) # elif defined __GNUC__ @@ -898,7 +902,7 @@ static FORCEINLINE double fsel(double fComparand, double fValGE, double fLT) #endif #endif -#elif defined (__arm__) || defined (__aarch64__) +#elif defined (__arm__) || defined (__aarch64__) || defined(PLATFORM_RISCV64) inline void SetupFPUControlWord() {} #else inline void SetupFPUControlWord() @@ -1069,7 +1073,7 @@ inline T QWordSwapC( T dw ) // The typically used methods. //------------------------------------- -#if (defined(__i386__) || defined(__amd64__) || defined(__arm__) || defined(__aarch64__)) && !defined(VALVE_LITTLE_ENDIAN) +#if (defined(__i386__) || defined(__amd64__) || defined(__arm__) || defined(__aarch64__) || defined(PLATFORM_RISCV64)) && !defined(VALVE_LITTLE_ENDIAN) #define VALVE_LITTLE_ENDIAN 1 #endif @@ -1235,7 +1239,7 @@ PLATFORM_INTERFACE struct tm * Plat_localtime( const time_t *timep, struct tm * inline uint64 Plat_Rdtsc() { -#if (defined( __arm__ ) || defined( __aarch64__ )) && defined (POSIX) +#if (defined( __arm__ ) || defined( __aarch64__ ) || defined( PLATFORM_RISCV64 )) && defined (POSIX) struct timespec t; clock_gettime( CLOCK_REALTIME, &t); return t.tv_sec * 1000000000ULL + t.tv_nsec; diff --git a/public/tier0/tslist.h b/public/tier0/tslist.h index f5661258a9..f6bf3a8b88 100644 --- a/public/tier0/tslist.h +++ b/public/tier0/tslist.h @@ -53,7 +53,13 @@ inline bool ThreadInterlockedAssignIf128( int128 volatile * pDest, const int128 // We do not want the original comparand modified by the swap // so operate on a local copy. int128 local_comparand = comparand; +#if defined( __riscv ) + // RISC-V (without Zacas) has no 128-bit CAS instruction, so there is no + // __sync_bool_compare_and_swap_16. libatomic provides a (lock-based) 16-byte CAS. + return __atomic_compare_exchange_n( pDest, &local_comparand, value, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ); +#else return __sync_bool_compare_and_swap( pDest, local_comparand, value ); +#endif } #endif diff --git a/scripts/build-debian-riscv64.sh b/scripts/build-debian-riscv64.sh new file mode 100755 index 0000000000..298d89a2b5 --- /dev/null +++ b/scripts/build-debian-riscv64.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Native build on Linux riscv64 (RV64GC), e.g. Debian 13 (trixie). + +git submodule init && git submodule update +sudo apt-get update +sudo apt-get install -y g++ python-is-python3 pkg-config libsimde-dev libopenal-dev libpng-dev libjpeg-dev libfreetype-dev libfontconfig-dev libcurl4-gnutls-dev libsdl2-dev zlib1g-dev libbz2-dev libedit-dev libgl-dev + +./waf configure -T debug --disable-warns $* && +./waf build diff --git a/tier0/cpu.cpp b/tier0/cpu.cpp index 0a1d84be0c..16ea7791d0 100644 --- a/tier0/cpu.cpp +++ b/tier0/cpu.cpp @@ -22,7 +22,7 @@ const tchar* GetProcessorVendorId(); static bool cpuid(uint32 function, uint32& out_eax, uint32& out_ebx, uint32& out_ecx, uint32& out_edx) { -#if defined (__arm__) || defined (__aarch64__) || defined( _X360 ) +#if defined (__arm__) || defined (__aarch64__) || defined( __riscv ) || defined( _X360 ) return false; #elif defined(GNUC) @@ -337,6 +337,8 @@ const tchar* GetProcessorVendorId() return "PPC"; #elif defined ( __arm__ ) return "ARM"; +#elif defined ( __riscv ) + return "RISC-V"; #else uint32 unused, VendorIDRegisters[3]; @@ -376,6 +378,8 @@ const tchar* GetProcessorArchName() return "aarch64"; #elif defined __arm__ || defined _M_ARM return "arm"; +#elif defined(__riscv) && (__riscv_xlen == 64) + return "riscv64"; #else #error "Unknown architecture" #endif diff --git a/tier0/cpu_posix.cpp b/tier0/cpu_posix.cpp index a86f40a77b..359cc7f05c 100644 --- a/tier0/cpu_posix.cpp +++ b/tier0/cpu_posix.cpp @@ -124,7 +124,7 @@ uint64 CalculateCPUFreq() } } -#if !defined(__arm__) && !defined(__aarch64__) +#if !defined(__arm__) && !defined(__aarch64__) && !defined(__riscv) // fallback mechanism to calculate when failed // Compute the period. Loop until we get 3 consecutive periods that // are the same to within a small error. The error is chosen diff --git a/tier1/pathmatch.cpp b/tier1/pathmatch.cpp index 8e439dfe7d..f545ed3772 100644 --- a/tier1/pathmatch.cpp +++ b/tier1/pathmatch.cpp @@ -71,7 +71,7 @@ static bool s_bShowDiag; #ifdef POSIX #include #define DEBUG_BREAK() raise(SIGINT) -#elif !defined (__arm__) +#elif !defined (__arm__) && !defined(__aarch64__) && !defined(__riscv) #define DEBUG_BREAK() __asm__ __volatile__ ( "int $3" ) #else #define DEBUG_BREAK() diff --git a/tier1/processor_detect_linux.cpp b/tier1/processor_detect_linux.cpp index 64d771a248..472f1ab96a 100644 --- a/tier1/processor_detect_linux.cpp +++ b/tier1/processor_detect_linux.cpp @@ -13,7 +13,7 @@ bool CheckMMXTechnology(void) { return false; } bool CheckSSETechnology(void) { return false; } bool CheckSSE2Technology(void) { return false; } bool Check3DNowTechnology(void) { return false; } -#elif defined (__arm__) || defined (__aarch64__) +#elif defined (__arm__) || defined (__aarch64__) || defined(__riscv) bool CheckMMXTechnology(void) { return false; } bool CheckSSETechnology(void) { return false; } bool CheckSSE2Technology(void) { return false; } diff --git a/tier1/reliabletimer.cpp b/tier1/reliabletimer.cpp index f575a67982..552c8ea49b 100644 --- a/tier1/reliabletimer.cpp +++ b/tier1/reliabletimer.cpp @@ -87,7 +87,7 @@ int64 CReliableTimer::GetPerformanceCountNow() uint64 ulNow; SYS_TIMEBASE_GET( ulNow ); return ulNow; -#elif (defined( __arm__ ) || defined( __aarch64__ )) && defined (POSIX) +#elif (defined( __arm__ ) || defined( __aarch64__ ) || defined( __riscv )) && defined (POSIX) struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return ts.tv_sec * 1000000000ULL + ts.tv_nsec; diff --git a/wscript b/wscript index 16f2263bd2..5636db97fd 100644 --- a/wscript +++ b/wscript @@ -523,7 +523,8 @@ def configure(conf): if conf.env.DEST_CPU in ['x86', 'x86_64']: flags += ['-mfpmath=sse'] - elif conf.env.DEST_CPU in ['arm', 'aarch64']: + elif conf.env.DEST_CPU in ['arm', 'aarch64', 'riscv']: + # char is unsigned by default on ARM and RISC-V, the code assumes x86 (signed) flags += ['-fsigned-char'] if conf.env.DEST_CPU == 'arm': @@ -532,6 +533,11 @@ def configure(conf): if conf.env.DEST_OS == 'freebsd': linkflags += ['-lexecinfo'] + if conf.env.DEST_CPU == 'riscv': + # 16-byte CAS (tier0/tslist.h) comes from libatomic on RISC-V. + # Added as LIB (not a linker flag) so it comes after the objects (--as-needed). + conf.env.append_unique('LIB', ['atomic']) + if conf.env.DEST_OS != 'win32': cflags += flags linkflags += flags