From 71ecbf74b5b2399ca815b2a178c0f4c63f225fd1 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Tue, 9 Sep 2025 15:41:22 -0500 Subject: [PATCH 1/5] [libc] Add check for support and a test for libc SIMD helpers Summary: This adds a few basic tests for the SIMD helpers and adds a CMake variable we can use to detect support. --- .../cmake/modules/CheckCompilerFeatures.cmake | 3 + .../check_ext_vector_type.cpp | 7 ++ libc/src/__support/CPP/simd.h | 2 +- libc/test/src/__support/CPP/CMakeLists.txt | 12 ++++ libc/test/src/__support/CPP/simd_test.cpp | 70 +++++++++++++++++++ 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 libc/cmake/modules/compiler_features/check_ext_vector_type.cpp create mode 100644 libc/test/src/__support/CPP/simd_test.cpp diff --git a/libc/cmake/modules/CheckCompilerFeatures.cmake b/libc/cmake/modules/CheckCompilerFeatures.cmake index a5ea66a5935b7..4d50d81e0ce45 100644 --- a/libc/cmake/modules/CheckCompilerFeatures.cmake +++ b/libc/cmake/modules/CheckCompilerFeatures.cmake @@ -15,6 +15,7 @@ set( "fixed_point" "cfloat16" "cfloat128" + "ext_vector_type" ) # Making sure ALL_COMPILER_FEATURES is sorted. @@ -126,6 +127,8 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES) set(LIBC_COMPILER_HAS_BUILTIN_ROUND TRUE) elseif(${feature} STREQUAL "builtin_roundeven") set(LIBC_COMPILER_HAS_BUILTIN_ROUNDEVEN TRUE) + elseif(${feature} STREQUAL "ext_vector_type") + set(LIBC_COMPILER_HAS_EXT_VECTOR_TYPE TRUE) endif() endif() endforeach() diff --git a/libc/cmake/modules/compiler_features/check_ext_vector_type.cpp b/libc/cmake/modules/compiler_features/check_ext_vector_type.cpp new file mode 100644 index 0000000000000..f268a8ff540f2 --- /dev/null +++ b/libc/cmake/modules/compiler_features/check_ext_vector_type.cpp @@ -0,0 +1,7 @@ +#include "src/__support/macros/attributes.h" + +#if !LIBC_HAS_VECTOR_TYPE +#error unsupported +#endif + +bool [[clang::ext_vector_type(1)]] v; diff --git a/libc/src/__support/CPP/simd.h b/libc/src/__support/CPP/simd.h index 449455c5c0390..12a493f6c7138 100644 --- a/libc/src/__support/CPP/simd.h +++ b/libc/src/__support/CPP/simd.h @@ -115,7 +115,7 @@ template LIBC_INLINE constexpr int find_first_set(simd m) { } template LIBC_INLINE constexpr int find_last_set(simd m) { constexpr size_t size = simd_size_v>; - return size - __builtin_clzg(m); + return size - 1 - __builtin_clzg(m); } // Elementwise operations. diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt index 3e1379d812c37..70965d6055bbe 100644 --- a/libc/test/src/__support/CPP/CMakeLists.txt +++ b/libc/test/src/__support/CPP/CMakeLists.txt @@ -160,3 +160,15 @@ add_libc_test( DEPENDS libc.src.__support.CPP.type_traits ) + +if(LIBC_COMPILER_HAS_EXT_VECTOR_TYPE) + add_libc_test( + simd_test + SUITE + libc-cpp-utils-tests + SRCS + simd_test.cpp + DEPENDS + libc.src.__support.CPP.simd + ) +endif() diff --git a/libc/test/src/__support/CPP/simd_test.cpp b/libc/test/src/__support/CPP/simd_test.cpp new file mode 100644 index 0000000000000..2c899f64d08d2 --- /dev/null +++ b/libc/test/src/__support/CPP/simd_test.cpp @@ -0,0 +1,70 @@ +//===-- Unittests for cpp::simd -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/simd.h" +#include "src/__support/CPP/utility.h" + +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" + +static_assert(LIBC_HAS_VECTOR_TYPE, "compiler needs ext_vector_type support"); + +using namespace LIBC_NAMESPACE::cpp; + +TEST(LlvmLibcSIMDTest, Basic) {} +TEST(LlvmLibcSIMDTest, VectorCreation) { + simd v1 = splat(5); + simd v2 = iota(); + + EXPECT_EQ(v1[0], 5); + EXPECT_EQ(v2[0], 0); +} + +TEST(LlvmLibcSIMDTest, TypeTraits) { + simd v1 = splat(0); + + static_assert(is_simd_v, "v1 should be a SIMD type"); + static_assert(!is_simd_v, "int is not a SIMD type"); + static_assert(is_simd_mask_v>, "should be a SIMD mask"); + + using Elem = simd_element_type_t; + static_assert(is_same_v, "element type should be int"); +} + +TEST(LlvmLibcSIMDTest, ElementwiseOperations) { + simd v1 = splat(1); + simd v2 = splat(-1); + + simd v_abs = abs(v2); + simd v_min = min(v1, v2); + simd v_max = max(v1, v2); + + EXPECT_EQ(v_min[0], -1); + EXPECT_EQ(v_max[0], 1); + EXPECT_EQ(v_abs[0], 1); +} + +TEST(LlvmLibcSIMDTest, ReductionOperations) { + simd v = splat(1); + + int sum = reduce(v); + int prod = reduce(v, multiplies<>{}); + + EXPECT_EQ(sum, static_cast(simd_size_v)); + EXPECT_EQ(prod, 1); +} + +TEST(LlvmLibcSIMDTest, MaskOperations) { + simd mask{true, false, true, false, false, false, false, false}; + + EXPECT_TRUE(any_of(mask)); + EXPECT_FALSE(all_of(mask)); + EXPECT_TRUE(some_of(mask)); + EXPECT_EQ(find_first_set(mask), 0); + EXPECT_EQ(find_last_set(mask), 2); +} From 7f97038381e36e45b7a9f00c642f4f564eef2459 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Tue, 9 Sep 2025 17:19:13 -0500 Subject: [PATCH 2/5] test all the things --- libc/test/src/__support/CPP/simd_test.cpp | 38 ++++++++++++++++++----- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/libc/test/src/__support/CPP/simd_test.cpp b/libc/test/src/__support/CPP/simd_test.cpp index 2c899f64d08d2..d9d1c1eed8939 100644 --- a/libc/test/src/__support/CPP/simd_test.cpp +++ b/libc/test/src/__support/CPP/simd_test.cpp @@ -37,16 +37,40 @@ TEST(LlvmLibcSIMDTest, TypeTraits) { } TEST(LlvmLibcSIMDTest, ElementwiseOperations) { - simd v1 = splat(1); - simd v2 = splat(-1); - - simd v_abs = abs(v2); - simd v_min = min(v1, v2); - simd v_max = max(v1, v2); + simd vi1 = splat(1); + simd vi2 = splat(-1); + simd vf1 = splat(1.0f); + simd vf2 = splat(-1.0f); + + simd v_abs = abs(vi2); + simd v_min = min(vi1, vi2); + simd v_max = max(vi1, vi2); + simd v_fma = fma(vf1, vf2, vf1); + simd v_ceil = ceil(splat(1.2f)); + simd v_floor = floor(splat(1.8f)); + simd v_roundeven = roundeven(splat(2.5f)); + simd v_round = round(splat(2.5f)); + simd v_trunc = trunc(splat(-2.9f)); + simd v_nearbyint = nearbyint(splat(3.4f)); + simd v_rint = rint(splat(3.6f)); + simd v_canonicalize = canonicalize(splat(1.0f)); + simd v_copysign = copysign(vf1, vf2); + simd v_fmod = fmod(splat(5.5f), splat(2.0f)); + EXPECT_EQ(v_abs[0], 1); EXPECT_EQ(v_min[0], -1); EXPECT_EQ(v_max[0], 1); - EXPECT_EQ(v_abs[0], 1); + EXPECT_FP_EQ(v_fma[0], 0.0f); + EXPECT_FP_EQ(v_ceil[0], 2.0f); + EXPECT_FP_EQ(v_floor[0], 1.0f); + EXPECT_FP_EQ(v_roundeven[0], 2.0f); + EXPECT_FP_EQ(v_round[0], 3.0f); + EXPECT_FP_EQ(v_trunc[0], -2.0f); + EXPECT_FP_EQ(v_nearbyint[0], 3.0f); + EXPECT_FP_EQ(v_rint[0], 4.0f); + EXPECT_FP_EQ(v_canonicalize[0], 1.0f); + EXPECT_FP_EQ(v_copysign[0], -1.0f); + EXPECT_FP_EQ(v_fmod[0], 1.5f); } TEST(LlvmLibcSIMDTest, ReductionOperations) { From 0ae3da5569ca97d07422a6cfa9abeeabf8e9aca6 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Tue, 9 Sep 2025 17:28:44 -0500 Subject: [PATCH 3/5] try fix errno on some platforms --- libc/test/src/__support/CPP/simd_test.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libc/test/src/__support/CPP/simd_test.cpp b/libc/test/src/__support/CPP/simd_test.cpp index d9d1c1eed8939..ad786764075c5 100644 --- a/libc/test/src/__support/CPP/simd_test.cpp +++ b/libc/test/src/__support/CPP/simd_test.cpp @@ -55,7 +55,6 @@ TEST(LlvmLibcSIMDTest, ElementwiseOperations) { simd v_rint = rint(splat(3.6f)); simd v_canonicalize = canonicalize(splat(1.0f)); simd v_copysign = copysign(vf1, vf2); - simd v_fmod = fmod(splat(5.5f), splat(2.0f)); EXPECT_EQ(v_abs[0], 1); EXPECT_EQ(v_min[0], -1); @@ -70,7 +69,6 @@ TEST(LlvmLibcSIMDTest, ElementwiseOperations) { EXPECT_FP_EQ(v_rint[0], 4.0f); EXPECT_FP_EQ(v_canonicalize[0], 1.0f); EXPECT_FP_EQ(v_copysign[0], -1.0f); - EXPECT_FP_EQ(v_fmod[0], 1.5f); } TEST(LlvmLibcSIMDTest, ReductionOperations) { From 79afef92c0cf6dc9ab1ddffba67c6a60d848c065 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Tue, 9 Sep 2025 18:21:36 -0500 Subject: [PATCH 4/5] namespace --- libc/test/src/__support/CPP/simd_test.cpp | 75 ++++++++++++----------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/libc/test/src/__support/CPP/simd_test.cpp b/libc/test/src/__support/CPP/simd_test.cpp index ad786764075c5..a769cd635999b 100644 --- a/libc/test/src/__support/CPP/simd_test.cpp +++ b/libc/test/src/__support/CPP/simd_test.cpp @@ -14,47 +14,48 @@ static_assert(LIBC_HAS_VECTOR_TYPE, "compiler needs ext_vector_type support"); -using namespace LIBC_NAMESPACE::cpp; +using namespace LIBC_NAMESPACE; TEST(LlvmLibcSIMDTest, Basic) {} TEST(LlvmLibcSIMDTest, VectorCreation) { - simd v1 = splat(5); - simd v2 = iota(); + cpp::simd v1 = cpp::splat(5); + cpp::simd v2 = cpp::iota(); EXPECT_EQ(v1[0], 5); EXPECT_EQ(v2[0], 0); } TEST(LlvmLibcSIMDTest, TypeTraits) { - simd v1 = splat(0); + cpp::simd v1 = cpp::splat(0); - static_assert(is_simd_v, "v1 should be a SIMD type"); - static_assert(!is_simd_v, "int is not a SIMD type"); - static_assert(is_simd_mask_v>, "should be a SIMD mask"); + static_assert(cpp::is_simd_v, "v1 should be a SIMD type"); + static_assert(!cpp::is_simd_v, "int is not a SIMD type"); + static_assert(cpp::is_simd_mask_v>, + "should be a SIMD mask"); - using Elem = simd_element_type_t; - static_assert(is_same_v, "element type should be int"); + using Elem = cpp::simd_element_type_t; + static_assert(cpp::is_same_v, "element type should be int"); } TEST(LlvmLibcSIMDTest, ElementwiseOperations) { - simd vi1 = splat(1); - simd vi2 = splat(-1); - simd vf1 = splat(1.0f); - simd vf2 = splat(-1.0f); - - simd v_abs = abs(vi2); - simd v_min = min(vi1, vi2); - simd v_max = max(vi1, vi2); - simd v_fma = fma(vf1, vf2, vf1); - simd v_ceil = ceil(splat(1.2f)); - simd v_floor = floor(splat(1.8f)); - simd v_roundeven = roundeven(splat(2.5f)); - simd v_round = round(splat(2.5f)); - simd v_trunc = trunc(splat(-2.9f)); - simd v_nearbyint = nearbyint(splat(3.4f)); - simd v_rint = rint(splat(3.6f)); - simd v_canonicalize = canonicalize(splat(1.0f)); - simd v_copysign = copysign(vf1, vf2); + cpp::simd vi1 = cpp::splat(1); + cpp::simd vi2 = cpp::splat(-1); + cpp::simd vf1 = cpp::splat(1.0f); + cpp::simd vf2 = cpp::splat(-1.0f); + + cpp::simd v_abs = cpp::abs(vi2); + cpp::simd v_min = cpp::min(vi1, vi2); + cpp::simd v_max = cpp::max(vi1, vi2); + cpp::simd v_fma = cpp::fma(vf1, vf2, vf1); + cpp::simd v_ceil = cpp::ceil(cpp::splat(1.2f)); + cpp::simd v_floor = cpp::floor(cpp::splat(1.8f)); + cpp::simd v_roundeven = cpp::roundeven(cpp::splat(2.5f)); + cpp::simd v_round = cpp::round(cpp::splat(2.5f)); + cpp::simd v_trunc = cpp::trunc(cpp::splat(-2.9f)); + cpp::simd v_nearbyint = cpp::nearbyint(cpp::splat(3.4f)); + cpp::simd v_rint = cpp::rint(cpp::splat(3.6f)); + cpp::simd v_canonicalize = cpp::canonicalize(cpp::splat(1.0f)); + cpp::simd v_copysign = cpp::copysign(vf1, vf2); EXPECT_EQ(v_abs[0], 1); EXPECT_EQ(v_min[0], -1); @@ -72,21 +73,21 @@ TEST(LlvmLibcSIMDTest, ElementwiseOperations) { } TEST(LlvmLibcSIMDTest, ReductionOperations) { - simd v = splat(1); + cpp::simd v = cpp::splat(1); - int sum = reduce(v); - int prod = reduce(v, multiplies<>{}); + int sum = cpp::reduce(v); + int prod = cpp::reduce(v, cpp::multiplies<>{}); - EXPECT_EQ(sum, static_cast(simd_size_v)); + EXPECT_EQ(sum, static_cast(cpp::simd_size_v)); EXPECT_EQ(prod, 1); } TEST(LlvmLibcSIMDTest, MaskOperations) { - simd mask{true, false, true, false, false, false, false, false}; + cpp::simd mask{true, false, true, false, false, false, false, false}; - EXPECT_TRUE(any_of(mask)); - EXPECT_FALSE(all_of(mask)); - EXPECT_TRUE(some_of(mask)); - EXPECT_EQ(find_first_set(mask), 0); - EXPECT_EQ(find_last_set(mask), 2); + EXPECT_TRUE(cpp::any_of(mask)); + EXPECT_FALSE(cpp::all_of(mask)); + EXPECT_TRUE(cpp::some_of(mask)); + EXPECT_EQ(cpp::find_first_set(mask), 0); + EXPECT_EQ(cpp::find_last_set(mask), 2); } From caddca5d0b1f6edece90b0ce49a63e004753dc7b Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Tue, 9 Sep 2025 18:52:48 -0500 Subject: [PATCH 5/5] undo --- libc/test/src/__support/CPP/simd_test.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/libc/test/src/__support/CPP/simd_test.cpp b/libc/test/src/__support/CPP/simd_test.cpp index a769cd635999b..600bf65057b21 100644 --- a/libc/test/src/__support/CPP/simd_test.cpp +++ b/libc/test/src/__support/CPP/simd_test.cpp @@ -9,7 +9,6 @@ #include "src/__support/CPP/simd.h" #include "src/__support/CPP/utility.h" -#include "test/UnitTest/FPMatcher.h" #include "test/UnitTest/Test.h" static_assert(LIBC_HAS_VECTOR_TYPE, "compiler needs ext_vector_type support"); @@ -40,36 +39,14 @@ TEST(LlvmLibcSIMDTest, TypeTraits) { TEST(LlvmLibcSIMDTest, ElementwiseOperations) { cpp::simd vi1 = cpp::splat(1); cpp::simd vi2 = cpp::splat(-1); - cpp::simd vf1 = cpp::splat(1.0f); - cpp::simd vf2 = cpp::splat(-1.0f); cpp::simd v_abs = cpp::abs(vi2); cpp::simd v_min = cpp::min(vi1, vi2); cpp::simd v_max = cpp::max(vi1, vi2); - cpp::simd v_fma = cpp::fma(vf1, vf2, vf1); - cpp::simd v_ceil = cpp::ceil(cpp::splat(1.2f)); - cpp::simd v_floor = cpp::floor(cpp::splat(1.8f)); - cpp::simd v_roundeven = cpp::roundeven(cpp::splat(2.5f)); - cpp::simd v_round = cpp::round(cpp::splat(2.5f)); - cpp::simd v_trunc = cpp::trunc(cpp::splat(-2.9f)); - cpp::simd v_nearbyint = cpp::nearbyint(cpp::splat(3.4f)); - cpp::simd v_rint = cpp::rint(cpp::splat(3.6f)); - cpp::simd v_canonicalize = cpp::canonicalize(cpp::splat(1.0f)); - cpp::simd v_copysign = cpp::copysign(vf1, vf2); EXPECT_EQ(v_abs[0], 1); EXPECT_EQ(v_min[0], -1); EXPECT_EQ(v_max[0], 1); - EXPECT_FP_EQ(v_fma[0], 0.0f); - EXPECT_FP_EQ(v_ceil[0], 2.0f); - EXPECT_FP_EQ(v_floor[0], 1.0f); - EXPECT_FP_EQ(v_roundeven[0], 2.0f); - EXPECT_FP_EQ(v_round[0], 3.0f); - EXPECT_FP_EQ(v_trunc[0], -2.0f); - EXPECT_FP_EQ(v_nearbyint[0], 3.0f); - EXPECT_FP_EQ(v_rint[0], 4.0f); - EXPECT_FP_EQ(v_canonicalize[0], 1.0f); - EXPECT_FP_EQ(v_copysign[0], -1.0f); } TEST(LlvmLibcSIMDTest, ReductionOperations) {