From bf2260745ee87d8103091c311499a0075ee40d93 Mon Sep 17 00:00:00 2001 From: Martijn Courteaux Date: Fri, 14 Aug 2026 10:41:31 +0200 Subject: [PATCH 1/5] Add regression test for #9333, which revealed more issues, which are fixed here. Move image checks below the user-defined requirements. --- src/BoundsInference.cpp | 23 +++++- test/correctness/vector_store_alignment.cpp | 77 +++++++++++++-------- 2 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/BoundsInference.cpp b/src/BoundsInference.cpp index 8772eed38a19..1620e53d1dab 100644 --- a/src/BoundsInference.cpp +++ b/src/BoundsInference.cpp @@ -1317,22 +1317,39 @@ Stmt bounds_inference(Stmt s, fused_pairs_in_groups.push_back(pairs); } + // Walk past a block of AssertStmts, which are the user-defined requirements. + // We want to use those to further simplify the image_checks which are injected later, + // by moving the inject-markers beyond those asserts. + std::vector stmts; + while (const Block *rb = s.as()) { + if (const auto *a = rb->first.as()) { + stmts.push_back(Stmt(a)); + s = rb->rest; + } else { + break; + } + } + // Add a note in the IR for where the outermost dynamic-stage skipping // checks should go. These are injected in a later pass. Expr marker = Call::make(Int(32), Call::skip_stages_marker, {}, Call::Intrinsic); - s = Block::make(Evaluate::make(marker), s); + stmts.push_back(Evaluate::make(marker)); if (target.has_feature(Target::Profile) || target.has_feature(Target::ProfileByTimer)) { // Add a note in the IR for what profiling should cover, so that it doesn't // include bounds queries as pipeline executions. marker = Call::make(Int(32), Call::profiling_enable_instance_marker, {}, Call::Intrinsic); - s = Block::make(Evaluate::make(marker), s); + stmts.push_back(Evaluate::make(marker)); } + + // Add a note in the IR for where assertions on input images // should go. Those are handled by a later lowering pass. marker = Call::make(Int(32), Call::add_image_checks_marker, {}, Call::Intrinsic); - s = Block::make(Evaluate::make(marker), s); + stmts.push_back(Evaluate::make(marker)); + stmts.push_back(std::move(s)); + s = Block::make(stmts); // Add a synthetic outermost loop to act as 'root'. s = For::make("", 0, 0, ForType::Serial, Partition::Never, DeviceAPI::None, s); diff --git a/test/correctness/vector_store_alignment.cpp b/test/correctness/vector_store_alignment.cpp index 2ae7c603b382..7f8605ecee0b 100644 --- a/test/correctness/vector_store_alignment.cpp +++ b/test/correctness/vector_store_alignment.cpp @@ -9,7 +9,7 @@ class CheckVectorStoreAlignment : public IRVisitor { using IRVisitor::visit; void visit(const Store *op) override { - if (op->name == "output" && op->value.type().lanes() == 32) { + if (starts_with(op->name, "output") && op->value.type().lanes() == 32) { found_vector_store = true; all_vector_stores_aligned &= op->alignment.modulus % 32 == 0 && op->alignment.remainder % 32 == 0; @@ -24,40 +24,61 @@ class CheckVectorStoreAlignment : public IRVisitor { } // namespace + int main(int argc, char **argv) { - Var x{"x"}, y{"y"}, xi{"xi"}, yi{"yi"}; - ImageParam input{UInt(16), 2, "input"}; + Target target = get_jit_target_from_environment() + //.with_feature(Target::NoAsserts) + .with_feature(Target::NoBoundsQuery) + .with_feature(Target::NoRuntime); - Func output{"output"}; - output(x, y) = input(x, y); + // We'll run the test twice, but with a different way of expressing + // the dense storage constraints: + // - once with dim(1).set_stride(dim(0).extent()) + // - once with add_requirement(dim(1).stride() == dim(0).extent()) + for (int i = 0; i < 2; ++i) { + Var x{"x"}, y{"y"}, xi{"xi"}, yi{"yi"}; + ImageParam input{UInt(16), 2, "input"}; - output.align_extent(x, 32) - .tile(x, y, xi, yi, 32, 4) - .vectorize(xi); + Func output{"output"}; + output(x, y) = input(x, y); - output.output_buffer().dim(0).set_min(0); - output.output_buffer().dim(1).set_min(0); - output.output_buffer().dim(1).set_stride(output.output_buffer().dim(0).extent()); - output.output_buffer().set_host_alignment(64); + output.align_extent(x, 32) + .align_extent(y, 4) + .tile(x, y, xi, yi, 32, 4) + .vectorize(xi); - Target target = get_jit_target_from_environment() - .with_feature(Target::NoAsserts) - .with_feature(Target::NoBoundsQuery) - .with_feature(Target::NoRuntime); - Module module = output.compile_to_module({input}, "vector_store_alignment", target); - CheckVectorStoreAlignment checker; - for (const LoweredFunc &f : module.functions()) { - f.body.accept(&checker); - } + output.output_buffer().set_host_alignment(64); + if (i == 0) { + printf("Testing Dimension setting tricks\n"); + output.output_buffer().dim(0).set_min(0); + output.output_buffer().dim(1).set_min(0); + output.output_buffer().dim(1).set_stride(output.output_buffer().dim(0).extent()); + } - if (!checker.found_vector_store) { - printf("Did not find the vectorized output store.\n"); - return 1; - } - if (!checker.all_vector_stores_aligned) { - printf("The vectorized output store was not known to be aligned by 32 elements.\n"); - return 1; + Pipeline p(output); + if (i == 1) { + printf("Testing add_requirements\n"); + p.add_requirement(output.output_buffer().dim(0).min() == 0); + p.add_requirement(output.output_buffer().dim(1).min() == 0); + p.add_requirement(output.output_buffer().dim(1).stride() == output.output_buffer().dim(0).extent()); + } + + Module module = p.compile_to_module({input}, "vector_store_alignment", target); + + CheckVectorStoreAlignment checker; + for (const LoweredFunc &f : module.functions()) { + f.body.accept(&checker); + } + + if (!checker.found_vector_store) { + printf("Did not find the vectorized output store.\n"); + return 1; + } + if (!checker.all_vector_stores_aligned) { + printf("The vectorized output store was not known to be aligned by 32 elements.\n"); + return 1; + } } printf("Success!\n"); From 754ff683dc3f95115134dcb3820748bfde29fd7b Mon Sep 17 00:00:00 2001 From: Martijn Courteaux Date: Fri, 14 Aug 2026 10:42:42 +0200 Subject: [PATCH 2/5] Put the actual printed IR in the requirement_failed string. The simplifier was simplifying it down to "false". --- src/IROperator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/IROperator.cpp b/src/IROperator.cpp index a676b7f9f933..f85ccddc5e25 100644 --- a/src/IROperator.cpp +++ b/src/IROperator.cpp @@ -1137,9 +1137,11 @@ Expr unwrap_tags(const Expr &e) { } Expr requirement_failed_error(Expr condition, const std::vector &args) { + std::stringstream cond_str; + cond_str << condition; return Call::make(Int(32), "halide_error_requirement_failed", - {stringify({std::move(condition)}), combine_strings(args)}, + {cond_str.str(), combine_strings(args)}, Call::Extern); } From 6a9e0b76cab0d507f8f42b40ec3ed6ef88ad03a0 Mon Sep 17 00:00:00 2001 From: "halide-ci[bot]" <266445882+halide-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:53:10 +0000 Subject: [PATCH 3/5] Apply pre-commit auto-fixes --- src/BoundsInference.cpp | 2 -- test/correctness/vector_store_alignment.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/BoundsInference.cpp b/src/BoundsInference.cpp index 1620e53d1dab..9c0213d2d324 100644 --- a/src/BoundsInference.cpp +++ b/src/BoundsInference.cpp @@ -1342,8 +1342,6 @@ Stmt bounds_inference(Stmt s, stmts.push_back(Evaluate::make(marker)); } - - // Add a note in the IR for where assertions on input images // should go. Those are handled by a later lowering pass. marker = Call::make(Int(32), Call::add_image_checks_marker, {}, Call::Intrinsic); diff --git a/test/correctness/vector_store_alignment.cpp b/test/correctness/vector_store_alignment.cpp index 7f8605ecee0b..212618c379ae 100644 --- a/test/correctness/vector_store_alignment.cpp +++ b/test/correctness/vector_store_alignment.cpp @@ -24,7 +24,6 @@ class CheckVectorStoreAlignment : public IRVisitor { } // namespace - int main(int argc, char **argv) { Target target = get_jit_target_from_environment() //.with_feature(Target::NoAsserts) @@ -47,7 +46,6 @@ int main(int argc, char **argv) { .tile(x, y, xi, yi, 32, 4) .vectorize(xi); - output.output_buffer().set_host_alignment(64); if (i == 0) { printf("Testing Dimension setting tricks\n"); From 888c4707cf53bcfc13698f52dd737b413b9d3114 Mon Sep 17 00:00:00 2001 From: Martijn Courteaux Date: Fri, 14 Aug 2026 14:53:13 +0200 Subject: [PATCH 4/5] Fixup the error-codes test. --- .gitignore | 3 +++ test/generator/error_codes_aottest.cpp | 26 ++++++++++++++++-------- test/generator/error_codes_generator.cpp | 6 +++--- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 28cf9d022cd6..ada26e076f1b 100644 --- a/.gitignore +++ b/.gitignore @@ -241,6 +241,9 @@ xcuserdata # NeoVim + clangd .cache +# CCLS +.ccls-cache + # Emacs tags TAGS diff --git a/test/generator/error_codes_aottest.cpp b/test/generator/error_codes_aottest.cpp index 7065dd6156f8..14e14b78fb71 100644 --- a/test/generator/error_codes_aottest.cpp +++ b/test/generator/error_codes_aottest.cpp @@ -5,15 +5,23 @@ #include "error_codes.h" +bool print = false; + void my_halide_error(void *user_context, const char *msg) { // Silently drop the error - // printf("%s\n", msg); + if (print) { + printf("%s\n", msg); + } } void check(int result, int correct) { if (result != correct) { printf("The exit status was %d instead of %d\n", result, correct); exit(1); + } else { + if (print) { + printf("Found expected error: %d\n\n", correct); + } } } @@ -23,17 +31,18 @@ int main(int argc, char **argv) { halide_buffer_t in = {0}, out = {0}; halide_dimension_t shape[] = {{0, 64, 1}, - {0, 123, 64}}; + {0, 123, 64}, + {0, 4, 64 * 123}}; - in.host = (uint8_t *)malloc(64 * 123 * 4); + in.host = (uint8_t *)malloc(64 * 123 * sizeof(int)); in.type = halide_type_of(); in.dim = shape; in.dimensions = 2; - out.host = (uint8_t *)malloc(64 * 123 * 4); + out.host = (uint8_t *)malloc(64 * 123 * 4 * sizeof(int)); out.type = halide_type_of(); out.dim = shape; - out.dimensions = 2; + out.dimensions = 3; // First, a successful run. int result = error_codes(&in, 64, &out); @@ -58,7 +67,8 @@ int main(int argc, char **argv) { // buffer extent negative, but in a way that doesn't trigger oob checks { halide_dimension_t bad_shape[] = {{0, 64, 1}, - {0, -123, 64}}; + {0, 123, 64}, + {0, -4, 64 * 123}}; halide_buffer_t i = in, o = out; i.dim = bad_shape; o.dim = bad_shape; @@ -69,8 +79,8 @@ int main(int argc, char **argv) { } // Input buffer larger than 2GB - halide_dimension_t huge[] = {{0, 10000000, 1}, - {0, 10000000, 64}}; + halide_dimension_t huge[] = {{0, 1 << 30, 1}, + {0, 123, 4}}; in.dim = huge; result = error_codes(&in, 64, &out); correct = halide_error_code_buffer_extents_too_large; diff --git a/test/generator/error_codes_generator.cpp b/test/generator/error_codes_generator.cpp index ce21ca458b2e..dfb3bd93aca5 100644 --- a/test/generator/error_codes_generator.cpp +++ b/test/generator/error_codes_generator.cpp @@ -6,13 +6,13 @@ class ErrorCodes : public Halide::Generator { public: Input> input{"input"}; Input f_explicit_bound{"f_explicit_bound", 1, 0, 64}; - Output> output{"output"}; + Output> output{"output"}; void generate() { assert(!get_target().has_feature(Target::LargeBuffers)); - Var x, y; + Var x, y, z; - output(x, y) = input(x, y); + output(x, y, z) = input(x, y); output.bound(x, 0, f_explicit_bound); add_requirement(input.dim(1).extent() == 123); From 608d23c4a7b03a553d24d331c65db7906f475a2a Mon Sep 17 00:00:00 2001 From: Martijn Courteaux Date: Fri, 14 Aug 2026 18:03:53 +0200 Subject: [PATCH 5/5] fix clang tidy and python test --- python_bindings/test/correctness/basics.py | 5 +++-- src/BoundsInference.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/python_bindings/test/correctness/basics.py b/python_bindings/test/correctness/basics.py index 15bc76087e77..e1853eb05c52 100644 --- a/python_bindings/test/correctness/basics.py +++ b/python_bindings/test/correctness/basics.py @@ -410,12 +410,13 @@ def test_requirements(): delta.set(1) p.realize([10]) - with assert_throws(hl.HalideError, r"Requirement Failed: \(false\)"): + with assert_throws(hl.HalideError, r"Requirement Failed: \(\(delta != 0\)\)"): delta.set(0) p.realize([10]) with assert_throws( - hl.HalideError, r"Requirement Failed: \(false\) negative values are bad -1" + hl.HalideError, + r"Requirement Failed: \(\(delta > 0\)\) negative values are bad -1", ): delta.set(-1) p.realize([10]) diff --git a/src/BoundsInference.cpp b/src/BoundsInference.cpp index 9c0213d2d324..c14ff5898205 100644 --- a/src/BoundsInference.cpp +++ b/src/BoundsInference.cpp @@ -1323,7 +1323,7 @@ Stmt bounds_inference(Stmt s, std::vector stmts; while (const Block *rb = s.as()) { if (const auto *a = rb->first.as()) { - stmts.push_back(Stmt(a)); + stmts.emplace_back(a); s = rb->rest; } else { break;