Skip to content

hoist_invariants: decide invariance modulo the RVar bounds - #9521

Draft
alexreinking wants to merge 4 commits into
alexreinking/multiramp-alignment-stackedfrom
alexreinking/hoist-invariants-bounds
Draft

alexreinking wants to merge 4 commits into
alexreinking/multiramp-alignment-stackedfrom
alexreinking/hoist-invariants-bounds

Conversation

@alexreinking

@alexreinking alexreinking commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Stage::hoist_invariants() decided invariance syntactically, so per-block factors in blocked reductions were never hoisted:

out() += cast<float>(data(r)) * scale(r / 32);
out.update().split(r, ko, ki, 32);
Func blocks = out.update().rfactor(ko, u);
blocks.update().hoist_invariants();  // user error before this PR

The intermediate reads scale(promise_clamped(u*32 + ki, ...) / 32), which is scale(u) for every ki in [0, 32).

Now a leaf that mentions RVars has its integer (never float) RVar-dependent subexpressions simplified under the RVars' constant bounds, and is hoisted if the result is RVar-free. rfactor's internal promise_clamped is dropped for this, along with the likely() tag on its value; user promises are kept. When the RVar bounds aren't enough (e.g. a lane var that was an RVar before rfactor), constant bound()s on the Func's pure vars are used too, and the factor is wrapped in a require() so that widening the bound() later fails loudly instead of silently using a wrong factor.

Depends on #9520 for the per-lane case, scale((lane*4 + ki) / 32).

Tests: new cases in correctness_hoist_invariants.

Breaking changes

No API changes. Some pipelines that used to error now hoist, and pipelines that already hoisted may hoist more leaves, which can change float rounding (as hoisting already does).


Authored by GitHub Copilot

@alexreinking
alexreinking added this pull request to stack #9522 October 6, 2026 19:34
@alexreinking alexreinking changed the title alexreinking/hoist invariants bounds hoist_invariants: decide invariance modulo the RVar bounds Oct 6, 2026
alexreinking added a commit that referenced this pull request Oct 6, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 6, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov

codecov Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.89189% with 6 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (alexreinking/multiramp-alignment-stacked@5addfc1). Learn more about missing BASE report.

Files with missing lines Patch % Lines
src/Func.cpp 91.89% 1 Missing and 5 partials ⚠️
Additional details and impacted files
@@                             Coverage Diff                             @@
##             alexreinking/multiramp-alignment-stacked    #9521   +/-   ##
===========================================================================
  Coverage                                            ?   72.23%           
===========================================================================
  Files                                               ?      262           
  Lines                                               ?    81949           
  Branches                                            ?    20026           
===========================================================================
  Hits                                                ?    59193           
  Misses                                              ?    16559           
  Partials                                            ?     6197           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@alexreinking
alexreinking force-pushed the alexreinking/hoist-invariants-bounds branch from 8fb4198 to a909d91 Compare October 7, 2026 06:39
alexreinking added a commit that referenced this pull request Oct 7, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 7, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking removed this pull request from stack #9522 October 7, 2026 16:08
alexreinking added a commit that referenced this pull request Oct 7, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 7, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking added this pull request to stack #9524 October 7, 2026 16:08
alexreinking added a commit that referenced this pull request Oct 7, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 7, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 7, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 7, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking removed this pull request from stack #9524 October 7, 2026 19:21
alexreinking added a commit that referenced this pull request Oct 7, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 7, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking added this pull request to stack #9527 October 7, 2026 19:26
@alexreinking
alexreinking removed this pull request from stack #9527 October 8, 2026 11:09
alexreinking added a commit that referenced this pull request Oct 8, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 8, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking added this pull request to stack #9529 October 8, 2026 11:10
alexreinking added a commit that referenced this pull request Oct 8, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 8, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking removed this pull request from stack #9529 October 8, 2026 14:37
alexreinking added a commit that referenced this pull request Oct 16, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking removed this pull request from stack #9540 October 16, 2026 19:47
@alexreinking
alexreinking force-pushed the alexreinking/hoist-invariants-bounds branch from bec4652 to 2903e64 Compare October 16, 2026 19:47
@alexreinking
alexreinking added this pull request to stack #9559 October 16, 2026 19:47
alexreinking added a commit that referenced this pull request Oct 16, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 16, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking removed this pull request from stack #9559 October 16, 2026 19:50
@alexreinking
alexreinking force-pushed the alexreinking/hoist-invariants-bounds branch from 2903e64 to 3ea6ebe Compare October 16, 2026 19:50
@alexreinking
alexreinking changed the base branch from alexreinking/simplify-div-bounded-offset to alexreinking/rfactor-tail-allocation October 16, 2026 19:50
alexreinking added a commit that referenced this pull request Oct 16, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking added this pull request to stack #9560 October 16, 2026 19:50
alexreinking added a commit that referenced this pull request Oct 16, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking force-pushed the alexreinking/hoist-invariants-bounds branch from 3ea6ebe to f60b2a3 Compare October 16, 2026 20:33
alexreinking added a commit that referenced this pull request Oct 16, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 16, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 16, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking force-pushed the alexreinking/hoist-invariants-bounds branch from f60b2a3 to 3e42629 Compare October 16, 2026 21:49
alexreinking added a commit that referenced this pull request Oct 16, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking removed this pull request from stack #9560 October 16, 2026 22:00
@alexreinking
alexreinking changed the base branch from alexreinking/rfactor-tail-allocation to alexreinking/multiramp-alignment-stacked October 16, 2026 22:00
@alexreinking
alexreinking force-pushed the alexreinking/hoist-invariants-bounds branch from 3e42629 to 8157e26 Compare October 16, 2026 22:00
@alexreinking
alexreinking added this pull request to stack #9561 October 16, 2026 22:00
alexreinking added a commit that referenced this pull request Oct 16, 2026
kernels/matmul.cpp: out(n, m) = sum_k W(k, n) * X(k, m) in f32, each
operand approximated by its format's scheme and severed at its records.
kernels/schedule.h: vec_dot for two block-quantized operands is rfactor
-> eager_inline -> hoist_invariants -> change_type(Int(32)) -> lanes,
which lowers to GGML's ggml_vec_dot_q4_0_q8_0 Neon loop (sdot, per-lane
f32 scale-accumulate, two blocks per iteration); otherwise decode + FMA.
Formats gain values per record (schemes.h) and acts (formats.cmake); the
kernel ABI is struct-typed records [K / block, N | M] whose types come
from the kernel metadata; the vec_dot adapter reuses static buffers.
q8_0 vec_dot comes with zero kernel changes.

Needs the framework's FR-1 (rank-polymorphic BlockReshape/StructLayout,
contextual signature) and hoist_invariants modulo RVar bounds (#9521).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking added a commit that referenced this pull request Oct 16, 2026
hoist_invariants() takes an explicit constant bound on a pure var as its
range (#9521); the rfactor'd lane is the in-block vector lane, extent
vector size / dot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexreinking and others added 4 commits October 24, 2026 13:00
hoist_invariants() decided whether a factor was loop-invariant
syntactically. In a blocked reduction with one factor per block, such as
a block-scaled dot product sum_k(scale(k / 32) * data(k)), the natural
schedule splits k by the block size and rfactors the block index:

    out.update().split(r, ko, ki, 32);
    Func blocks = out.update().rfactor(ko, u);
    blocks.update().hoist_invariants();

The intermediate then reads scale((u*32 + ki) / 32), which equals
scale(u) for every ki in [0, 32), but mentions ki, so nothing was hoisted
and hoist_invariants() raised a user error.

Before partitioning the leaves of the inner-op chain, a leaf that is not
syntactically RVar-free now has its float-free RVar-dependent
subexpressions simplified under the RVars' constant bounds. If the
result is RVar-free, it becomes the hoisted factor; otherwise the
original leaf stays in the body unchanged. This is sound for any tail
strategy, since the RDom bounds hold for every iteration the reduction
runs. Floating-point math is never rewritten. The internal promises
rfactor() wraps around a split RVar's index are dropped while
simplifying, but promises on RVar-invariant values and a user's
unsafe_promise_clamped on an RVar-dependent value are kept, so bounds
inference still has them.

Tests cover divisible, symbolic-tail, and constant-tail extents, the
min/+ and ||/&& laws, a promised data-dependent block index, an RDom
starting off a block boundary (block-aligned factor hoisted, misaligned
one kept), and a finer-grained factor that must not be hoisted.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split the reduction into blocks of 32 and then lanes of 4, and rfactor both
the block and the lane, so each lane keeps its own partial sum. The per-block
factor's index then only simplifies to lane/8 + u given the bound on the
innermost RVar, which needs the (x*c0 + y)/c1 simplifier rule. Check that the
factor is hoisted, with divisible and non-divisible extents.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A blocked reduction whose block is processed as several chunks of lanes,
e.g. split(r, ro, ri, 32).split(ri, rc, rl, 16).split(rl, rv, rw, 4)
followed by rfactor({{rv, v}, {ro, u}}), leaves the per-block factor
reading s((u*32 + rc*16 + v*4 + rw) / 32). That is s(u) only given that
the pure var v (formerly an RVar) is in [0, 4), which nothing recorded,
so hoist_invariants() rejected it.

hoist_invariants() now also uses explicit bound(var, min, extent)
directives with constant min and extent on the Func's pure vars as the
ranges of those vars when simplifying leaves. bound() fixes the region
the Func is computed over, and Halide asserts consumers need no more
than that. bound_extent(), align_bounds() and bound_storage() are not
used.

RVar bounds alone are tried first. A factor that is invariant only given
the pure-var bounds is wrapped in a require() on those vars. The require
simplifies away while the bound stands. If bound() is widened after
hoisting, the require turns that into a runtime error instead of a wrong
result.

Tests cover the two-chunk block with divisible and tail extents, the
error without bound() or with a too-wide bound, and reads outside the
bound both with the bound unchanged and with it widened after hoisting.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rfactor() now tags the clamped RVar itself, promise_clamped(likely(r), ...).
Removing the promise but keeping likely() hid the index from the simplifier,
so e.g. (likely(u*32 + ki))/32 no longer simplified to u and the per-block
factor was not hoisted (correctness_hoist_invariants, block index cases).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip_buildbots Do not run buildbots on this PR. Must add before opening PR as we scan labels immediately.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant