Add change_type() directive with reduction-aware overflow checks - #9257
Open
alexreinking wants to merge 9 commits into
Open
Add change_type() directive with reduction-aware overflow checks#9257alexreinking wants to merge 9 commits into
alexreinking wants to merge 9 commits into
Conversation
alexreinking
force-pushed
the
alexreinking/change-type
branch
2 times, most recently
from
July 29, 2026 19:59
75752de to
6f0a76e
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## alexreinking/eager-inline #9257 +/- ##
=============================================================
+ Coverage 70.24% 70.25% +0.01%
=============================================================
Files 257 258 +1
Lines 79122 79605 +483
Branches 18957 19111 +154
=============================================================
+ Hits 55578 55926 +348
- Misses 17895 17939 +44
- Partials 5649 5740 +91 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
alexreinking
force-pushed
the
alexreinking/change-type
branch
3 times, most recently
from
July 30, 2026 15:20
f352290 to
0fdc17c
Compare
abadams
reviewed
Jul 30, 2026
alexreinking
force-pushed
the
alexreinking/change-type
branch
from
July 30, 2026 20:11
53d147e to
085bf96
Compare
alexreinking
force-pushed
the
alexreinking/change-type
branch
from
July 31, 2026 15:49
085bf96 to
9458298
Compare
alexreinking
force-pushed
the
alexreinking/change-type
branch
from
July 31, 2026 21:30
5376049 to
daf9049
Compare
alexreinking
marked this pull request as ready for review
August 1, 2026 03:18
abadams
reviewed
Aug 3, 2026
abadams
reviewed
Aug 3, 2026
abadams
reviewed
Aug 3, 2026
alexreinking
force-pushed
the
alexreinking/change-type
branch
from
August 4, 2026 19:39
daf9049 to
4e4989d
Compare
alexreinking
force-pushed
the
alexreinking/change-type
branch
from
August 4, 2026 21:37
4e4989d to
4237483
Compare
Add Func::change_type(Type, unsafe), which changes the type at which a Func computes and stores its values. It works eagerly at schedule time by splitting the Func in two: a returned intermediate that copies the Func's definitions but accumulates at the new type (inserting casts, preferring integer forms like widening_mul over float round-trips), and the original Func, rewritten in place into an inline wrapper that casts the intermediate's result back to the original type so every existing consumer is unaffected. Safety is validated with the bounds machinery: for an integer target, change_type() bounds the accumulator by combining the per-term value range (constant_integer_bounds augmented by FuncValueBounds) with the reduction extent. Statically-safe cases pass silently; a case provable only under a runtime precondition (symbolic RDom extent) records that condition, which a new lowering pass (add_type_change_checks, modeled on add_split_factor_checks) injects into the pipeline's assertion block and no_asserts strips. Otherwise change_type() errors unless unsafe=true. Supporting changes: Function::clear_definition() to redefine a Func in place as the wrapper; FuncSchedule carries the injected type_change_checks; get_associative_identity() for retyped reduction identities; StrictifyFloat treats int<->float casts as strict so change_type() won't strip a user's strict_cast. Adds as_binary_operands()/make_binary_op() and select_binary_operand() as reusable binary-operator helpers, placed early in Func.cpp (alongside project_rdom()) so hoist_invariants() can reuse all three without redefining them. Adds a Python binding.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
change_type_prove_safe() was only invoked for integer targets, so retyping a reduction to a float type got no safety check at all. Run it for float targets too, bounding the accumulation against the largest integer the target can represent exactly (e.g. 2048 for float16) rather than its full dynamic range. Also fixes bounds_of() to recover the exact integer value of a leaf that retype_leaf() constant-folded directly into a float literal (e.g. a seed of 0), which it previously treated as unbounded. Adds test coverage for float targets, and for sum-then-clamp, sum-scan, and histogram reductions confirming the existing extent-based bound is conservative for those shapes too.
alexreinking
force-pushed
the
alexreinking/change-type
branch
from
August 4, 2026 22:13
4237483 to
db7edbe
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a new
.change_type()directive to Halide that takes a typeSand replaces a funcFof typeTwith:Where
e' : Sis a type-adjusted version ofe : Tsuch thate = cast(T, e').We use
FuncValueBoundsandlossless_castto ensure that the replaced type will not perturb values in the pipeline. The directive is aware of reductions and will add runtime asserts to ensure that accumulations are not too long if the extent cannot be proven sufficiently short.When constructing
e',change_typemight adjust the form to use widening intrinsics. For instance, the result of changing the type off32(i8a) * f32(i8b)to Int32 would becast<int32>(widening_mul(i8a, i8b)), which is bitwise exact.A prototype of this was written over several sessions involving several LLMs. I probably wrote as much prompt as code was produced, and I feel that the ultimate implementation is more mine than any LLM's. The tests were written by machine outright, however.
Breaking changes
None: this is a new directive.
Checklist
Stack created with GitHub Stacks CLI • Give Feedback 💬