Skip to content

Commit eb5b1f8

Browse files
committed
Fix stride verification to respect divisibility constraints
_verify_stride_on_original was rejecting valid stride decompositions for B-data and B-scale reads because it probed with arbitrary values that violated divisibility assumptions (e.g. K not a multiple of 256). This made floor/Mod terms produce different results per IV step, causing the verifier to think the stride was non-constant. Pass div_fwd substitutions into the verifier so probed values always satisfy the constraints. This eliminates all residual affine.apply ops for B-data and B-scale index offsets in the loop body (12 per test for 1x4, 28 for 2x2). The only remaining affine.apply in the loop body is a single bounds-check computation unrelated to data/scale index offsets. Made-with: Cursor
1 parent cef03e3 commit eb5b1f8

2 files changed

Lines changed: 30 additions & 23 deletions

File tree

lit_tests/kernel/wave/linearize_loop_affine_maps.py

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@
88
index computation rather than monolithic ``affine.apply`` ops with many
99
operands.
1010
11-
Reads whose flat index is truly affine in the IV (A-data GatherToLDS)
12-
get their stride annotated and codegen emits ``base + IV * stride``.
13-
Reads with complex preshuffle mappings that contain floor/Mod terms
14-
interacting with the IV (B-data, B-scale reads) may still use
15-
``affine.apply`` because the expression is not decomposable as
16-
``base + IV * stride`` in the symbolic domain.
11+
All reads whose flat index involves the loop IV -- A-data, B-data,
12+
A-scale, and B-scale -- get their stride annotated and codegen emits
13+
``base + IV * stride``. A single residual ``affine.apply`` may remain
14+
for the bounds-check computation, but all data/scale index offsets are
15+
hoisted.
1716
1817
Two wave shapes are tested to cover different workgroup tiling patterns:
1918
(1, 4): 64 threads, 1 wave in M, 4 in N -- block (256, 192, 256)
@@ -62,17 +61,13 @@ def test_hoisted_indices_in_loop_1x4():
6261
# Inside the pipelined scf.for, GatherToLDS reads should use
6362
# hoisted base + IV * stride (arith.muli + arith.addi), not
6463
# monolithic affine.apply with 6+ operands.
65-
# B-data reads (complex preshuffle) may still use affine.apply
66-
# because the expression is not decomposable as base + IV * stride.
6764
# CHECK: scf.for
6865
# CHECK: arith.muli
6966
# CHECK: arith.addi
7067
# CHECK: gather_to_lds
71-
# KEY CHECK -- do not remove. The affine.apply count and
72-
# CHECK-NOT verify that hoisting is working: only the
73-
# residual B-data/B-scale preshuffle reads (with floor/Mod
74-
# IV interaction) remain as affine.apply in the loop body.
75-
# CHECK-COUNT-12: affine.apply
68+
# KEY CHECK -- do not remove. After gather_to_lds there
69+
# must be NO affine.apply ops; all data/scale index offsets
70+
# (A-data, B-data, A-scale, B-scale) are fully hoisted.
7671
# CHECK-NOT: affine.apply
7772
# CHECK: scf.yield
7873

@@ -88,10 +83,8 @@ def test_hoisted_indices_in_loop_2x2():
8883
# CHECK: arith.muli
8984
# CHECK: arith.addi
9085
# CHECK: gather_to_lds
91-
# KEY CHECK -- do not remove. The affine.apply count and
92-
# CHECK-NOT verify that hoisting is working: only the
93-
# residual B-data/B-scale preshuffle reads (with floor/Mod
94-
# IV interaction) remain as affine.apply in the loop body.
95-
# CHECK-COUNT-28: affine.apply
86+
# KEY CHECK -- do not remove. After gather_to_lds there
87+
# must be NO affine.apply ops; all data/scale index offsets
88+
# are fully hoisted.
9689
# CHECK-NOT: affine.apply
9790
# CHECK: scf.yield

wave_lang/kernel/wave/analysis/annotate_iv_strides.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,23 +168,35 @@ def _eval2(iv_val):
168168
return base, sympy.Integer(stride_val)
169169

170170

171-
def _verify_stride_on_original(flat, iv_sym, step, base, stride):
171+
def _verify_stride_on_original(flat, iv_sym, step, base, stride, div_fwd=None):
172172
"""Numerically verify ``flat(iv=step*i) == base + i*stride``.
173173
174174
The divisibility-substitution path extracts the stride from a
175175
simplified expression but the base comes from the original.
176176
Floor/Mod interactions with the IV can invalidate this
177177
decomposition, so we probe several concrete IV values.
178+
179+
When *div_fwd* is given, the divisibility substitutions are applied
180+
before probing so that the generated probe values satisfy the
181+
constraints (e.g. K is always a multiple of 256). Without this,
182+
expressions with ``floor(IV * C / K)`` would get probed with
183+
arbitrary K, giving wrong floor results and rejecting valid strides.
178184
"""
179-
free_syms = sorted(flat.free_symbols - {iv_sym}, key=str)
185+
fwd_dict = dict(div_fwd) if div_fwd else {}
186+
187+
flat_p = sympy.sympify(flat).subs(fwd_dict) if fwd_dict else flat
188+
base_p = sympy.sympify(base).subs(fwd_dict) if fwd_dict else base
189+
stride_p = sympy.sympify(stride).subs(fwd_dict) if fwd_dict else stride
190+
191+
free_syms = sorted(flat_p.free_symbols - {iv_sym}, key=str)
180192
probe_map = {s: 137 + i * 31 for i, s in enumerate(free_syms)}
181193
probe_map2 = {s: 251 + i * 47 for i, s in enumerate(free_syms)}
182194

183195
for pmap in (probe_map, probe_map2):
184196
for i in range(8):
185197
iv_val = step * i
186-
orig = subs_idxc(flat.subs({iv_sym: iv_val})).subs(pmap)
187-
expected = subs_idxc(base + i * stride).subs(pmap)
198+
orig = subs_idxc(flat_p.subs({iv_sym: iv_val})).subs(pmap)
199+
expected = subs_idxc(base_p + i * stride_p).subs(pmap)
188200
try:
189201
if int(orig) != int(expected):
190202
return False
@@ -234,7 +246,9 @@ def _try_with_div_subs(flat, iv_sym, step, div_fwd, div_bwd):
234246

235247
base = safe_subs(flat, {iv_sym: sympy.Integer(0)})
236248

237-
if not _verify_stride_on_original(flat, iv_sym, step, base, stride):
249+
if not _verify_stride_on_original(
250+
flat, iv_sym, step, base, stride, div_fwd=div_fwd
251+
):
238252
return None
239253

240254
return base, stride, method

0 commit comments

Comments
 (0)