Skip to content

Fix unreachable constant-mask load_masked kernels (sse2, neon)#1376

Open
DiamonDinoia wants to merge 3 commits into
xtensor-stack:masterfrom
DiamonDinoia:fix-constant-mask-load-dispatch
Open

Fix unreachable constant-mask load_masked kernels (sse2, neon)#1376
DiamonDinoia wants to merge 3 commits into
xtensor-stack:masterfrom
DiamonDinoia:fix-constant-mask-load-dispatch

Conversation

@DiamonDinoia

Copy link
Copy Markdown
Contributor

The public dispatch in xsimd_batch.hpp calls
kernel::load_masked(mem, mask, convert{}, mode, A{}), but the sse2 (integral/float/double) and neon constant-mask overloads were declared without the convert parameter, so overload resolution always fell through to the generic common kernel, which round-trips partial loads through a zeroed stack buffer.

The public dispatch in xsimd_batch.hpp calls
kernel::load_masked<A>(mem, mask, convert<T>{}, mode, A{}), but the
sse2 (integral/float/double) and neon constant-mask overloads were
declared without the convert<T> parameter, so overload resolution
always fell through to the generic common kernel, which round-trips
partial loads through a zeroed stack buffer.

Evidence the overloads were dead code: the neon body called
broadcast(T(0), A{}) with a non-deducible template parameter A and
could never have compiled; fixed to broadcast<A>(T(0), A{}).

With the fix, a 2-of-4 constant prefix mask load lowers to a single
vmovq (x86, GCC/clang, sse2 through avx512) instead of
GPR->stack->vector, and neon reaches its lane-insert kernel instead
of the generic fallback.

Store kernels already had the correct 5-parameter shape and were
unaffected.
- countr_one()/countl_one() run-count conditions also matched
  non-contiguous masks (e.g. 0b1011 has countr_one()==2), silently
  dropping selected lanes on both load and store. Use exact masks.
- The integral mask()==0x1 branch had no sizeof(T)==1 case and fell
  off the end of the function (UB, crashed test_load_store on
  avx2_128 once the int8 path became reachable). Restructure as an
  else-chain falling through to the common kernel.
- Fix mm_loadu_siN -> _mm_loadu_siN typos (these overloads were
  previously unreachable, so they never compiled).
- Add detail::lowers_to_plain_moves(mask): true when the kernels
  here lower the mask to plain narrow moves (proper prefix or exact
  upper half). Wider archs use it to decide when to delegate.
…ove masks

- Add batch_bool_constant::is_prefix()/is_suffix(): named, unit-tested
  mask-shape predicates replacing ad-hoc bit tests in kernels.
- avx_128 constant-mask load/store forwarded every mask to the runtime
  vmaskmov path ('constant masks gain nothing on a single register').
  Wrong for masks the sse2 kernels lower to plain narrow moves: masked
  stores never store-forward on Intel and are microcoded on AMD, while
  movq/movlps do forward. Measured on a 2D spread RMW loop (Meteor
  Lake): the plain-move tail is ~9% faster end-to-end, -49% split-line
  accesses, -41% store-forward blocks. Delegate those masks to sse2;
  keep the runtime path for masks vmaskmov genuinely helps.
- This also restores the intended lowering for AVX 256-bit constant-mask
  half-recursion, whose half_arch resolves to avx2_128.
@DiamonDinoia DiamonDinoia force-pushed the fix-constant-mask-load-dispatch branch from 1b5c826 to f56f450 Compare July 8, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant