Skip to content

Commit

Permalink
Continue the implementation process of std::simd
Browse files Browse the repository at this point in the history
Signed-off-by: Ian <[email protected]>
  • Loading branch information
Rinzii committed Jan 15, 2025
1 parent 6d83f93 commit bce06ee
Show file tree
Hide file tree
Showing 13 changed files with 595 additions and 41 deletions.
14 changes: 9 additions & 5 deletions include/ccmath/internal/math/runtime/pp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
ccm_add_headers(
simd.hpp
arm_detail.hpp
assume_aligned.hpp
const_eval.hpp
constexpr_wrapper.hpp
debug_print.hpp
detail.hpp
may_alias.hpp
simd_intrinsic.hpp
trap.hpp
constexpr_wrapper.hpp
flags.hpp
fwddecl.hpp
may_alias.hpp
simd.hpp
simd_config.hpp
simd_meta.hpp
trap.hpp
vec_detail.hpp
x86_include.hpp
)
2 changes: 2 additions & 0 deletions include/ccmath/internal/math/runtime/pp/arm_detail.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

#pragma once
27 changes: 27 additions & 0 deletions include/ccmath/internal/math/runtime/pp/assume_aligned.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

#pragma once

#include <cstddef>

#include "ccmath/internal/predef/attributes/always_inline.hpp"

#if defined(_MSC_VER) && !defined(__clang__)
#include <cstdint>
#endif

namespace ccm::pp
{
template <std::size_t Alignment>
CCM_ALWAYS_INLINE void * assume_aligned(void * ptr)
{
static_assert((Alignment & (Alignment - 1)) == 0, "Alignment must be a power of 2"); // TODO: Might remove this check not sure if it's necessary
#if defined(__GNUC__) || (defined(__clang__) && !defined(_MSC_VER))
return __builtin_assume_aligned(ptr, Alignment);
#elif defined(_MSC_VER)
__assume((reinterpret_cast<std::uintptr_t>(ptr) & (Alignment - 1)) == 0);
return ptr;
#else
return ptr;
#endif
}
} // namespace ccm::pp
9 changes: 9 additions & 0 deletions include/ccmath/internal/math/runtime/pp/constexpr_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
/*
* Copyright (c) Ian Pike
* Copyright (c) CCMath contributors
*
* CCMath is provided under the Apache-2.0 License WITH LLVM-exception.
* See LICENSE for more information.
*
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

#pragma once
35 changes: 17 additions & 18 deletions include/ccmath/internal/math/runtime/pp/detail.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "const_eval.hpp"
#include "debug_print.hpp"
#include "may_alias.hpp"
#include "simd_intrinsic.hpp"
#include "trap.hpp"

#include "ccmath/internal/predef/attributes/always_inline.hpp"
Expand Down Expand Up @@ -57,7 +56,7 @@ namespace ccm::pp::detail
inline constexpr PrivateInit private_init = PrivateInit{};

template <typename T, typename F>
CCM_ATTR_SIMD_INTRINSIC static void bit_iteration(T mask, F && func)
CCM_SIMD_INTRINSIC static void bit_iteration(T mask, F && func)
{
static_assert(sizeof(0ULL) >= sizeof(T));
using ConditionalType = std::conditional_t<sizeof(T) <= sizeof(0u), unsigned, unsigned long long>;
Expand All @@ -70,7 +69,7 @@ namespace ccm::pp::detail
}

template <size_t Np, bool Sanitized, typename F>
CCM_ATTR_SIMD_INTRINSIC static void bit_iteration(BitMask<Np, Sanitized> mask, F && func)
CCM_SIMD_INTRINSIC static void bit_iteration(BitMask<Np, Sanitized> mask, F && func)
{
bit_iteration(mask.sanitized().to_bits(), func);
}
Expand Down Expand Up @@ -270,13 +269,13 @@ namespace ccm::pp::detail
}();

template <typename Integral, std::enable_if_t<std::is_integral_v<Integral>, int> = 0>
CCM_ATTR_SIMD_INTRINSIC SimdSizeType lowest_bit(Integral bits)
CCM_SIMD_INTRINSIC SimdSizeType lowest_bit(Integral bits)
{
return ccm::support::ctz(bits);
}

template <typename Integral, std::enable_if_t<std::is_integral_v<Integral>, int> = 0>
CCM_ATTR_SIMD_INTRINSIC SimdSizeType highest_bit(Integral bits)
CCM_SIMD_INTRINSIC SimdSizeType highest_bit(Integral bits)
{
if constexpr (sizeof(bits) <= sizeof(int)) { return sizeof(int) * CHAR_BIT - 1 - ccm::support::countl_zero(bits); }
else if constexpr (sizeof(bits) <= sizeof(long)) { return sizeof(long) * CHAR_BIT - 1 - ccm::support::countl_zero(bits); }
Expand Down Expand Up @@ -403,33 +402,33 @@ namespace ccm::pp::detail
SimdSizeType index;
Up & obj;

CCM_ATTR_SIMD_INTRINSIC constexpr ValueType read() const noexcept { return Accessor::get(obj, index); }
CCM_SIMD_INTRINSIC constexpr ValueType read() const noexcept { return Accessor::get(obj, index); }

template <typename Tp>
CCM_ATTR_SIMD_INTRINSIC constexpr void write(Tp && x) const
CCM_SIMD_INTRINSIC constexpr void write(Tp && x) const
{
Accessor::set(obj, index, std::forward<Tp>(x));
}

public:
CCM_ATTR_SIMD_INTRINSIC constexpr SmartReference(Up & o, SimdSizeType i) noexcept : index(i), obj(o) {}
CCM_SIMD_INTRINSIC constexpr SmartReference(Up & o, SimdSizeType i) noexcept : index(i), obj(o) {}

using value_type = ValueType;

SmartReference(const SmartReference &) = delete;

CCM_ATTR_SIMD_INTRINSIC constexpr operator value_type() const noexcept { return read(); }
CCM_SIMD_INTRINSIC constexpr operator value_type() const noexcept { return read(); }

template <typename Tp>
CCM_ATTR_SIMD_INTRINSIC constexpr SmartReference operator=(Tp && x) &&
CCM_SIMD_INTRINSIC constexpr SmartReference operator=(Tp && x) &&
{
write(std::forward<Tp>(x));
return {obj, index};
}

#define CCM_SIMD_OP(op) \
template <typename Tp> \
CCM_ATTR_SIMD_INTRINSIC constexpr SmartReference operator op##=(Tp && x) && \
CCM_SIMD_INTRINSIC constexpr SmartReference operator op##=(Tp && x) && \
{ \
const value_type & lhs = read(); \
write(lhs op std::forward<Tp>(x)); \
Expand All @@ -450,15 +449,15 @@ namespace ccm::pp::detail
#undef CCM_SIMD_OP

template <typename Tp = void, typename = decltype(++std::declval<std::conditional_t<true, value_type, Tp> &>())>
CCM_ATTR_SIMD_INTRINSIC constexpr SmartReference operator++() &&
CCM_SIMD_INTRINSIC constexpr SmartReference operator++() &&
{
value_type x = read();
write(++x);
return {obj, index};
}

template <typename Tp = void, typename = decltype(std::declval<std::conditional_t<true, value_type, Tp> &>()++)>
CCM_ATTR_SIMD_INTRINSIC constexpr value_type operator++(int) &&
CCM_SIMD_INTRINSIC constexpr value_type operator++(int) &&
{
const value_type r = read();
value_type x = r;
Expand All @@ -467,31 +466,31 @@ namespace ccm::pp::detail
}

template <typename Tp = void, typename = decltype(--std::declval<std::conditional_t<true, value_type, Tp> &>())>
CCM_ATTR_SIMD_INTRINSIC constexpr SmartReference operator--() &&
CCM_SIMD_INTRINSIC constexpr SmartReference operator--() &&
{
value_type x = read();
write(--x);
return {obj, index};
}

template <typename Tp = void, typename = decltype(std::declval<std::conditional_t<true, value_type, Tp> &>()--)>
CCM_ATTR_SIMD_INTRINSIC constexpr value_type operator--(int) &&
CCM_SIMD_INTRINSIC constexpr value_type operator--(int) &&
{
const value_type r = read();
value_type x = r;
write(--x);
return r;
}

CCM_ATTR_SIMD_INTRINSIC friend constexpr void swap(SmartReference && a, SmartReference && b) noexcept(
CCM_SIMD_INTRINSIC friend constexpr void swap(SmartReference && a, SmartReference && b) noexcept(
std::conjunction_v<std::is_nothrow_constructible<value_type, SmartReference &&>, std::is_nothrow_assignable<SmartReference &&, value_type &&>>)
{
value_type tmp = std::forward<SmartReference>(a);
std::forward<SmartReference>(a) = static_cast<value_type>(b);
std::forward<SmartReference>(b) = std::move(tmp);
}

CCM_ATTR_SIMD_INTRINSIC friend constexpr void swap(value_type & a, SmartReference && b) noexcept(
CCM_SIMD_INTRINSIC friend constexpr void swap(value_type & a, SmartReference && b) noexcept(
std::conjunction_v<std::is_nothrow_constructible<value_type, value_type &&>, std::is_nothrow_assignable<value_type &, value_type &&>,
std::is_nothrow_assignable<SmartReference &&, value_type &&>>)
{
Expand All @@ -500,7 +499,7 @@ namespace ccm::pp::detail
std::forward<SmartReference>(b) = std::move(tmp);
}

CCM_ATTR_SIMD_INTRINSIC friend constexpr void swap(SmartReference && a, value_type & b) noexcept(
CCM_SIMD_INTRINSIC friend constexpr void swap(SmartReference && a, value_type & b) noexcept(
std::conjunction_v<std::is_nothrow_constructible<value_type, SmartReference &&>, std::is_nothrow_assignable<value_type &, value_type &&>,
std::is_nothrow_assignable<SmartReference &&, value_type &&>>)
{
Expand Down
179 changes: 179 additions & 0 deletions include/ccmath/internal/math/runtime/pp/flags.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* Copyright (c) Ian Pike
* Copyright (c) CCMath contributors
*
* CCMath is provided under the Apache-2.0 License WITH LLVM-exception.
* See LICENSE for more information.
*
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

#pragma once

#include "ccmath/internal/predef/attributes/always_inline.hpp"
#include "ccmath/internal/support/bits.hpp"
#include "ccmath/internal/support/floating_point_traits.hpp"

// ReSharper disable once CppUnusedIncludeDirective
#include "assume_aligned.hpp"
#include "fwddecl.hpp"
#include "simd_config.hpp"

namespace ccm::pp
{
namespace detail
{
struct LoadStoreTag
{
};

struct Convert : LoadStoreTag
{
};

template <typename T>
struct ConvertTo : LoadStoreTag
{
using type = T;
};

struct Aligned : LoadStoreTag
{
template <typename T, typename U>
CCM_SIMD_INTRINSIC static constexpr U * adjust_pointer(U * ptr)
{
return static_cast<U *>(pp::assume_aligned<simd_alignment_v<T, U>>(ptr));
}
};

template <std::size_t N>
struct Overaligned : LoadStoreTag
{
static_assert(support::has_single_bit(N));

template <typename, typename U>
CCM_SIMD_INTRINSIC static constexpr U * adjust_pointer(U * ptr)
{
return static_cast<U *>(pp::assume_aligned<N>(ptr));
}
};

struct Streaming : LoadStoreTag
{
};

template <int L1, int L2>
struct Prefetch : LoadStoreTag
{
template <typename, typename U>
CCM_ALWAYS_INLINE static U * adjust_pointer(U * ptr)
{
// one read: 0, 0
// L1: 0, 1
// L2: 0, 2
// L3: 0, 3
// (exclusive cache line) for writing: 1, 0 / 1, 1
/* constexpr int write = 1;
constexpr int level = 0-3;
__builtin_prefetch(ptr, write, level)
_mm_prefetch(reinterpret_cast<char const*>(ptr), _MM_HINT_T0);
_mm_prefetch(reinterpret_cast<char const*>(ptr), _MM_HINT_T1);
_mm_prefetch(reinterpret_cast<char const*>(ptr), _MM_HINT_T2);
_mm_prefetch(reinterpret_cast<char const*>(ptr), _MM_HINT_ET0);
_mm_prefetch(reinterpret_cast<char const*>(ptr), _MM_HINT_ET1);
_mm_prefetch(reinterpret_cast<char const*>(ptr), _MM_HINT_NTA);*/
return ptr;
}
};

template <typename T>
using is_loadstore_tag = std::is_base_of<LoadStoreTag, T>;
} // namespace detail

template <typename... Flags>
struct simd_flags
{
static_assert((detail::is_loadstore_tag<Flags>::value && ...), "All Flags must derive from LoadStoreTag");

// ReSharper disable once CppMemberFunctionMayBeStatic
CCM_CONSTEVAL bool is_equal(simd_flags) const { return true; }

template <typename... Other>
CCM_CONSTEVAL bool is_equal([[maybe_unused]] simd_flags<Other...> other) const
{
return std::is_same_v<simd_flags<>, decltype(xor_flags(other))>;
}

template <typename... Other>
CCM_CONSTEVAL bool test(simd_flags<Other...> other) const noexcept
{
return other.is_equal(and_flags(other));
}

friend CCM_CONSTEVAL auto operator|(simd_flags, simd_flags<>) { return simd_flags{}; }

template <typename T0, typename... More>
friend CCM_CONSTEVAL auto operator|(simd_flags, simd_flags<T0, More...>)
{
if constexpr ((std::is_same_v<Flags, T0> || ...)) { return simd_flags<Flags...>{} | simd_flags<More...>{}; }
else { return simd_flags<Flags..., T0>{} | simd_flags<More...>{}; }
}

// ReSharper disable once CppMemberFunctionMayBeStatic
CCM_CONSTEVAL auto and_flags(simd_flags<>) const { return simd_flags<>{}; }

template <typename T0, typename... More>
CCM_CONSTEVAL auto and_flags(simd_flags<T0, More...>) const
{
if constexpr ((std::is_same_v<Flags, T0> || ...)) { return simd_flags<T0>{} | (simd_flags{}.and_flags(simd_flags<More...>{})); }
else { return simd_flags{}.and_flags(simd_flags<More...>{}); }
}

CCM_CONSTEVAL auto xor_flags(simd_flags<>) const { return simd_flags{}; }

template <typename T0, typename... More>
CCM_CONSTEVAL auto xor_flags(simd_flags<T0, More...>) const
{
if constexpr ((std::is_same_v<Flags, T0> || ...))
{
constexpr auto removed = (std::conditional_t<std::is_same_v<Flags, T0>, simd_flags<>, simd_flags<Flags>>{} | ...);
return removed.xor_flags(simd_flags<More...>{});
}
else { return (simd_flags{} | simd_flags<T0>{}).xor_flags(simd_flags<More...>{}); }
}

template <typename F0, typename Tp, typename Ptr>
static constexpr void apply_adjust_pointer(Ptr & ptr)
{
if constexpr (std::is_same_v<decltype(F0::template adjust_pointer<Tp>(ptr)), void>) { ptr = F0::template adjust_pointer<Tp>(ptr); }
}

template <typename Tp, typename Up>
static constexpr Up * adjust_pointer(Up * ptr)
{
(apply_adjust_pointer<Flags, Tp>(ptr), ...);
return ptr;
}
};

// [simd.flags]
inline constexpr simd_flags<> simd_flag_default;

inline constexpr simd_flags<detail::Convert> simd_flag_convert;

inline constexpr simd_flags<detail::Aligned> simd_flag_aligned;

template <std::size_t N>
requires(support::has_single_bit(N))
inline constexpr simd_flags<detail::Overaligned<N>> simd_flag_overaligned;

// extensions
template <typename T>
inline constexpr simd_flags<detail::ConvertTo<T>> simd_flag_convert_to;

inline constexpr simd_flags<detail::Streaming> simd_flag_streaming;

template <int L1, int L2>
inline constexpr simd_flags<detail::Prefetch<L1, L2>> simd_flag_prefetch;

} // namespace ccm::pp
Loading

0 comments on commit bce06ee

Please sign in to comment.