native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
bmi2.h
1// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
2#pragma once
3#include "native/config.h"
4#include "native/attributes.h"
5#include "native/isa.h"
6#include <bit>
7#include <cstdint>
8#if NATIVE_HOST_X86
9#include <immintrin.h>
10#endif
11#if NATIVE_HOST_X86
12namespace native::detail {
13 template<class U>
14 constexpr U bmi2_zero_high(U value, unsigned index) noexcept {
15 index &= 255u;
16 return index >= sizeof(U) * 8 ? value : U(value & ((U{1} << index) - U{1}));
17 }
18
19 template<class U>
20 constexpr U bmi2_deposit(U value, U mask) noexcept {
21 U result = 0;
22 for (U bit = 1; mask; bit <<= 1, mask &= mask - U{1})
23 if (value & bit) result |= mask & (U{0} - mask);
24 return result;
25 }
26
27 template<class U>
28 constexpr U bmi2_extract(U value, U mask) noexcept {
29 U result = 0;
30 for (U bit = 1; mask; bit <<= 1, mask &= mask - U{1})
31 if (value & (mask & (U{0} - mask))) result |= bit;
32 return result;
33 }
34
35 constexpr std::uint32_t bmi2_multiply(std::uint32_t a, std::uint32_t b,
36 std::uint32_t* high) noexcept {
37 auto product = std::uint64_t{a} * b;
38 *high = static_cast<std::uint32_t>(product >> 32);
39 return static_cast<std::uint32_t>(product);
40 }
41
42 constexpr std::uint64_t bmi2_multiply(std::uint64_t a, std::uint64_t b,
43 std::uint64_t* high) noexcept {
44 // Base-2^32 limbs avoid a nonstandard 128-bit integer requirement.
45 auto a_low = std::uint64_t{static_cast<std::uint32_t>(a)};
46 auto b_low = std::uint64_t{static_cast<std::uint32_t>(b)};
47 auto a_high = a >> 32;
48 auto b_high = b >> 32;
49 auto low_product = a_low * b_low;
50 auto middle = a_high * b_low + (low_product >> 32);
51 auto upper = middle >> 32;
52 middle = std::uint64_t{static_cast<std::uint32_t>(middle)} + a_low * b_high;
53 *high = a_high * b_high + upper + (middle >> 32);
54 return (middle << 32) | static_cast<std::uint32_t>(low_product);
55 }
56}
57
58namespace native {
64
66 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
67 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
68 constexpr std::uint32_t bzhi(std::uint32_t value, unsigned index) noexcept {
69 if (__builtin_is_constant_evaluated()) {
70 return detail::bmi2_zero_high(value, index);
71 } else {
72 return _bzhi_u32(value, index);
73 }
74 }
75
76 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
77 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
78 constexpr std::uint64_t bzhi(std::uint64_t value, unsigned index) noexcept {
79 if (__builtin_is_constant_evaluated()) {
80 return detail::bmi2_zero_high(value, index);
81 } else {
82 return _bzhi_u64(value, index);
83 }
84 }
85
87 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
88 native_nodiscard native_inline __attribute__((target("bmi2")))
89 constexpr std::uint32_t mulx(std::uint32_t a, std::uint32_t b, std::uint32_t* high) noexcept {
90 if (__builtin_is_constant_evaluated()) {
91 return detail::bmi2_multiply(a, b, high);
92 } else {
93 unsigned int upper;
94 auto lower = _mulx_u32(a, b, &upper);
95 *high = upper;
96 return lower;
97 }
98 }
99
100 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
101 native_nodiscard native_inline __attribute__((target("bmi2")))
102 constexpr std::uint64_t mulx(std::uint64_t a, std::uint64_t b, std::uint64_t* high) noexcept {
103 if (__builtin_is_constant_evaluated()) {
104 return detail::bmi2_multiply(a, b, high);
105 } else {
106 // uint64_t can be unsigned long on LP64; the intrinsic requires unsigned long long*.
107 unsigned long long upper;
108 auto lower = _mulx_u64(a, b, &upper);
109 *high = upper;
110 return lower;
111 }
112 }
113
115 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
116 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
117 constexpr std::uint32_t pdep(std::uint32_t value, std::uint32_t mask) noexcept {
118 if (__builtin_is_constant_evaluated()) {
119 return detail::bmi2_deposit(value, mask);
120 } else {
121 return _pdep_u32(value, mask);
122 }
123 }
124
125 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
126 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
127 constexpr std::uint64_t pdep(std::uint64_t value, std::uint64_t mask) noexcept {
128 if (__builtin_is_constant_evaluated()) {
129 return detail::bmi2_deposit(value, mask);
130 } else {
131 return _pdep_u64(value, mask);
132 }
133 }
134
136 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
137 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
138 constexpr std::uint32_t pext(std::uint32_t value, std::uint32_t mask) noexcept {
139 if (__builtin_is_constant_evaluated()) {
140 return detail::bmi2_extract(value, mask);
141 } else {
142 return _pext_u32(value, mask);
143 }
144 }
145
146 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
147 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
148 constexpr std::uint64_t pext(std::uint64_t value, std::uint64_t mask) noexcept {
149 if (__builtin_is_constant_evaluated()) {
150 return detail::bmi2_extract(value, mask);
151 } else {
152 return _pext_u64(value, mask);
153 }
154 }
155
157 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
158 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
159 constexpr std::uint32_t shlx(std::uint32_t value, unsigned count) noexcept {
160 return value << (count & 31u);
161 }
162
163 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
164 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
165 constexpr std::uint64_t shlx(std::uint64_t value, unsigned count) noexcept {
166 return value << (count & 63u);
167 }
168
170 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
171 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
172 constexpr std::uint32_t shrx(std::uint32_t value, unsigned count) noexcept {
173 return value >> (count & 31u);
174 }
175
176 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
177 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
178 constexpr std::uint64_t shrx(std::uint64_t value, unsigned count) noexcept {
179 return value >> (count & 63u);
180 }
181
183 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
184 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
185 constexpr std::int32_t sarx(std::int32_t value, unsigned count) noexcept {
186 return value >> (count & 31u);
187 }
188
189 template<isa<x86> Arch> requires(Arch.has(x86_feature::bmi2))
190 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
191 constexpr std::int64_t sarx(std::int64_t value, unsigned count) noexcept {
192 return value >> (count & 63u);
193 }
194
196 template<isa<x86> Arch, unsigned Imm8> requires(Arch.has(x86_feature::bmi2) && Imm8 <= 255)
197 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
198 constexpr std::uint32_t rorx(std::uint32_t value) noexcept {
199 return std::rotr(value, int(Imm8));
200 }
201
202 template<isa<x86> Arch, unsigned Imm8> requires(Arch.has(x86_feature::bmi2) && Imm8 <= 255)
203 native_nodiscard native_inline native_const __attribute__((target("bmi2")))
204 constexpr std::uint64_t rorx(std::uint64_t value) noexcept {
205 return std::rotr(value, int(Imm8));
206 }
207
208
210 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
212 consteval std::uint32_t bzhi(std::uint32_t value, unsigned index) noexcept {
213 return bzhi<isa<x86>{x86_feature::bmi2}>(value, index);
214 }
215
217 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
219 consteval std::uint64_t bzhi(std::uint64_t value, unsigned index) noexcept {
220 return bzhi<isa<x86>{x86_feature::bmi2}>(value, index);
221 }
222
224 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
226 consteval std::uint32_t mulx(std::uint32_t a, std::uint32_t b, std::uint32_t* high) noexcept {
227 return mulx<isa<x86>{x86_feature::bmi2}>(a, b, high);
228 }
229
231 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
233 consteval std::uint64_t mulx(std::uint64_t a, std::uint64_t b, std::uint64_t* high) noexcept {
234 return mulx<isa<x86>{x86_feature::bmi2}>(a, b, high);
235 }
236
238 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
240 consteval std::uint32_t pdep(std::uint32_t value, std::uint32_t mask) noexcept {
241 return pdep<isa<x86>{x86_feature::bmi2}>(value, mask);
242 }
243
245 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
247 consteval std::uint64_t pdep(std::uint64_t value, std::uint64_t mask) noexcept {
248 return pdep<isa<x86>{x86_feature::bmi2}>(value, mask);
249 }
250
252 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
254 consteval std::uint32_t pext(std::uint32_t value, std::uint32_t mask) noexcept {
255 return pext<isa<x86>{x86_feature::bmi2}>(value, mask);
256 }
257
259 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
261 consteval std::uint64_t pext(std::uint64_t value, std::uint64_t mask) noexcept {
262 return pext<isa<x86>{x86_feature::bmi2}>(value, mask);
263 }
264
266 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
268 consteval std::uint32_t shlx(std::uint32_t value, unsigned count) noexcept {
269 return shlx<isa<x86>{x86_feature::bmi2}>(value, count);
270 }
271
273 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
275 consteval std::uint64_t shlx(std::uint64_t value, unsigned count) noexcept {
276 return shlx<isa<x86>{x86_feature::bmi2}>(value, count);
277 }
278
280 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
282 consteval std::uint32_t shrx(std::uint32_t value, unsigned count) noexcept {
283 return shrx<isa<x86>{x86_feature::bmi2}>(value, count);
284 }
285
287 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
289 consteval std::uint64_t shrx(std::uint64_t value, unsigned count) noexcept {
290 return shrx<isa<x86>{x86_feature::bmi2}>(value, count);
291 }
292
294 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
296 consteval std::int32_t sarx(std::int32_t value, unsigned count) noexcept {
297 return sarx<isa<x86>{x86_feature::bmi2}>(value, count);
298 }
299
301 template<isa<x86> Arch> requires(!Arch.has(x86_feature::bmi2))
303 consteval std::int64_t sarx(std::int64_t value, unsigned count) noexcept {
304 return sarx<isa<x86>{x86_feature::bmi2}>(value, count);
305 }
306
308 template<isa<x86> Arch, unsigned Imm8> requires(!Arch.has(x86_feature::bmi2) && Imm8 <= 255)
310 consteval std::uint32_t rorx(std::uint32_t value) noexcept {
311 return rorx<isa<x86>{x86_feature::bmi2}, Imm8>(value);
312 }
313
315 template<isa<x86> Arch, unsigned Imm8> requires(!Arch.has(x86_feature::bmi2) && Imm8 <= 255)
317 consteval std::uint64_t rorx(std::uint64_t value) noexcept {
318 return rorx<isa<x86>{x86_feature::bmi2}, Imm8>(value);
319 }
320
322}
323#endif
Compiler attributes for host code, with shader-safe shared modifiers.
#define native_inline
inline [[always_inline]]
Definition attributes.h:212
#define native_nodiscard
C++17 [[nodiscard]].
Definition attributes.h:189
#define native_const
[[const]] is not const
Definition attributes.h:108
typename mask_traits< std::remove_cvref_t< T > >::type mask
Definition mask_traits.h:22
constexpr std::uint32_t pdep(std::uint32_t value, std::uint32_t mask) noexcept
Parallel bit deposit (PDEP): place the low popcount(mask) bits into mask's set positions,...
Definition bmi2.h:117
constexpr std::uint32_t bzhi(std::uint32_t value, unsigned index) noexcept
Zero bits at and above index's low eight bits; an index at least the width leaves value unchanged.
Definition bmi2.h:68
constexpr std::uint32_t mulx(std::uint32_t a, std::uint32_t b, std::uint32_t *high) noexcept
Return the low product and write the high product to a valid output pointer.
Definition bmi2.h:89
constexpr std::int32_t sarx(std::int32_t value, unsigned count) noexcept
Arithmetic right shift using the low five bits of count, propagating the sign bit.
Definition bmi2.h:185
constexpr std::uint32_t pext(std::uint32_t value, std::uint32_t mask) noexcept
Parallel bit extract (PEXT): gather mask's selected bits into the low bits, in order.
Definition bmi2.h:138
constexpr std::uint32_t shlx(std::uint32_t value, unsigned count) noexcept
Logical left shift using the low five bits of count.
Definition bmi2.h:159
constexpr std::uint32_t shrx(std::uint32_t value, unsigned count) noexcept
Logical right shift using the low five bits of count.
Definition bmi2.h:172
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
constexpr std::uint32_t rorx(std::uint32_t value) noexcept
constexpr int target
First matching requirement, with every later choice checked for shadowing.
Definition isa.h:396
Standard-library adaptations documented here for SIMD value types.