Skip to content

Split struct-element kernel scratch into per-field buffers - #2982

Open
wsmoses wants to merge 1 commit into
mainfrom
pb/split-struct-scratch
Open

Split struct-element kernel scratch into per-field buffers#2982
wsmoses wants to merge 1 commit into
mainfrom
pb/split-struct-scratch

Conversation

@wsmoses

@wsmoses wsmoses commented Aug 26, 2026

Copy link
Copy Markdown
Member

MFEM's reduce-with-location kernels (ArgMin/ArgMax/MinMaxLoc reducers, exercised by the GPU unit-test suite's Reduce * cases) keep their block partials in shared scratch of struct type — memref<256 x !llvm.struct<(f64, i32)>> — which the raiser rejects as a non-primitive alloca.

Every access reaches such scratch through a flat primitive view with a statically determined byte offset inside the struct, so the array-of-structs splits into one primitive scratch per field:

  • affine.load/store through pointer2memref views: the map's byte expression has a constant residue mod the struct size, picking the field ([t*4+2] on an i32 view of an (f64,i32) pair → the i32 field of pair t).
  • plain memref.load/store whose index is an arith chain: a small residue analysis (constants, sums, products, shifts, casts) derives the same field pinning for the tree-reduction phase's computed indices.
  • same-size accesses of a differently-typed field (an i64 move of the f64 half) become arith.bitcasts.
  • wider integer moves covering whole fields (the i64 whole-pair move of an (i32,i32) pair) split into per-field accesses recomposed with shifts.
  • whole-struct llvm.intr.memcpy between struct-strided geps (16-byte pair copies) becomes per-field moves, including geps folded to constant indices or to the scratch base.

Validated end to end on MFEM: general/test_reduction.cpp fully raises and the whole reduction battery (Sum/Mult/BAnd/BOr/Min/Max/MinMax/ArgMin/ArgMax) passes on GPU through raised XLA kernels, exactly.

🤖 Generated with Claude Code

https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD

Reduce-with-location kernels (MFEM's ArgMin/ArgMax/MinMaxLoc reducers)
keep their block partials in shared scratch of struct type --
memref<256 x struct<(f64, i32)>> -- which cannot become a tensor whole.
Every access reaches the scratch through a flat primitive view whose
index pins a fixed byte offset within the struct: affine maps with a
constant residue, plain arith index chains with a derivable residue,
whole-struct integer moves, and fixed-size memcpys between
struct-strided geps. Split the array-of-structs into one primitive
scratch per field and rewrite each access form onto its field buffer,
with same-size type punning kept as bitcasts and struct-wide integer
moves recomposed with shifts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD
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