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/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 8772eed38a19..c14ff5898205 100644 --- a/src/BoundsInference.cpp +++ b/src/BoundsInference.cpp @@ -1317,22 +1317,37 @@ 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.emplace_back(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/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); } diff --git a/test/correctness/vector_store_alignment.cpp b/test/correctness/vector_store_alignment.cpp index 2ae7c603b382..212618c379ae 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; @@ -25,39 +25,58 @@ 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); + 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()); + } - CheckVectorStoreAlignment checker; - for (const LoweredFunc &f : module.functions()) { - f.body.accept(&checker); - } + 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()); + } - 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; + 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"); 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);