[libc-commits] [libc] [libc][mathvec] Add loop over scalar for unary FP32 (PR #199273)

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Thu Jun 11 05:06:59 PDT 2026


================
@@ -0,0 +1,34 @@
+//===-- Implementation header for SIMD acosf ------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATHVEC_ACOSF_H
+#define LLVM_LIBC_SRC___SUPPORT_MATHVEC_ACOSF_H
+
+#include "src/__support/CPP/simd.h"
+#define LIBC_MATH (LIBC_MATH_NO_ERRNO | LIBC_MATH_NO_EXCEPT)
+#include "src/__support/math/acosf.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace mathvec {
+
+template <size_t N>
+LIBC_INLINE cpp::simd<float, N> acosf(cpp::simd<float, N> x) {
+  cpp::simd<float, N> result;
----------------
kaladron wrote:

@lntue What do you think of this.  I don't love the pointer arithmetic in each function.  I'd like us to fix up cpp::simd to give us the same functionality as C++26.  Something like this:

```c++
#include "src/__support/CPP/type_traits.h"
#include "src/__support/CPP/utility/integer_sequence.h"
#include "src/__support/macros/attributes.h"

namespace LIBC_NAMESPACE_DECL {
namespace cpp {

template <typename T, size_t N>
class simd {
public:
  using storage_type = T [[clang::ext_vector_type(N)]];
  using value_type = T;
  
  LIBC_INLINE constexpr static size_t size() { return N; }

private:
  storage_type value;

public:
  // 1. Default constructor
  LIBC_INLINE constexpr simd() = default;

  // 2. Implicit conversion from primitive vector type (for compatibility)
  LIBC_INLINE constexpr simd(storage_type v) : value(v) {}

  // 3. Broadcast constructor
  LIBC_INLINE constexpr explicit simd(T v) {
    value = storage_type(v); // Clang splat
  }

  // 4. C++26 Generator Constructor
  template <typename G>
  LIBC_INLINE constexpr explicit simd(G gen) {
    init(gen, cpp::make_index_sequence<N>{});
  }

  // Element access
  LIBC_INLINE constexpr T operator[](size_t i) const { return value[i]; }
  LIBC_INLINE constexpr T& operator[](size_t i) { return value[i]; }

  // Implicit conversion back to primitive vector type
  // This allows cpp::simd to be passed directly to Clang builtins (e.g. __builtin_elementwise_sin)
  LIBC_INLINE constexpr operator storage_type() const { return value; }
  LIBC_INLINE constexpr operator storage_type&() { return value; }

private:
  // Helper to expand the generator at compile time
  template <typename G, size_t... Indices>
  LIBC_INLINE constexpr void init(G gen, cpp::index_sequence<Indices...>) {
    // We construct the vector element-by-element.
    // Clang's ext_vector_type allows initialization from a list of elements.
    value = storage_type{gen(cpp::integral_constant<size_t, Indices>{})...};
  }
};

// Ensure the wrapper does not add size overhead
static_assert(sizeof(simd<float, 4>) == sizeof(float [[clang::ext_vector_type(4)]]));
static_assert(cpp::is_trivially_copyable_v<simd<float, 4>>);

} // namespace cpp
} // namespace LIBC_NAMESPACE_DECL
```

That should let the functions look like:

```c++
template <size_t N>
LIBC_INLINE cpp::simd<float, N> acosf(cpp::simd<float, N> x) {
  return cpp::simd<float, N>([&](<auto i>) { 
    return math::acosf(x[i]); 
  });
}
```

With no overhead in the final binary.   It's a deeper change to this (and we should provide the std::simd outside of this PR first)

With another wrapper like so:

```c++
namespace LIBC_NAMESPACE_DECL {
namespace mathvec {

// Generic Unary Vector Wrapper
template <auto ScalarFunc, typename T, size_t N>
LIBC_INLINE constexpr cpp::simd<T, N> unary_vectorize(cpp::simd<T, N> x) {
  return cpp::simd<T, N>([&](<auto i>) { 
    return ScalarFunc(x[i]); 
  });
}

// Generic Binary Vector Wrapper (for future use)
template <auto ScalarFunc, typename T, size_t N>
LIBC_INLINE constexpr cpp::simd<T, N> binary_vectorize(cpp::simd<T, N> x, cpp::simd<T, N> y) {
  return cpp::simd<T, N>([&](<auto i>) { 
    return ScalarFunc(x[i], y[i]); 
  });
}

} // namespace mathvec
} // namespace LIBC_NAMESPACE_DECL
```

The math functions become:

```c++
#include "src/__support/CPP/simd.h"
#include "src/__support/math/acosf.h"
#include "src/__support/mathvec/vector_wrapper.h"

namespace LIBC_NAMESPACE_DECL {
namespace mathvec {

template <size_t N>
LIBC_INLINE cpp::simd<float, N> acosf(cpp::simd<float, N> x) {
  return unary_vectorize<math::acosf>(x);
}

} // namespace mathvec
} // namespace LIBC_NAMESPACE_DECL
```

WDYT?

https://github.com/llvm/llvm-project/pull/199273


More information about the libc-commits mailing list