@@ -134,39 +134,6 @@ inline void store(Simd<T,W> v, T* p, element_aligned_tag = {}) {
134134 v.copy_to (p, element_aligned);
135135}
136136
137- // Unmasked gather: result[i] = ptr[idx[i]] for all lanes.
138- // IdxT may be int32_t or int64_t; the compiler selects the matching hardware
139- // instruction (vpgatherdps/vpgatherdq for 32-bit idx, vpgatherqq for 64-bit idx).
140- template <typename T, typename IdxT, int W>
141- inline Simd<T,W> gather (const T* __restrict__ ptr, Simd<IdxT,W> idx) {
142- return Simd<T,W>([&](int i) { return ptr[idx[i]]; });
143- }
144-
145- // Masked gather: result[i] = mask[i] ? ptr[idx[i]] : fallback.
146- // Implemented as a full gather + where-blend; ptr is accessed for ALL lanes,
147- // so every idx[i] must be a valid offset regardless of mask[i].
148- template <typename T, typename IdxT, int W>
149- inline Simd<T,W> gather (SimdMask<T,W> mask, const T* __restrict__ ptr,
150- Simd<IdxT,W> idx, T fallback = T(0 )) {
151- auto result = Simd<T,W>(fallback);
152- stdx::where (mask, result) = Simd<T,W>([&](int i) { return ptr[idx[i]]; });
153- return result;
154- }
155-
156- // Merge-masked gather: dst[i] = mask[i] ? ptr[idx[i]] : dst[i] (unchanged).
157- // MaskElemT may differ from T (heterogeneous mask, e.g. SimdMask<uint32_t,W>
158- // applied to Simd<uint64_t,W> data). When T==MaskElemT, delegates directly
159- // to stdx::where; otherwise uses the WhereExpression boolean round-trip.
160- template <typename T, typename IdxT, typename MaskElemT, int W>
161- inline void gather_if (Simd<T,W>& dst, SimdMask<MaskElemT,W> mask,
162- const T* __restrict__ ptr, Simd<IdxT,W> idx) {
163- if constexpr (std::is_same_v<T, MaskElemT>) {
164- stdx::where (mask, dst) = Simd<T,W>([&](int i) { return ptr[idx[i]]; });
165- } else {
166- where (mask, dst) = Simd<T,W>([&](int i) { return ptr[idx[i]]; });
167- }
168- }
169-
170137// ===========================================================================
171138// Implementation B: std::array backend (default)
172139// ===========================================================================
@@ -346,44 +313,16 @@ __hostdev__ void store(Simd<T,W> v, T* p, element_aligned_tag = {}) {
346313 v.store (p);
347314}
348315
349- // Unmasked gather: result[i] = ptr[idx[i]] for all lanes.
350- template <typename T, typename IdxT, int W>
351- __hostdev__ Simd<T,W> gather (const T* __restrict__ ptr, Simd<IdxT,W> idx) {
352- Simd<T,W> r;
353- for (int i = 0 ; i < W; i++) r[i] = ptr[idx[i]];
354- return r;
355- }
356-
357- // Masked gather: result[i] = mask[i] ? ptr[idx[i]] : fallback.
358- // Scalar path: accesses ptr only for true lanes (ternary short-circuits).
359- template <typename T, typename IdxT, int W>
360- __hostdev__ Simd<T,W> gather (SimdMask<T,W> mask, const T* __restrict__ ptr,
361- Simd<IdxT,W> idx, T fallback = T(0 )) {
362- Simd<T,W> r;
363- for (int i = 0 ; i < W; i++) r[i] = mask[i] ? ptr[idx[i]] : fallback;
364- return r;
365- }
366-
367- // Merge-masked gather: dst[i] = mask[i] ? ptr[idx[i]] : dst[i] (unchanged).
368- // MaskElemT may differ from T (heterogeneous mask).
369- // Scalar path: only accesses ptr for true lanes.
370- template <typename T, typename IdxT, typename MaskElemT, int W>
371- __hostdev__ void gather_if (Simd<T,W>& dst, SimdMask<MaskElemT,W> mask,
372- const T* __restrict__ ptr, Simd<IdxT,W> idx) {
373- for (int i = 0 ; i < W; i++)
374- if (mask[i]) dst[i] = ptr[idx[i]];
375- }
376-
377316#endif // NANOVDB_USE_STD_SIMD
378317
379318// ---------------------------------------------------------------------------
380319// simd_cast<DstT> — element-wise static_cast between Simd types of the same W.
381320//
382- // Used for widening (uint16_t → uint32_t, uint32_t → uint64_t) and for
383- // reinterpreting signedness ( uint32_t → int32_t) when building gather indices.
384- // Both backends: the array backend uses a lane loop; the stdx backend uses the
385- // generator constructor, which the compiler lowers to a vpmovsxbw / vpmovzxwd
386- // sequence or similar sign/zero-extend instruction depending on the types.
321+ // Used for widening between integer element types (uint16_t → uint32_t,
322+ // uint32_t → uint64_t). Both backends: the array backend uses a lane loop;
323+ // the stdx backend uses the generator constructor, which the compiler lowers
324+ // to a vpmovsxbw / vpmovzxwd sequence or similar sign/zero-extend instruction
325+ // depending on the types.
387326// Scalar overload: degrades to static_cast for plain scalar types.
388327// ---------------------------------------------------------------------------
389328template <typename DstT, typename SrcT, int W>
@@ -420,39 +359,6 @@ __hostdev__ void simd_cast_if(DstT& dst, bool mask, SrcT src) {
420359 if (mask) dst = static_cast <DstT>(src);
421360}
422361
423- // ---------------------------------------------------------------------------
424- // popcount64 — scalar SWAR popcount, always uses arithmetic (no __builtin_popcountll).
425- //
426- // Safe to call per-lane inside a vectorizable loop: every operation (>>, &, +, -)
427- // maps to an AVX2 instruction for 64-bit elements (vpsrlq, vpand, vpaddq, vpsubq).
428- // The final byte-sum uses a shift-and-add tree instead of the multiply trick
429- // (v * 0x0101...) since 64x64->64 multiply has no AVX2 equivalent (vpmullq is AVX-512).
430- // ---------------------------------------------------------------------------
431- __hostdev__ inline uint64_t popcount64 (uint64_t v)
432- {
433- v -= (v >> 1 ) & uint64_t (0x5555555555555555 );
434- v = (v & uint64_t (0x3333333333333333 )) + ((v >> 2 ) & uint64_t (0x3333333333333333 ));
435- v = (v + (v >> 4 )) & uint64_t (0x0F0F0F0F0F0F0F0F ); // per-byte counts
436- v += v >> 8 ; v &= uint64_t (0x00FF00FF00FF00FF );
437- v += v >> 16 ; v &= uint64_t (0x0000FFFF0000FFFF );
438- v += v >> 32 ;
439- return v & uint64_t (63 );
440- }
441-
442- // Lane-wise SIMD popcount: applies popcount64 to every lane.
443- // Backend A: generator constructor; Backend B: element loop (auto-vectorized by GCC/Clang).
444- template <int W>
445- __hostdev__ Simd<uint64_t ,W> popcount (Simd<uint64_t ,W> v) {
446- #ifdef NANOVDB_USE_STD_SIMD
447- return Simd<uint64_t ,W>([&](int i) { return popcount64 (v[i]); });
448- #else
449- Simd<uint64_t ,W> r;
450- for (int i = 0 ; i < W; ++i) r[i] = popcount64 (v[i]);
451- return r;
452- #endif
453- }
454- __hostdev__ inline uint64_t popcount (uint64_t v) { return popcount64 (v); }
455-
456362// ---------------------------------------------------------------------------
457363// simd_traits — generic per-lane access for scalar and Simd<T,W> types.
458364//
@@ -548,15 +454,5 @@ __hostdev__ ScalarWhereProxy<T> where(bool mask, T& target) {
548454 return {mask, target};
549455}
550456
551- // Unmasked scalar gather: result = ptr[idx].
552- template <typename T, typename IdxT>
553- __hostdev__ T gather (const T* __restrict__ ptr, IdxT idx) { return ptr[idx]; }
554-
555- // Merge-masked scalar gather: dst = ptr[idx] only if mask, else dst unchanged.
556- template <typename T, typename IdxT>
557- __hostdev__ void gather_if (T& dst, bool mask, const T* __restrict__ ptr, IdxT idx) {
558- if (mask) dst = ptr[idx];
559- }
560-
561457} // namespace util
562458} // namespace nanovdb
0 commit comments