From b563de42bd0b75b9f895cf761fdf513c33ed74d7 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 16 May 2024 15:12:17 +0200 Subject: [PATCH 1/9] cmake: tell MSVC to properly report __cplusplus MSVC doesn't properly report __cplusplus by default to keep compatibility with broken legacy code: - https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ --- cmake/DaemonFlags.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/DaemonFlags.cmake b/cmake/DaemonFlags.cmake index cface099a9..adba8ee0b8 100644 --- a/cmake/DaemonFlags.cmake +++ b/cmake/DaemonFlags.cmake @@ -133,6 +133,9 @@ if (MSVC) set_c_cxx_flag("/fp:fast") set_c_cxx_flag("/d2Zi+" RELWITHDEBINFO) + # https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ + set_cxx_flag("/Zc:__cplusplus") + # At least Ninja doesn't remove the /W3 flag when we add /W4|/Wall one, which # leads to compilation warnings. Remove /W3 entirely, as /W4|/Wall be used. foreach(flag_var From 1191ef9141e14e211f1d622829e625aa026dd54b Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 16 May 2024 11:34:53 +0200 Subject: [PATCH 2/9] common/Compiler: define NOEXCEPT and CONSTEXPR according to C++ version --- src/common/Compiler.h | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/common/Compiler.h b/src/common/Compiler.h index 6b8fc45940..d6bf910d5f 100644 --- a/src/common/Compiler.h +++ b/src/common/Compiler.h @@ -91,15 +91,6 @@ int CountTrailingZeroes(unsigned long long x); #define BREAKPOINT() #endif -// noexcept keyword, this should be used on all move constructors and move -// assignments so that containers move objects instead of copying them. -#define NOEXCEPT noexcept -#define NOEXCEPT_IF(x) noexcept(x) -#define NOEXCEPT_EXPR(x) noexcept(x) - -// Work around lack of constexpr -#define CONSTEXPR constexpr - #if defined(__SANITIZE_ADDRESS__) // Detects GCC and MSVC AddressSanitizer # define USING_ADDRESS_SANITIZER # define USING_SANITIZER @@ -150,10 +141,6 @@ inline int CountTrailingZeroes(unsigned long long x) { return __builtin_ctzll(x) #define DLLEXPORT __declspec(dllexport) #define DLLIMPORT __declspec(dllimport) #define BREAKPOINT() __debugbreak() -#define NOEXCEPT noexcept -#define NOEXCEPT_IF(x) noexcept(x) -#define NOEXCEPT_EXPR(x) noexcept(x) -#define CONSTEXPR constexpr #define ATTRIBUTE_NO_SANITIZE_ADDRESS inline int CountTrailingZeroes(unsigned int x) { unsigned long ans; _BitScanForward(&ans, x); return ans; } @@ -201,6 +188,26 @@ inline int CountTrailingZeroes(unsigned long long x) { int i = 0; while (i < 64 # define ALIGN_STACK_FOR_MINGW #endif +/* The noexcept keyword should be used on all move constructors and move +assignments so that containers move objects instead of copying them. +The noexcept keyword was added in C++11 but the __cpp_noexcept_function_type +definition to detect its implementation was only added in C++17. */ +#if defined(__cpp_noexcept_function_type) || __cplusplus >= 201103 + #define NOEXCEPT noexcept + #define NOEXCEPT_IF(x) noexcept(x) + #define NOEXCEPT_EXPR(x) noexcept(x) +#else + #define NOEXCEPT + #define NOEXCEPT_IF(x) + #define NOEXCEPT_EXPR(x) x +#endif + +#if defined(__cpp_constexpr) + #define CONSTEXPR constexpr +#else + #define CONSTEXPR +#endif + // Uses SD-6 Feature Test Recommendations #ifdef __cpp_constexpr # if __cpp_constexpr >= 201304 From a68219a49c96b4f22d51ded0a05c95ddf43bf6ed Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 16 May 2024 13:03:50 +0200 Subject: [PATCH 3/9] common/Compiler: move compiler intrinsics code and group compiler customization code - move code using compiler intrinsics to a dedicated place - group compiler customization code --- src/common/Compiler.h | 152 ++++++++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 64 deletions(-) diff --git a/src/common/Compiler.h b/src/common/Compiler.h index d6bf910d5f..9b4df2ab2f 100644 --- a/src/common/Compiler.h +++ b/src/common/Compiler.h @@ -33,14 +33,61 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef COMMON_COMPILER_H_ #define COMMON_COMPILER_H_ -// CountTrailingZeroes returns the number of trailing zeroes of the argument in binary. -// The result is unspecified if the input is 0. -int CountTrailingZeroes(unsigned int x); -int CountTrailingZeroes(unsigned long x); -int CountTrailingZeroes(unsigned long long x); +// Code making use of compiler intrinsics. + +/* CountTrailingZeroes returns the number of +trailing zeroes of the argument in binary. +The result is unspecified if the input is 0. */ +#if defined(__GNUC__) +inline int CountTrailingZeroes(unsigned int x) +{ + return __builtin_ctz(x); +} +inline int CountTrailingZeroes(unsigned long x) +{ + return __builtin_ctzl(x); +} +inline int CountTrailingZeroes(unsigned long long x) +{ + return __builtin_ctzll(x); +} +#elif defined(_MSC_VER) +inline int CountTrailingZeroes(unsigned int x) +{ + unsigned long ans; _BitScanForward(&ans, x); return ans; +} +inline int CountTrailingZeroes(unsigned long x) +{ + unsigned long ans; _BitScanForward(&ans, x); return ans; +} +inline int CountTrailingZeroes(unsigned long long x) +{ + unsigned long ans; + #ifdef _WIN64 + _BitScanForward64(&ans, x); return ans; + #else + bool nonzero = _BitScanForward(&ans, static_cast(x)); + if (!nonzero) { _BitScanForward(&ans, x >> 32); } + #endif + return ans; +} +#else +inline int CountTrailingZeroes(unsigned int x) +{ + int i = 0; while (i < 32 && !(x & 1)) { ++i; x >>= 1; } return i; +} +inline int CountTrailingZeroes(unsigned long x) +{ + int i = 0; while (i < 64 && !(x & 1)) { ++i; x >>= 1; } return i; +} +inline int CountTrailingZeroes(unsigned long long x) +{ + int i = 0; while (i < 64 && !(x & 1)) { ++i; x >>= 1; } return i; +} +#endif // GCC and Clang -#ifdef __GNUC__ +#if defined(__GNUC__) // Emit a nice warning when a function is used #define DEPRECATED __attribute__((__deprecated__)) @@ -91,6 +138,19 @@ int CountTrailingZeroes(unsigned long long x); #define BREAKPOINT() #endif +/* Compiler can be fooled when calling ASSERT_UNREACHABLE() macro at end of non-void function. +In this case, compiler is complaining because control reaches end of non-void function, +even if the execution flow is expected to be taken down by assert before. + +That's why we use these compiler specific unreachable builtin on modern compilers, +ASSERT_UNREACHABLE() macro makes use of this UNREACHABLE() macro, preventing useless warnings. +Unsupported compilers will raise "control reaches end of non-void function" warnings but +that's not a big issue and that's likely to never happen (these compilers would be too old and +would lack too much features to compile Daemon anyway). + +See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf */ +#define UNREACHABLE() __builtin_unreachable() + #if defined(__SANITIZE_ADDRESS__) // Detects GCC and MSVC AddressSanitizer # define USING_ADDRESS_SANITIZER # define USING_SANITIZER @@ -116,12 +176,17 @@ int CountTrailingZeroes(unsigned long long x); # define ATTRIBUTE_NO_SANITIZE_ADDRESS #endif -inline int CountTrailingZeroes(unsigned int x) { return __builtin_ctz(x); } -inline int CountTrailingZeroes(unsigned long x) { return __builtin_ctzl(x); } -inline int CountTrailingZeroes(unsigned long long x) { return __builtin_ctzll(x); } +// The new -Wimplicit-fallthrough warning... +#if defined(__clang__) && __clang_major__ >= 6 + #define DAEMON_FALLTHROUGH [[clang::fallthrough]] +#elif __GNUC__ >= 7 + #define DAEMON_FALLTHROUGH [[gnu::fallthrough]] +#else + #define DAEMON_FALLTHROUGH +#endif // Microsoft Visual C++ -#elif defined( _MSC_VER ) +#elif defined(_MSC_VER) // See descriptions above #define DEPRECATED __declspec(deprecated) @@ -141,23 +206,9 @@ inline int CountTrailingZeroes(unsigned long long x) { return __builtin_ctzll(x) #define DLLEXPORT __declspec(dllexport) #define DLLIMPORT __declspec(dllimport) #define BREAKPOINT() __debugbreak() +#define UNREACHABLE() __assume(0) #define ATTRIBUTE_NO_SANITIZE_ADDRESS - -inline int CountTrailingZeroes(unsigned int x) { unsigned long ans; _BitScanForward(&ans, x); return ans; } -inline int CountTrailingZeroes(unsigned long x) { unsigned long ans; _BitScanForward(&ans, x); return ans; } -#ifdef _WIN64 -inline int CountTrailingZeroes(unsigned long long x) { unsigned long ans; _BitScanForward64(&ans, x); return ans; } -#else -inline int CountTrailingZeroes(unsigned long long x) -{ - unsigned long ans; - bool nonzero = _BitScanForward(&ans, static_cast(x)); - if (!nonzero) { - _BitScanForward(&ans, x >> 32); - } - return ans; -} -#endif +#define DAEMON_FALLTHROUGH // Other compilers, unsupported #else @@ -174,19 +225,12 @@ inline int CountTrailingZeroes(unsigned long long x) #define DLLEXPORT #define DLLIMPORT #define BREAKPOINT() - -inline int CountTrailingZeroes(unsigned int x) { int i = 0; while (i < 32 && !(x & 1)) { ++i; x >>= 1; } return i; } -inline int CountTrailingZeroes(unsigned long x) { int i = 0; while (i < 64 && !(x & 1)) { ++i; x >>= 1; } return i; } -inline int CountTrailingZeroes(unsigned long long x) { int i = 0; while (i < 64 && !(x & 1)) { ++i; x >>= 1; } return i; } +#define UNREACHABLE() +#define ATTRIBUTE_NO_SANITIZE_ADDRESS +#define DAEMON_FALLTHROUGH #endif -#if defined(__MINGW32__) && defined(__i386__) -// On x86, GCC expects 16-byte stack alignment (used for SSE instructions), but MSVC only uses 4-byte alignment. -// Therefore the stack needs to be adjusted whenever MSVC code calls into GCC code. -# define ALIGN_STACK_FOR_MINGW __attribute__((force_align_arg_pointer)) -#else -# define ALIGN_STACK_FOR_MINGW -#endif +// Keywords specific to C++ versions /* The noexcept keyword should be used on all move constructors and move assignments so that containers move objects instead of copying them. @@ -222,36 +266,16 @@ definition to detect its implementation was only added in C++17. */ # define CONSTEXPR_FUNCTION_RELAXED #endif -// The new -Wimplicit-fallthrough warning... -#if defined(__clang__) && __clang_major__ >= 6 -# define DAEMON_FALLTHROUGH [[clang::fallthrough]] -#elif __GNUC__ >= 7 -# define DAEMON_FALLTHROUGH [[gnu::fallthrough]] +// Compiler specificities we can't disable. + +#if defined(__MINGW32__) && defined(__i386__) +// On x86, GCC expects 16-byte stack alignment (used for SSE instructions), but MSVC only uses 4-byte alignment. +// Therefore the stack needs to be adjusted whenever MSVC code calls into GCC code. + #define ALIGN_STACK_FOR_MINGW __attribute__((force_align_arg_pointer)) #else -# define DAEMON_FALLTHROUGH + #define ALIGN_STACK_FOR_MINGW #endif -/* Compiler can be fooled when calling ASSERT_UNREACHABLE() macro at end of non-void function. - * In this case, compiler is complaining because control reaches end of non-void function, - * even if the execution flow is expected to be taken down by assert before. - * - * That's why we use these compiler specific unreachable builtin on modern compilers, - * ASSERT_UNREACHABLE() macro makes use of this UNREACHABLE() macro, preventing useless warnings. - * Unsupported compilers will raise "control reaches end of non-void function" warnings but - * that's not a big issue and that's likely to never happen (these compilers would be too old and - * would lack too much features to compile Daemon anyway). - * - * See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf -*/ -#if defined(_MSC_VER) //UNREACHABLE - #define UNREACHABLE() __assume(0) -// All of gcc, clang and icc define __GNUC__ -#elif defined(__GNUC__) - #define UNREACHABLE() __builtin_unreachable() -#else // UNREACHABLE - #define UNREACHABLE() -#endif // UNREACHABLE - /* Use a C++11 braced initializer on MSVC instead of a bracket initializer when zeroing a struct. This works around a bug in how MSVC generates implicit default constructors. -- Amanieu From c748defa1eb413abf7a6d9c23d353e2ca348ef20 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 16 May 2024 15:46:36 +0200 Subject: [PATCH 4/9] common/Compiler: move sanitizer detection to a place it can detect MSVC sanitizer --- src/common/Compiler.h | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/common/Compiler.h b/src/common/Compiler.h index 9b4df2ab2f..1772c447db 100644 --- a/src/common/Compiler.h +++ b/src/common/Compiler.h @@ -86,6 +86,26 @@ inline int CountTrailingZeroes(unsigned long long x) } #endif +// Sanitizer detection + +#if defined(__SANITIZE_ADDRESS__) // Detects GCC and MSVC AddressSanitizer + #define USING_ADDRESS_SANITIZER + #define USING_SANITIZER +#elif defined(__SANITIZE_THREAD__) // Detects GCC ThreadSanitizer + #define USING_SANITIZER +#elif defined(__has_feature) + #if __has_feature(address_sanitizer) // Detects Clang AddressSanitizer + #define USING_ADDRESS_SANITIZER + #define USING_SANITIZER + #elif __has_feature(leak_sanitizer) // Detects Clang LeakSanitizer + #define USING_SANITIZER + #elif __has_feature(memory_sanitizer) // Detects Clang MemorySanitizer + #define USING_SANITIZER + #elif __has_feature(thread_sanitizer) // Detects Clang ThreadSanitizer + #define USING_SANITIZER + #endif +#endif + // GCC and Clang #if defined(__GNUC__) @@ -151,24 +171,6 @@ would lack too much features to compile Daemon anyway). See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf */ #define UNREACHABLE() __builtin_unreachable() -#if defined(__SANITIZE_ADDRESS__) // Detects GCC and MSVC AddressSanitizer -# define USING_ADDRESS_SANITIZER -# define USING_SANITIZER -#elif defined(__SANITIZE_THREAD__) // Detects GCC ThreadSanitizer -# define USING_SANITIZER -#elif defined(__has_feature) -# if __has_feature(address_sanitizer) // Detects Clang AddressSanitizer -# define USING_ADDRESS_SANITIZER -# define USING_SANITIZER -# elif __has_feature(leak_sanitizer) // Detects Clang LeakSanitizer -# define USING_SANITIZER -# elif __has_feature(memory_sanitizer) // Detects Clang MemorySanitizer -# define USING_SANITIZER -# elif __has_feature(thread_sanitizer) // Detects Clang ThreadSanitizer -# define USING_SANITIZER -# endif -#endif - // To mark functions which cause issues with address sanitizer #ifdef USING_ADDRESS_SANITIZER # define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) From ef9e3f26bf25d9dff8dd21f5fedf3e3be727ae33 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Wed, 15 May 2024 08:31:19 +0200 Subject: [PATCH 5/9] cmake,common/Platform: add USE_ARCH_INTRINSICS CMake option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces the USE_ARCH_INTRINSICS CMake option. It is enabled by default. Disabling it is meant to disable custom asm code and usage of intrinsincs functions for the target platform in the Dæmon code base, it may also be used by games built with the Dæmon common code base. It is not meant to disable asm or intrinsincs usage in third-party libraries. It is not meant to prevent the compiler to use such intrinsics in its optimization passes. It is not meant to disable the compiler flags we set to tell the compiler to try to use such intrinsics in its optimization passes. For this, one should disable USE_CPU_RECOMMENDED_FEATURES instead. For obvious reason the asm code in the BREAKPOINT() implementation is not meant to be disabled by USE_ARCH_INTRINSICS. The macro syntax is: DAEMON_ARCH_INTRINSICS_(architecture)[_extension] Examples: - DAEMON_ARCH_INTRINSICS_i686: i686 specific code, including asm code. - DAEMON_ARCH_INTRINSICS_i686_sse: i686 SSE specific code. - DAEMON_ARCH_INTRINSICS_i686_sse2: i686 SSE2 specific code. If a platform inherits feature from an parent platform, the parent platform name is used. For example on amd64, the definition enabling SSE code is DAEMON_ARCH_INTRINSICS_i686_sse, enabling SSE code on both i686 with SSE and amd64 platforms. and both DAEMON_ARCH_INTRINSICS_amd64 and DAEMON_ARCH_INTRINSICS_i686 are available. --- cmake/DaemonArchitecture.cmake | 25 +++++++++++ src/common/Compiler.h | 1 + src/common/Platform.h | 77 ++++++++++++++++++++++++++++++---- src/engine/qcommon/q_math.cpp | 7 ++-- src/engine/qcommon/q_shared.h | 7 ++-- 5 files changed, 102 insertions(+), 15 deletions(-) diff --git a/cmake/DaemonArchitecture.cmake b/cmake/DaemonArchitecture.cmake index 662343ee7a..d379d9c14e 100644 --- a/cmake/DaemonArchitecture.cmake +++ b/cmake/DaemonArchitecture.cmake @@ -90,3 +90,28 @@ endif() # Quotes cannot be part of the define as support for them is not reliable. add_definitions(-DNACL_ARCH_STRING=${NACL_ARCH}) + +option(USE_ARCH_INTRINSICS "Enable custom code using intrinsics functions or asm declarations" ON) +mark_as_advanced(USE_ARCH_INTRINSICS) + +macro(set_arch_intrinsics name) + if (USE_ARCH_INTRINSICS) + message(STATUS "Enabling ${name} architecture intrinsics") + add_definitions(-DDAEMON_USE_ARCH_INTRINSICS_${name}=1) + else() + message(STATUS "Disabling ${name} architecture intrinsics") + endif() +endmacro() + +if (USE_ARCH_INTRINSICS) + add_definitions(-DDAEMON_USE_ARCH_INTRINSICS=1) +endif() + +set_arch_intrinsics(${ARCH}) + +set(amd64_PARENT "i686") +set(arm64_PARENT "armhf") + +if (${ARCH}_PARENT) + set_arch_intrinsics(${${ARCH}_PARENT}) +endif() diff --git a/src/common/Compiler.h b/src/common/Compiler.h index 1772c447db..a5a43aa90e 100644 --- a/src/common/Compiler.h +++ b/src/common/Compiler.h @@ -148,6 +148,7 @@ inline int CountTrailingZeroes(unsigned long long x) // Raise an exception and break in the debugger #if defined(DAEMON_ARCH_i686) || defined(DAEMON_ARCH_amd64) + // Always run this asm code even if DAEMON_USE_ARCH_INTRINSICS is not defined. #define BREAKPOINT() __asm__ __volatile__("int $3\n\t") #elif defined(DAEMON_ARCH_nacl) // TODO: find how to implement breakpoint on NaCl diff --git a/src/common/Platform.h b/src/common/Platform.h index 9ccef8df7e..13269a35c7 100644 --- a/src/common/Platform.h +++ b/src/common/Platform.h @@ -63,15 +63,74 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define __x86_64__ 1 #endif -// SSE support -#if defined(__x86_64__) || defined(__SSE__) || _M_IX86_FP >= 1 -#include -#if defined(__x86_64__) || defined(__SSE2__) || _M_IX86_FP >= 2 -#include -#define idx86_sse 2 -#else -#define idx86_sse 1 -#endif +/* The definition name syntax is: DAEMON_USE_ARCH_INTRINSICS_[_extension] + +Examples: + +- DAEMON_USE_ARCH_INTRINSICS_i686: i686 specific code, including asm code. +- DAEMON_USE_ARCH_INTRINSICS_i686_sse: i686 SSE specific code. +- DAEMON_USE_ARCH_INTRINSICS_i686_sse2: i686 SSE2 specific code. + +If a architecture inherits a feature from an parent architecture, the parent +architecture name is used. For example on amd64, the definition enabling +SSE code is DAEMON_USE_ARCH_INTRINSICS_i686_sse, enabling SSE code on both +i686 with SSE and amd64. + +The definitions for the architecture itself are automatically set by CMake. */ + +#if defined(DAEMON_USE_ARCH_INTRINSICS) + // Set architecture extensions definitions. + + #if defined(_MSC_VER) + /* Detect MSVC providing SSE and SSE2. + + MSVC doesn't set __SSE*__, and only sets _M_IX86_FP on i686. + We should look for _M_AMD64 or _M_X64 to know if SSE and SSE2 + are enabled when building code for amd64. Beware that _M_AMD64 + and _M_X64 are also enabled when building for ARM64EC: + + > - _M_AMD64 Defined as the integer literal value 100 for compilations + > that target x64 processors or ARM64EC. Otherwise, undefined. + > - _M_X64 Defined as the integer literal value 100 for compilations + > that target x64 processors or ARM64EC. Otherwise, undefined. + > - _M_ARM64EC Defined as 1 for compilations that target ARM64EC. + > Otherwise, undefined. + > -- https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 + + It is unclear if xmmintrin.h is available on ARM64EC. */ + + #if defined(_M_IX86_FP) + #if _M_IX86_FP >= 2 + #define DAEMON_USE_ARCH_INTRINSICS_i686_sse + #define DAEMON_USE_ARCH_INTRINSICS_i686_sse2 + #elif _M_IX86_FP == 1 + #define DAEMON_USE_ARCH_INTRINSICS_i686_sse + #endif + #elif defined(_M_AMD64) || defined(_M_X64) + #if !defined(_M_ARM64EC) + #define DAEMON_USE_ARCH_INTRINSICS_i686_sse + #define DAEMON_USE_ARCH_INTRINSICS_i686_sse2 + #endif + #endif + #else + #if defined(__SSE__) || defined(MSVC_SSE) + #define DAEMON_USE_ARCH_INTRINSICS_i686_sse + #endif + + #if defined(__SSE2__) || defined(MSVC_SSE2) + #define DAEMON_USE_ARCH_INTRINSICS_i686_sse2 + #endif + #endif + + // Include intrinsics-specific headers. + + #if defined(DAEMON_USE_ARCH_INTRINSICS_i686_sse) + #include + #endif + + #if defined(DAEMON_USE_ARCH_INTRINSICS_i686_sse2) + #include + #endif #endif // VM Prefixes diff --git a/src/engine/qcommon/q_math.cpp b/src/engine/qcommon/q_math.cpp index 511aad85ed..9c667d2477 100644 --- a/src/engine/qcommon/q_math.cpp +++ b/src/engine/qcommon/q_math.cpp @@ -743,7 +743,7 @@ void SetPlaneSignbits( cplane_t *out ) int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const cplane_t *p ) { -#if idx86_sse +#if defined(DAEMON_USE_ARCH_INTRINSICS_i686_sse) auto mins = sseLoadVec3Unsafe( emins ); auto maxs = sseLoadVec3Unsafe( emaxs ); auto normal = sseLoadVec3Unsafe( p->normal ); @@ -1802,7 +1802,7 @@ void MatrixSetupShear( matrix_t m, vec_t x, vec_t y ) void MatrixMultiply( const matrix_t a, const matrix_t b, matrix_t out ) { -#if idx86_sse +#if defined(DAEMON_USE_ARCH_INTRINSICS_i686_sse) //#error MatrixMultiply int i; __m128 _t0, _t1, _t2, _t3, _t4, _t5, _t6, _t7; @@ -3291,7 +3291,8 @@ void QuatTransformVectorInverse( const quat_t q, const vec3_t in, vec3_t out ) VectorAdd( out, tmp2, out ); } -#if !idx86_sse +// The SSE variants are inline functions in q_shared.h file. +#if !defined(DAEMON_USE_ARCH_INTRINSICS_i686_sse) // create an identity transform void TransInit( transform_t *t ) { diff --git a/src/engine/qcommon/q_shared.h b/src/engine/qcommon/q_shared.h index e1ec6ac5eb..1ab3217870 100644 --- a/src/engine/qcommon/q_shared.h +++ b/src/engine/qcommon/q_shared.h @@ -241,7 +241,7 @@ void Com_Free_Aligned( void *ptr ); // floats (quat: 4, scale: 1, translation: 3), which is very // convenient for SSE and GLSL, which operate on 4-dimensional // float vectors. -#if idx86_sse +#if defined(DAEMON_USE_ARCH_INTRINSICS_i686_sse) // Here we have a union of scalar struct and sse struct, transform_u and the // scalar struct must match transform_t so we have to use anonymous structs. // We disable compiler warnings when using -Wpedantic for this specific case. @@ -348,7 +348,7 @@ extern const quat_t quatIdentity; float y; // compute approximate inverse square root -#if defined( idx86_sse ) +#if defined(DAEMON_USE_ARCH_INTRINSICS_i686_sse) // SSE rsqrt relative error bound: 3.7 * 10^-4 _mm_store_ss( &y, _mm_rsqrt_ss( _mm_load_ss( &number ) ) ); #else @@ -831,7 +831,7 @@ void SnapVector( V &&v ) //============================================= // combining Transformations -#if idx86_sse +#if defined(DAEMON_USE_ARCH_INTRINSICS_i686_sse) /* swizzles for _mm_shuffle_ps instruction */ #define SWZ_XXXX 0x00 #define SWZ_YXXX 0x01 @@ -1350,6 +1350,7 @@ void SnapVector( V &&v ) t->sseRot = sseQuatNormalize( t->sseRot ); } #else + // The non-SSE variants are in q_math.cpp file. void TransInit( transform_t *t ); void TransCopy( const transform_t *in, transform_t *out ); From 98d81e7db270f524a0ca5e5cb86ac546fd831133 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 16 May 2024 17:14:49 +0200 Subject: [PATCH 6/9] cmake,common/Compiler: add USE_COMPILER_INTRINSICS and USE_COMPILER_CUSTOMIZATION CMake options This introduces USE_COMPILER_INTRINSICS and USE_COMPILER_CUSTOMIZATION CMake options. They are enabled by default. Disabling USE_COMPILER_INTRINSICS is meant to test generic fallback implementations. Disabling USE_COMPILER_CUSTOMIZATION is meant to test the unknown compiler fallback definitions. --- cmake/DaemonFlags.cmake | 20 ++++++++++++++++++++ src/common/Compiler.h | 13 +++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/cmake/DaemonFlags.cmake b/cmake/DaemonFlags.cmake index adba8ee0b8..b8b10a0336 100644 --- a/cmake/DaemonFlags.cmake +++ b/cmake/DaemonFlags.cmake @@ -29,6 +29,26 @@ include(CheckCXXCompilerFlag) add_definitions(-DDAEMON_BUILD_${CMAKE_BUILD_TYPE}) +option(USE_COMPILER_INTRINSICS "Enable usage of compiler intrinsics" ON) +mark_as_advanced(USE_COMPILER_INTRINSICS) + +if (USE_COMPILER_INTRINSICS) + add_definitions(-DDAEMON_USE_COMPILER_INTRINSICS=1) + message(STATUS "Enabling compiler intrinsics") +else() + message(STATUS "Disabling compiler intrinsics") +endif() + +option(USE_COMPILER_CUSTOMIZATION "Enable usage of compiler custom attributes and operators" ON) +mark_as_advanced(USE_COMPILER_CUSTOMIZATION) + +if (USE_COMPILER_CUSTOMIZATION) + add_definitions(-DDAEMON_USE_COMPILER_CUSTOMIZATION=1) + message(STATUS "Enabling compiler custom attributes and operators") +else() + message(STATUS "Disabling compiler custom attributes and operators") +endif() + # Set flag without checking, optional argument specifies build type macro(set_c_flag FLAG) if (${ARGC} GREATER 1) diff --git a/src/common/Compiler.h b/src/common/Compiler.h index a5a43aa90e..ffb135d38e 100644 --- a/src/common/Compiler.h +++ b/src/common/Compiler.h @@ -38,7 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* CountTrailingZeroes returns the number of trailing zeroes of the argument in binary. The result is unspecified if the input is 0. */ -#if defined(__GNUC__) +#if defined(DAEMON_USE_COMPILER_INTRINSICS) && defined(__GNUC__) inline int CountTrailingZeroes(unsigned int x) { return __builtin_ctz(x); @@ -51,7 +51,7 @@ inline int CountTrailingZeroes(unsigned long long x) { return __builtin_ctzll(x); } -#elif defined(_MSC_VER) +#elif defined(DAEMON_USE_COMPILER_INTRINSICS) && defined(_MSC_VER) inline int CountTrailingZeroes(unsigned int x) { unsigned long ans; _BitScanForward(&ans, x); return ans; @@ -106,8 +106,8 @@ inline int CountTrailingZeroes(unsigned long long x) #endif #endif -// GCC and Clang -#if defined(__GNUC__) +// GCC and Clang attribute and operator customization. +#if defined(DAEMON_USE_COMPILER_CUSTOMIZATION) && defined(__GNUC__) // Emit a nice warning when a function is used #define DEPRECATED __attribute__((__deprecated__)) @@ -188,8 +188,8 @@ See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf */ #define DAEMON_FALLTHROUGH #endif -// Microsoft Visual C++ -#elif defined(_MSC_VER) +// Microsoft Visual C++ attribute and operator customization. +#elif defined(DAEMON_USE_COMPILER_CUSTOMIZATION) && defined(_MSC_VER) // See descriptions above #define DEPRECATED __declspec(deprecated) @@ -220,6 +220,7 @@ See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf */ #define WARN_UNUSED_RESULT #define COLD #define NORETURN +#define NORETURN_PTR #define PRINTF_LIKE(n) #define VPRINTF_LIKE(n) #define PRINTF_TRANSLATE_ARG(a) From be4b6cf34459a0082c095dacadde9c7666417a53 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 27 May 2024 15:28:45 +0200 Subject: [PATCH 7/9] common/Compiler: only define language keyword definitions if undefined --- src/common/Compiler.h | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/common/Compiler.h b/src/common/Compiler.h index ffb135d38e..11c12e617f 100644 --- a/src/common/Compiler.h +++ b/src/common/Compiler.h @@ -244,30 +244,35 @@ definition to detect its implementation was only added in C++17. */ #define NOEXCEPT noexcept #define NOEXCEPT_IF(x) noexcept(x) #define NOEXCEPT_EXPR(x) noexcept(x) -#else - #define NOEXCEPT - #define NOEXCEPT_IF(x) - #define NOEXCEPT_EXPR(x) x #endif +// Uses SD-6 Feature Test Recommendations #if defined(__cpp_constexpr) #define CONSTEXPR constexpr -#else - #define CONSTEXPR + #define CONSTEXPR_FUNCTION constexpr + #if __cpp_constexpr >= 201304 + #define CONSTEXPR_FUNCTION_RELAXED constexpr + #endif #endif -// Uses SD-6 Feature Test Recommendations -#ifdef __cpp_constexpr -# if __cpp_constexpr >= 201304 -# define CONSTEXPR_FUNCTION_RELAXED constexpr -# else -# define CONSTEXPR_FUNCTION_RELAXED -# endif -# define CONSTEXPR_FUNCTION constexpr -#else -// Work around lack of constexpr -# define CONSTEXPR_FUNCTION -# define CONSTEXPR_FUNCTION_RELAXED +// Work around lack of language keywords. +#if !defined(NOEXCEPT) + #define NOEXCEPT +#endif +#if !defined(NOEXCEPT_IF) + #define NOEXCEPT_IF(x) +#endif +#if !defined(NOEXCEPT_EXPR) + #define NOEXCEPT_EXPR(x) x +#endif +#if !defined(CONSTEXPR) + #define CONSTEXPR +#endif +#if !defined(CONSTEXPR_FUNCTION) + #define CONSTEXPR_FUNCTION +#endif +#if !defined(CONSTEXPR_FUNCTION_RELAXED) + #define CONSTEXPR_FUNCTION_RELAXED #endif // Compiler specificities we can't disable. From c98d5513373ab52657acee121442bc2caf7ef954 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Tue, 28 May 2024 12:24:04 +0200 Subject: [PATCH 8/9] common/Compiler: only define compiler customization definition if undefined --- src/common/Compiler.h | 98 ++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/src/common/Compiler.h b/src/common/Compiler.h index 11c12e617f..0213cdd554 100644 --- a/src/common/Compiler.h +++ b/src/common/Compiler.h @@ -134,9 +134,6 @@ inline int CountTrailingZeroes(unsigned long long x) // other pointer #define MALLOC_LIKE __attribute__((__malloc__)) -// Marks this function as memory allocator -#define ALLOCATOR - // Shared library function import/export #ifdef _WIN32 #define DLLEXPORT __attribute__((__dllexport__)) @@ -150,13 +147,6 @@ inline int CountTrailingZeroes(unsigned long long x) #if defined(DAEMON_ARCH_i686) || defined(DAEMON_ARCH_amd64) // Always run this asm code even if DAEMON_USE_ARCH_INTRINSICS is not defined. #define BREAKPOINT() __asm__ __volatile__("int $3\n\t") -#elif defined(DAEMON_ARCH_nacl) - // TODO: find how to implement breakpoint on NaCl - // Accept our fate, do not raise a warning. - #define BREAKPOINT() -#else - #warning BREAKPOINT is not implemented for this platform - #define BREAKPOINT() #endif /* Compiler can be fooled when calling ASSERT_UNREACHABLE() macro at end of non-void function. @@ -174,9 +164,7 @@ See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf */ // To mark functions which cause issues with address sanitizer #ifdef USING_ADDRESS_SANITIZER -# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) -#else -# define ATTRIBUTE_NO_SANITIZE_ADDRESS + #define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) #endif // The new -Wimplicit-fallthrough warning... @@ -184,8 +172,6 @@ See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf */ #define DAEMON_FALLTHROUGH [[clang::fallthrough]] #elif __GNUC__ >= 7 #define DAEMON_FALLTHROUGH [[gnu::fallthrough]] -#else - #define DAEMON_FALLTHROUGH #endif // Microsoft Visual C++ attribute and operator customization. @@ -194,44 +180,72 @@ See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf */ // See descriptions above #define DEPRECATED __declspec(deprecated) #define WARN_UNUSED_RESULT _Check_return_ -#define COLD #define NORETURN __declspec(noreturn) -#define NORETURN_PTR -#define PRINTF_LIKE(n) -#define VPRINTF_LIKE(n) -#define PRINTF_TRANSLATE_ARG(a) + +// Marks this function as memory allocator #if _MSC_VER >= 1900 && !defined( _CORECRT_BUILD ) -#define ALLOCATOR __declspec(allocator) -#else -#define ALLOCATOR + #define ALLOCATOR __declspec(allocator) #endif + #define MALLOC_LIKE ALLOCATOR __declspec(restrict) #define DLLEXPORT __declspec(dllexport) #define DLLIMPORT __declspec(dllimport) #define BREAKPOINT() __debugbreak() #define UNREACHABLE() __assume(0) -#define ATTRIBUTE_NO_SANITIZE_ADDRESS -#define DAEMON_FALLTHROUGH // Other compilers, unsupported #else -#warning "Unsupported compiler" -#define DEPRECATED -#define WARN_UNUSED_RESULT -#define COLD -#define NORETURN -#define NORETURN_PTR -#define PRINTF_LIKE(n) -#define VPRINTF_LIKE(n) -#define PRINTF_TRANSLATE_ARG(a) -#define MALLOC_LIKE -#define ALLOCATOR -#define DLLEXPORT -#define DLLIMPORT -#define BREAKPOINT() -#define UNREACHABLE() -#define ATTRIBUTE_NO_SANITIZE_ADDRESS -#define DAEMON_FALLTHROUGH + #warning "Unsupported compiler" +#endif + +// Work around lack of compiler customization. +#if !defined(DEPRECATED) + #define DEPRECATED +#endif +#if !defined(WARN_UNUSED_RESULT) + #define WARN_UNUSED_RESULT +#endif +#if !defined(COLD) + #define COLD +#endif +#if !defined(NORETURN) + #define NORETURN +#endif +#if !defined(NORETURN_PTR) + #define NORETURN_PTR +#endif +#if !defined(PRINTF_LIKE) + #define PRINTF_LIKE(n) +#endif +#if !defined(VPRINTF_LIKE) + #define VPRINTF_LIKE(n) +#endif +#if !defined(PRINTF_TRANSLATE_ARG) + #define PRINTF_TRANSLATE_ARG(a) +#endif +#if !defined(MALLOC_LIKE) + #define MALLOC_LIKE +#endif +#if !defined(ALLOCATOR) + #define ALLOCATOR +#endif +#if !defined(DLLEXPORT) + #define DLLEXPORT +#endif +#if !defined(DLLIMPORT) + #define DLLIMPORT +#endif +#if !defined(BREAKPOINT) + #define BREAKPOINT() +#endif +#if !defined(UNREACHABLE) + #define UNREACHABLE() +#endif +#if !defined(ATTRIBUTE_NO_SANITIZE_ADDRESS) + #define ATTRIBUTE_NO_SANITIZE_ADDRESS +#endif +#if !defined(DAEMON_FALLTHROUGH) + #define DAEMON_FALLTHROUGH #endif // Keywords specific to C++ versions From 3cbe3096914c2d130a52e3fa84f38b737a37e2d6 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 20 Jun 2024 05:57:12 +0200 Subject: [PATCH 9/9] common/Compiler: make NOEXCEPT definition not conditional --- src/common/Compiler.h | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/common/Compiler.h b/src/common/Compiler.h index 0213cdd554..3add821f49 100644 --- a/src/common/Compiler.h +++ b/src/common/Compiler.h @@ -250,15 +250,14 @@ See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf */ // Keywords specific to C++ versions -/* The noexcept keyword should be used on all move constructors and move +/* TODO: Rewrite all NOEXCEPT usages from the whole code base. + +The noexcept keyword should be used on all move constructors and move assignments so that containers move objects instead of copying them. -The noexcept keyword was added in C++11 but the __cpp_noexcept_function_type -definition to detect its implementation was only added in C++17. */ -#if defined(__cpp_noexcept_function_type) || __cplusplus >= 201103 - #define NOEXCEPT noexcept - #define NOEXCEPT_IF(x) noexcept(x) - #define NOEXCEPT_EXPR(x) noexcept(x) -#endif +That keyword was added in C++11, all compilers should now support it. */ +#define NOEXCEPT noexcept +#define NOEXCEPT_IF(x) noexcept(x) +#define NOEXCEPT_EXPR(x) noexcept(x) // Uses SD-6 Feature Test Recommendations #if defined(__cpp_constexpr) @@ -270,15 +269,6 @@ definition to detect its implementation was only added in C++17. */ #endif // Work around lack of language keywords. -#if !defined(NOEXCEPT) - #define NOEXCEPT -#endif -#if !defined(NOEXCEPT_IF) - #define NOEXCEPT_IF(x) -#endif -#if !defined(NOEXCEPT_EXPR) - #define NOEXCEPT_EXPR(x) x -#endif #if !defined(CONSTEXPR) #define CONSTEXPR #endif