From 719ea022ec5d85a41c3df3d79ed22fe1f93c2db8 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Tue, 22 Jul 2025 20:17:46 +0300 Subject: [PATCH] [UCRT] Fix GCC/Clang SIMD compilation GCC and Clang need to mark functions that use SSE/AVX etc, either with a function attribute or a pragma around the function. strlen uses a template function that either uses SSE2 or AVX2. Previously the template was surrounded with pragmas to allow both SSE2 and AVX2, but that makes GCC assume that it can use AVX2 instructions even in the SSE2 version. To fix this the template instances are now build in individual compilation units for SSE2 and AVX, separate from the "dispatcher" function. Now ucrtbase doesn't crash anymore on GCC build. Another issue was the namespace around strnlen_mode, which has confused clang so much, that it forgot to instantiate the template code. --- sdk/lib/ucrt/inc/corecrt_internal_simd.h | 16 +++++++++---- sdk/lib/ucrt/string/string.cmake | 11 +++++++++ sdk/lib/ucrt/string/strnlen-avx2.cpp | 29 ++++++++++++++++++++++++ sdk/lib/ucrt/string/strnlen-sse2.cpp | 29 ++++++++++++++++++++++++ sdk/lib/ucrt/string/strnlen.cpp | 17 +++++++++----- 5 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 sdk/lib/ucrt/string/strnlen-avx2.cpp create mode 100644 sdk/lib/ucrt/string/strnlen-sse2.cpp diff --git a/sdk/lib/ucrt/inc/corecrt_internal_simd.h b/sdk/lib/ucrt/inc/corecrt_internal_simd.h index 01a92229438..4b5421f4f6e 100644 --- a/sdk/lib/ucrt/inc/corecrt_internal_simd.h +++ b/sdk/lib/ucrt/inc/corecrt_internal_simd.h @@ -19,22 +19,27 @@ #if defined _CRT_SIMD_SUPPORT_AVAILABLE #if defined(__clang__) -#define _UCRT_ENABLE_EXTENDED_ISA \ +#define _UCRT_ENABLE_SSE2 \ + _Pragma("clang attribute push(__attribute__((target(\"sse2\"))), apply_to=function)") +#define _UCRT_ENABLE_AVX2 \ _Pragma("clang attribute push(__attribute__((target(\"sse2,avx,avx2\"))), apply_to=function)") #define _UCRT_RESTORE_DEFAULT_ISA \ _Pragma("clang attribute pop") #elif defined(__GNUC__) -#define _UCRT_ENABLE_EXTENDED_ISA \ +#define _UCRT_ENABLE_SSE2 \ + _Pragma("GCC push_options") \ + _Pragma("GCC target(\"sse2\")") +#define _UCRT_ENABLE_AVX2 \ _Pragma("GCC push_options") \ _Pragma("GCC target(\"avx2\")") #define _UCRT_RESTORE_DEFAULT_ISA \ _Pragma("GCC pop_options") #else -#define _UCRT_ENABLE_EXTENDED_ISA +#define _UCRT_ENABLE_SSE2 +#define _UCRT_ENABLE_AVX2 #define _UCRT_RESTORE_DEFAULT_ISA #endif -_UCRT_ENABLE_EXTENDED_ISA extern "C" int __isa_available; @@ -70,6 +75,7 @@ _UCRT_ENABLE_EXTENDED_ISA }; +_UCRT_ENABLE_SSE2 template <> struct __crt_simd_cleanup_guard<__crt_simd_isa::sse2> @@ -120,7 +126,9 @@ _UCRT_ENABLE_EXTENDED_ISA } }; +_UCRT_RESTORE_DEFAULT_ISA +_UCRT_ENABLE_AVX2 template <> struct __crt_simd_cleanup_guard<__crt_simd_isa::avx2> diff --git a/sdk/lib/ucrt/string/string.cmake b/sdk/lib/ucrt/string/string.cmake index afd2ec57db6..6c80430c68c 100644 --- a/sdk/lib/ucrt/string/string.cmake +++ b/sdk/lib/ucrt/string/string.cmake @@ -57,6 +57,17 @@ list(APPEND UCRT_STRING_SOURCES string/wmemmove_s.cpp ) +# Special handling for GCC and Clang +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") + list(APPEND UCRT_STRING_SOURCES + string/strnlen-avx2.cpp + string/strnlen-sse2.cpp + ) + + set_source_files_properties(string/strnlen-sse2.cpp PROPERTIES COMPILE_OPTIONS "-msse2") + set_source_files_properties(string/strnlen-avx2.cpp PROPERTIES COMPILE_OPTIONS "-mavx2") +endif() + if(${ARCH} STREQUAL "i386") list(APPEND UCRT_STRING_ASM_SOURCES string/i386/_memicmp.s diff --git a/sdk/lib/ucrt/string/strnlen-avx2.cpp b/sdk/lib/ucrt/string/strnlen-avx2.cpp new file mode 100644 index 00000000000..4b94794d167 --- /dev/null +++ b/sdk/lib/ucrt/string/strnlen-avx2.cpp @@ -0,0 +1,29 @@ +// +// strnlen-avx2.cpp +// +// Copyright (c) Timo Kreuzer +// +// Explicit template instantiations for AVX2 str(n)len code +// + +#pragma GCC target("avx2") +#define _UCRT_BUILD_AVX2 +#include "strnlen.cpp" + +template +size_t __cdecl common_strnlen_simd( + uint8_t const* const string, + size_t const maximum_count + ) throw(); + +template +size_t __cdecl common_strnlen_simd( + uint16_t const* const string, + size_t const maximum_count + ) throw(); + +template +size_t __cdecl common_strnlen_simd( + uint16_t const* const string, + size_t const maximum_count + ) throw(); diff --git a/sdk/lib/ucrt/string/strnlen-sse2.cpp b/sdk/lib/ucrt/string/strnlen-sse2.cpp new file mode 100644 index 00000000000..8258bade2f9 --- /dev/null +++ b/sdk/lib/ucrt/string/strnlen-sse2.cpp @@ -0,0 +1,29 @@ +// +// strnlen-sse2.cpp +// +// Copyright (c) Timo Kreuzer +// +// Explicit template instantiations for SSE2 str(n)len code +// + +#pragma GCC target("sse2") +#define _UCRT_BUILD_SSE2 +#include "strnlen.cpp" + +template +size_t __cdecl common_strnlen_simd( + uint8_t const* const string, + size_t const maximum_count + ) throw(); + +template +size_t __cdecl common_strnlen_simd( + uint16_t const* const string, + size_t const maximum_count + ) throw(); + +template +size_t __cdecl common_strnlen_simd( + uint16_t const* const string, + size_t const maximum_count + ) throw(); diff --git a/sdk/lib/ucrt/string/strnlen.cpp b/sdk/lib/ucrt/string/strnlen.cpp index 302086cb9f9..024f98e9a42 100644 --- a/sdk/lib/ucrt/string/strnlen.cpp +++ b/sdk/lib/ucrt/string/strnlen.cpp @@ -21,14 +21,14 @@ -namespace -{ +//namespace // clang doesn't like this! +//{ enum strnlen_mode { bounded, // strnlen mode; maximum_count is respected unbounded, // strlen mode; maximum_count is ignored }; -} +//} // This function returns true if we have reached the end of the range to be // searched for a terminator. For the bounded strnlen functions, we must @@ -78,16 +78,18 @@ static __forceinline size_t __cdecl common_strnlen_c( #ifdef _CRT_SIMD_SUPPORT_AVAILABLE -_UCRT_ENABLE_EXTENDED_ISA template _Check_return_ _When_(maximum_count > _String_length_(string), _Post_satisfies_(return == _String_length_(string))) _When_(maximum_count <= _String_length_(string), _Post_satisfies_(return == maximum_count)) - static __inline size_t __cdecl common_strnlen_simd( + size_t __cdecl common_strnlen_simd( Element const* const string, size_t const maximum_count ) throw() +#if (defined(__GNUC__) || defined(__clang__)) && !defined(_UCRT_BUILD_SSE2) && !defined(_UCRT_BUILD_AVX2) + ; +#else { using traits = __crt_simd_traits; @@ -170,10 +172,12 @@ _UCRT_ENABLE_EXTENDED_ISA return static_cast(it - string); } -_UCRT_RESTORE_DEFAULT_ISA +#endif // (defined(__GNUC__) || defined(__clang__)) && !defined(_UCRT_BUILD_SSE2) && !defined(_UCRT_BUILD_AVX2) #endif // _CRT_SIMD_SUPPORT_AVAILABLE +#if !defined(_UCRT_BUILD_SSE2) && !defined(_UCRT_BUILD_AVX2) + template _Check_return_ _When_(maximum_count > _String_length_(string), _Post_satisfies_(return == _String_length_(string))) @@ -225,3 +229,4 @@ extern "C" size_t __cdecl wcslen( } #endif // _M_ARM64 +#endif // !defined(_UCRT_BUILD_SSE2) && !defined(_UCRT_BUILD_AVX2)