From 4719c989770f37d0971099c7bfafa0520a9f40f8 Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Tue, 4 Aug 2026 15:49:22 -0400 Subject: [PATCH 1/3] Port internal self-tests out of libHalide into test/correctness Halide has historically embedded self-test functions (e.g. Foo::test(), foo_test()) directly in src/*.cpp, invoked from a single test/internal.cpp binary. This moves every one of those test bodies (and their exclusive helper functions) out of libHalide entirely, into standalone test/correctness/*.cpp executables that exercise the same internal APIs via Halide::Internal::, following the same conventions as the rest of the correctness suite. test/internal.cpp and the _test_internal test target are removed; a trivial test/pch_helper.cpp takes over _test_internal's role as the precompiled-header donor for the other test targets. Two internal APIs needed small additions to support this: - CodeGen_C.h gained three accessor functions (codegen_c_test_*) since the binary2cpp-generated blobs its test compares against aren't exported from the shared library. - spirv_ir.cpp gets its own CMake wiring for the internal-only SpirvIR.h header and vendored SPIR-V headers. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/testing-make.yml | 3 - Makefile | 19 +- src/Associativity.cpp | 316 ----------- src/Associativity.h | 2 - src/AutoScheduleUtils.cpp | 30 -- src/AutoScheduleUtils.h | 2 - src/Bounds.cpp | 518 ------------------ src/Bounds.h | 2 - src/CPlusPlusMangle.cpp | 482 ----------------- src/CPlusPlusMangle.h | 2 - src/CSE.cpp | 176 ------ src/CSE.h | 2 - src/CodeGen_C.cpp | 209 +------ src/CodeGen_C.h | 14 +- src/Deinterleave.cpp | 42 -- src/Deinterleave.h | 2 - src/Generator.cpp | 200 ------- src/IREquality.cpp | 84 --- src/IREquality.h | 2 - src/IRMatch.cpp | 36 -- src/IRMatch.h | 2 - src/IRPrinter.cpp | 54 -- src/IRPrinter.h | 2 - src/ModulusRemainder.cpp | 32 -- src/ModulusRemainder.h | 2 - src/Monotonic.cpp | 95 ---- src/Monotonic.h | 2 - src/Reduction.cpp | 79 --- src/Reduction.h | 2 - src/SpirvIR.cpp | 61 --- src/SpirvIR.h | 9 - src/UniquifyVariableNames.cpp | 83 --- src/UniquifyVariableNames.h | 2 - test/CMakeLists.txt | 11 +- test/correctness/CMakeLists.txt | 27 + test/correctness/associativity.cpp | 325 +++++++++++ test/correctness/bounds_internal.cpp | 540 +++++++++++++++++++ test/correctness/codegen_c.cpp | 237 ++++++++ test/correctness/cplusplus_mangle.cpp | 489 +++++++++++++++++ test/correctness/cse.cpp | 179 ++++++ test/correctness/deinterleave_vector.cpp | 45 ++ test/correctness/expr_match.cpp | 41 ++ test/correctness/generator_internal.cpp | 217 ++++++++ test/correctness/ir_equality.cpp | 52 ++ test/correctness/ir_printer.cpp | 59 ++ test/correctness/is_monotonic.cpp | 101 ++++ test/correctness/modulus_remainder.cpp | 39 ++ test/correctness/propagate_estimate.cpp | 37 ++ test/correctness/spirv_ir.cpp | 59 ++ test/correctness/split_predicate.cpp | 87 +++ test/correctness/uniquify_variable_names.cpp | 85 +++ test/internal.cpp | 45 -- test/pch_helper.cpp | 8 + 53 files changed, 2669 insertions(+), 2582 deletions(-) create mode 100644 test/correctness/associativity.cpp create mode 100644 test/correctness/bounds_internal.cpp create mode 100644 test/correctness/codegen_c.cpp create mode 100644 test/correctness/cplusplus_mangle.cpp create mode 100644 test/correctness/cse.cpp create mode 100644 test/correctness/deinterleave_vector.cpp create mode 100644 test/correctness/expr_match.cpp create mode 100644 test/correctness/generator_internal.cpp create mode 100644 test/correctness/ir_equality.cpp create mode 100644 test/correctness/ir_printer.cpp create mode 100644 test/correctness/is_monotonic.cpp create mode 100644 test/correctness/modulus_remainder.cpp create mode 100644 test/correctness/propagate_estimate.cpp create mode 100644 test/correctness/spirv_ir.cpp create mode 100644 test/correctness/split_predicate.cpp create mode 100644 test/correctness/uniquify_variable_names.cpp delete mode 100644 test/internal.cpp create mode 100644 test/pch_helper.cpp diff --git a/.github/workflows/testing-make.yml b/.github/workflows/testing-make.yml index 8755309d4437..e790c71760bb 100644 --- a/.github/workflows/testing-make.yml +++ b/.github/workflows/testing-make.yml @@ -89,9 +89,6 @@ jobs: - run: make build_tests - - run: make test_internal - if: ${{ !cancelled() }} - - run: make test_correctness if: ${{ !cancelled() }} diff --git a/Makefile b/Makefile index 3f874f214d8a..d8a059913ff4 100644 --- a/Makefile +++ b/Makefile @@ -996,7 +996,7 @@ endif endif .PHONY: all -all: distrib test_internal +all: distrib # Depending on which linker we're using, # we need a different invocation to get the @@ -1393,7 +1393,7 @@ test_generator: $(GENERATOR_AOT_TESTS) $(GENERATOR_AOTCPP_TESTS) $(GENERATOR_JIT $(FILTERS_DIR)/rungen_test $(FILTERS_DIR)/registration_test -ALL_TESTS = test_internal test_correctness test_error test_tutorial test_warning test_runtime test_generator +ALL_TESTS = test_correctness test_error test_tutorial test_warning test_runtime test_generator # These targets perform timings of each test. For most tests this includes Halide JIT compile times, and run times. # For generator tests they time the compile time only. The times are recorded in CSV files. @@ -1453,10 +1453,6 @@ $(BIN_DIR)/%/runtime.a: $(BIN_DIR)/runtime.generator @mkdir -p $(@D) $(CURDIR)/$< -r runtime -o $(CURDIR)/$(BIN_DIR)/$* target=$* -$(BIN_DIR)/test_internal: $(ROOT_DIR)/test/internal.cpp $(TEST_DEPS) - @mkdir -p $(@D) - $(CXX) $(TEST_CXX_FLAGS) $< -I$(SRC_DIR) $(TEST_LD_FLAGS) -o $@ - ifneq (,$(shell which flatc)) $(BUILD_DIR)/Deserialization.o : $(BUILD_DIR)/halide_ir.fbs.h $(BUILD_DIR)/Serialization.o : $(BUILD_DIR)/halide_ir.fbs.h @@ -1486,6 +1482,12 @@ $(BIN_DIR)/correctness_halide_buffer: $(ROOT_DIR)/test/correctness/halide_buffer $(BIN_DIR)/correctness_image_io: $(ROOT_DIR)/test/correctness/image_io.cpp $(TEST_DEPS) $(CXX) $(TEST_CXX_FLAGS) $(IMAGE_IO_CXX_FLAGS) -I$(ROOT_DIR)/src/runtime -I$(ROOT_DIR)/test/common $(OPTIMIZE_FOR_BUILD_TIME) $< -I$(INCLUDE_DIR) $(TEST_LD_FLAGS) $(IMAGE_IO_LIBS) -o $@ +# The spirv_ir test needs access to the internal-only SpirvIR.h header and +# the vendored SPIR-V headers used by the Vulkan backend. +$(BIN_DIR)/correctness_spirv_ir: $(ROOT_DIR)/test/correctness/spirv_ir.cpp $(TEST_DEPS) + @mkdir -p $(@D) + $(CXX) $(TEST_CXX_FLAGS) -I$(ROOT_DIR)/src/runtime -I$(ROOT_DIR)/test/common $(OPTIMIZE_FOR_BUILD_TIME) $< -I$(INCLUDE_DIR) -I$(SRC_DIR) $(SPIRV_CXX_FLAGS) $(TEST_LD_FLAGS) -o $@ + # OpenCL runtime correctness test requires runtime.a to be linked. $(BIN_DIR)/$(TARGET)/correctness_opencl_runtime: $(ROOT_DIR)/test/correctness/opencl_runtime.cpp $(RUNTIME_EXPORTED_INCLUDES) $(BIN_DIR)/$(TARGET)/runtime.a @mkdir -p $(@D) @@ -2076,11 +2078,6 @@ $(BIN_DIR)/tutorial_lesson_21_auto_scheduler_run: $(ROOT_DIR)/tutorial/lesson_21 -lHalide $(TEST_LD_FLAGS) $(COMMON_LD_FLAGS) $(IMAGE_IO_LIBS) -o $@ @-echo -test_internal: $(BIN_DIR)/test_internal - @-mkdir -p $(TMP_DIR) - cd $(TMP_DIR) ; $(CURDIR)/$< - @-echo - correctness_%: $(BIN_DIR)/correctness_% @-mkdir -p $(TMP_DIR) cd $(TMP_DIR) ; $(CURDIR)/$< diff --git a/src/Associativity.cpp b/src/Associativity.cpp index 0a2dd839a83d..40a2dd1110b1 100644 --- a/src/Associativity.cpp +++ b/src/Associativity.cpp @@ -447,321 +447,5 @@ AssociativeOp prove_associativity(const string &f, vector args, vector &args, const vector &exprs) { - std::ostringstream stream; - stream << f << "("; - for (size_t i = 0; i < args.size(); ++i) { - stream << args[i]; - if (i != args.size() - 1) { - stream << ", "; - } - } - stream << ") = "; - - if (exprs.size() == 1) { - stream << exprs[0]; - } else if (exprs.size() > 1) { - stream << "Tuple("; - for (size_t i = 0; i < exprs.size(); ++i) { - stream << exprs[i]; - if (i != exprs.size() - 1) { - stream << ", "; - } - } - stream << ")"; - } - return stream.str(); -} - -void check_associativity(const string &f, const vector &args, const vector &exprs, - const AssociativeOp &assoc_op) { - auto result = prove_associativity(f, args, exprs); - internal_assert(result.associative() == assoc_op.associative()) - << "Checking associativity: " << print_args(f, args, exprs) << "\n" - << " Expect is associative: " << assoc_op.associative() << "\n" - << " instead of " << result.associative() << "\n"; - if (assoc_op.associative()) { - map replacement; - for (size_t i = 0; i < assoc_op.size(); ++i) { - internal_assert(equal(result.pattern.identities[i], assoc_op.pattern.identities[i])) - << "Checking associativity: " << print_args(f, args, exprs) << "\n" - << " Index: " << i << "\n" - << " Expect identity: " << assoc_op.pattern.identities[i] << "\n" - << " instead of " << result.pattern.identities[i] << "\n"; - internal_assert(equal(result.xs[i].expr, assoc_op.xs[i].expr)) - << "Checking associativity: " << print_args(f, args, exprs) << "\n" - << " Index: " << i << "\n" - << " Expect x: " << assoc_op.xs[i].expr << "\n" - << " instead of " << result.xs[i].expr << "\n"; - internal_assert(equal(result.ys[i].expr, assoc_op.ys[i].expr)) - << "Checking associativity: " << print_args(f, args, exprs) << "\n" - << " Index: " << i << "\n" - << " Expect y: " << assoc_op.ys[i].expr << "\n" - << " instead of " << result.ys[i].expr << "\n"; - - if (result.xs[i].expr.defined()) { - replacement.emplace(assoc_op.xs[i].var, Variable::make(result.xs[i].expr.type(), result.xs[i].var)); - } - if (result.ys[i].expr.defined()) { - replacement.emplace(assoc_op.ys[i].var, Variable::make(result.ys[i].expr.type(), result.ys[i].var)); - } - } - for (size_t i = 0; i < assoc_op.size(); ++i) { - Expr expected_op = substitute(replacement, assoc_op.pattern.ops[i]); - - internal_assert(equal(result.pattern.ops[i], expected_op)) - << "Checking associativity: " << print_args(f, args, exprs) << "\n" - << " Index: " << i << "\n" - << " Expect bin op: " << expected_op << "\n" - << " instead of " << result.pattern.ops[i] << "\n"; - - debug(5) << "\nExpected op: " << expected_op << "\n"; - debug(5) << "Operator: " << result.pattern.ops[i] << "\n"; - debug(5) << " identity: " << result.pattern.identities[i] << "\n"; - debug(5) << " x: " << result.xs[i].var << " -> " << result.xs[i].expr << "\n"; - debug(5) << " y: " << result.ys[i].var << " -> " << result.ys[i].expr << "\n"; - } - } -} - -} // anonymous namespace - -void associativity_test() { - typedef AssociativeOp::Replacement Replacement; - - { - // Tests for saturating addition - Type t = UInt(8); - Expr x = Variable::make(t, "x"); - Expr y = Variable::make(t, "y"); - Expr x_idx = Variable::make(Int(32), "x_idx"); - Expr f_call_0 = Call::make(t, "f", {x_idx}, Call::CallType::Halide, FunctionPtr(), 0); - - for (const Expr &e : {cast(min(cast(x) + y, 255)), - select(x > 255 - y, make_const(UInt(8), 255), x + y), - select(x < ~y, x + y, make_const(UInt(8), 255)), - saturating_add(x, y), - saturating_add(y, x), - saturating_cast(widening_add(x, y))}) { - check_associativity("f", {x_idx}, {substitute("x", f_call_0, e)}, - AssociativeOp( - AssociativePattern(solve_expression(e, "x").result, - make_const(t, 0), true), - {Replacement("x", f_call_0)}, - {Replacement("y", y)}, - true)); - } - } - - { - // Tests for logical And/Or - Type t = UInt(1); - Expr x = Variable::make(t, "x"); - Expr y = Variable::make(t, "y"); - Expr x_idx = Variable::make(Int(32), "x_idx"); - Expr f_call_0 = Call::make(t, "f", {x_idx}, Call::CallType::Halide, FunctionPtr(), 0); - - // f(x) = y && f(x) - check_associativity("f", {x_idx}, {And::make(y, f_call_0)}, - AssociativeOp( - AssociativePattern(And::make(x, y), const_true(), true), - {Replacement("x", f_call_0)}, - {Replacement("y", y)}, - true)); - - // f(x) = y || f(x) - check_associativity("f", {x_idx}, {Or::make(y, f_call_0)}, - AssociativeOp( - AssociativePattern(Or::make(x, y), const_false(), true), - {Replacement("x", f_call_0)}, - {Replacement("y", y)}, - true)); - } - - { - // Tests for 1D reduction - Type t = Int(32); - Expr x = Variable::make(t, "x"); - Expr y = Variable::make(t, "y"); - Expr z = Variable::make(t, "z"); - Expr rx = Variable::make(t, "rx"); - Expr f_call_0 = Call::make(t, "f", {x}, Call::CallType::Halide, FunctionPtr(), 0); - Expr g_call_0 = Call::make(t, "g", {rx}, Call::CallType::Halide, FunctionPtr(), 0); - - // f(x) = f(x) - check_associativity("f", {x}, {f_call_0}, - AssociativeOp( - AssociativePattern(x, make_const(t, 0), true), - {Replacement("x", f_call_0)}, - {Replacement("", Expr())}, - true)); - - // f(x) = min(f(x), y + int16(z)) - check_associativity("f", {x}, {min(f_call_0, y + Cast::make(Int(16), z))}, - AssociativeOp( - AssociativePattern(min(x, y), t.max(), true), - {Replacement("x", f_call_0)}, - {Replacement("y", y + Cast::make(Int(16), z))}, - true)); - - // f(x) = f(x) + g(rx) + y + z - check_associativity("f", {x}, {y + z + f_call_0}, - AssociativeOp( - AssociativePattern(x + y, make_const(t, 0), true), - {Replacement("x", f_call_0)}, - {Replacement("y", y + z)}, - true)); - - // f(x) = max(y, f(x)) - check_associativity("f", {x}, {max(y, f_call_0)}, - AssociativeOp( - AssociativePattern(max(x, y), t.min(), true), - {Replacement("x", f_call_0)}, - {Replacement("y", y)}, - true)); - - // f(x) = max(f(x) + g(rx), g(rx)) -> not associative - check_associativity("f", {x}, {max(f_call_0 + g_call_0, g_call_0)}, AssociativeOp()); - - // f(x) = max(f(x) + g(rx), f(x) - 3) -> f(x) + max(g(rx) - 3) - check_associativity("f", {x}, {max(f_call_0 + g_call_0, f_call_0 - 3)}, - AssociativeOp( - AssociativePattern(x + y, 0, true), - {Replacement("x", f_call_0)}, - {Replacement("y", max(g_call_0, -3))}, - true)); - - // f(x) = max(max(min(f(x), g(rx) + 2), f(x)), g(rx) + 2) -> can be simplified into max(f(x), g(rx) + 2) - check_associativity("f", {x}, {max(max(min(f_call_0, g_call_0 + 2), f_call_0), g_call_0 + 2)}, - AssociativeOp( - AssociativePattern(max(x, y), t.min(), true), - {Replacement("x", f_call_0)}, - {Replacement("y", g_call_0 + 2)}, - true)); - - // f(x) = max(x0, f(x)) -> x0 may conflict with the wildcard associative op pattern - Expr x0 = Variable::make(t, "x0"); - check_associativity("f", {x}, {max(x0, f_call_0)}, - AssociativeOp( - AssociativePattern(max(x, y), t.min(), true), - {Replacement("x", f_call_0)}, - {Replacement("y", x0)}, - true)); - } - - { - // Tests for multi-dimensional reduction (with mixed types) - Type t = Int(32); - Expr x = Variable::make(t, "x"); - Expr y = Variable::make(t, "y"); - Expr z = Variable::make(t, "z"); - Expr rx = Variable::make(t, "rx"); - - vector ts = {Int(32), Int(32), Float(32)}; - vector xs(3), ys(3), zs(3); - for (size_t i = 0; i < xs.size(); ++i) { - xs[i] = Variable::make(ts[i], "x" + std::to_string(i)); - ys[i] = Variable::make(ts[i], "y" + std::to_string(i)); - zs[i] = Variable::make(ts[i], "z" + std::to_string(i)); - } - - Expr f_call_0 = Call::make(ts[0], "f", {x}, Call::CallType::Halide, FunctionPtr(), 0); - Expr f_call_1 = Call::make(ts[1], "f", {x}, Call::CallType::Halide, FunctionPtr(), 1); - Expr f_call_2 = Call::make(ts[2], "f", {x}, Call::CallType::Halide, FunctionPtr(), 2); - Expr g_call_0 = Call::make(ts[0], "g", {rx}, Call::CallType::Halide, FunctionPtr(), 0); - Expr g_call_1 = Call::make(ts[1], "g", {rx}, Call::CallType::Halide, FunctionPtr(), 1); - - // f(x) = Tuple(f(x)[0], f(x)[2] + z) - check_associativity("f", {x}, {f_call_0, f_call_1 + cast(ts[1], z)}, - AssociativeOp( - AssociativePattern({xs[0], xs[1] + ys[1]}, - {make_const(ts[0], 0), make_const(ts[1], 0)}, - true), - {Replacement("x0", f_call_0), Replacement("x1", f_call_1)}, - {Replacement("", Expr()), Replacement("y1", cast(ts[1], z))}, - true)); - - // f(x) = Tuple(min(f(x)[0], g(rx)), f(x)[1]*g(x)*2, f(x)[2] + z) - check_associativity("f", {x}, {min(f_call_0, g_call_0), f_call_1 * g_call_0 * 2, f_call_2 + cast(ts[2], z)}, - AssociativeOp( - AssociativePattern( - {min(xs[0], ys[0]), xs[1] * ys[1], xs[2] + ys[2]}, - {ts[0].max(), make_const(ts[1], 1), make_const(ts[2], 0)}, - true), - {Replacement("x0", f_call_0), Replacement("x1", f_call_1), Replacement("x2", f_call_2)}, - {Replacement("y0", g_call_0), Replacement("y1", g_call_0 * 2), Replacement("y2", cast(ts[2], z))}, - true)); - - // Complex multiplication: f(x) = Tuple(f(x)[0]*g(r.x)[0] - f(x)[1]*g(r.x)[1], f(x)[0]*g(r.x)[1] + f(x)[1]*g(r.x)[0]) - check_associativity("f", {x}, {f_call_0 * g_call_0 - f_call_1 * g_call_1, f_call_0 * g_call_1 + f_call_1 * g_call_0}, - AssociativeOp( - AssociativePattern( - {xs[0] * ys[0] - ys[1] * xs[1], xs[1] * ys[0] + ys[1] * xs[0]}, - {make_const(ts[0], 1), make_const(ts[1], 0)}, - true), - {Replacement("x0", f_call_0), Replacement("x1", f_call_1)}, - {Replacement("y0", g_call_0), Replacement("y1", g_call_1)}, - true)); - - // 1D argmin: f(x) = Tuple(min(f(x)[0], g(r.x)[0]), select(f(x)[0] < g(r.x)[0], f(x)[1], g(r.x)[1]) - check_associativity("f", {x}, {min(f_call_0, g_call_0), select(f_call_0 < g_call_0, f_call_1, g_call_1)}, - AssociativeOp( - AssociativePattern( - {min(xs[0], ys[0]), select(xs[0] < ys[0], xs[1], ys[1])}, - {ts[0].max(), make_const(ts[1], 0)}, - true), - {Replacement("x0", f_call_0), Replacement("x1", f_call_1)}, - {Replacement("y0", g_call_0), Replacement("y1", g_call_1)}, - true)); - } - - { - Type t = Int(32); - Expr x = Variable::make(t, "x"); - Expr y = Variable::make(t, "y"); - Expr rx = Variable::make(t, "rx"); - Expr ry = Variable::make(t, "ry"); - - vector ts = {UInt(8), Int(32), Int(16), Float(32)}; - vector xs(4), ys(4), zs(4); - for (size_t i = 0; i < xs.size(); ++i) { - xs[i] = Variable::make(ts[i], "x" + std::to_string(i)); - ys[i] = Variable::make(ts[i], "y" + std::to_string(i)); - zs[i] = Variable::make(ts[i], "z" + std::to_string(i)); - } - - Expr f_xy_call_0 = Call::make(ts[0], "f", {x, y}, Call::CallType::Halide, FunctionPtr(), 0); - Expr f_xy_call_1 = Call::make(ts[1], "f", {x, y}, Call::CallType::Halide, FunctionPtr(), 1); - Expr f_xy_call_2 = Call::make(ts[2], "f", {x, y}, Call::CallType::Halide, FunctionPtr(), 2); - Expr f_xy_call_3 = Call::make(ts[3], "f", {x, y}, Call::CallType::Halide, FunctionPtr(), 3); - Expr g_xy_call_0 = Call::make(ts[0], "g", {rx, ry}, Call::CallType::Halide, FunctionPtr(), 0); - - // 2D argmin + sum - // f(x, y) = Tuple(min(f(x, y)[0], g(r.x, r.y)[0]), - // f(x, y)[1] + r.x, - // select(f(x, y)[0] < g(r.x, r.y)[0], f(x)[2], r.x), - // select(f(x, y)[0] < g(r.x, r.y)[0], f(x)[3], r.y)) - check_associativity("f", {x, y}, - {min(f_xy_call_0, g_xy_call_0), - f_xy_call_1 + rx, - select(f_xy_call_0 < g_xy_call_0, f_xy_call_2, cast(Int(16), rx)), - select(f_xy_call_0 < g_xy_call_0, f_xy_call_3, cast(Float(32), ry))}, - AssociativeOp( - AssociativePattern( - {min(xs[0], ys[0]), xs[1] + ys[1], select(xs[0] < ys[0], xs[2], ys[2]), select(xs[0] < ys[0], xs[3], ys[3])}, - {ts[0].max(), make_const(ts[1], 0), make_const(ts[2], 0), make_const(ts[3], 0)}, - true), - {Replacement("x0", f_xy_call_0), Replacement("x1", f_xy_call_1), - Replacement("x2", f_xy_call_2), Replacement("x3", f_xy_call_3)}, - {Replacement("y0", g_xy_call_0), Replacement("y1", rx), - Replacement("y2", cast(Int(16), rx)), Replacement("y3", cast(Float(32), ry))}, - true)); - } - - std::cout << "Associativity test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/Associativity.h b/src/Associativity.h index a28735fca4b5..a17649587cef 100644 --- a/src/Associativity.h +++ b/src/Associativity.h @@ -111,8 +111,6 @@ struct AssociativeOp { AssociativeOp prove_associativity( const std::string &f, std::vector args, std::vector exprs); -void associativity_test(); - } // namespace Internal } // namespace Halide diff --git a/src/AutoScheduleUtils.cpp b/src/AutoScheduleUtils.cpp index 5f4578ee484f..ce625e8a4215 100644 --- a/src/AutoScheduleUtils.cpp +++ b/src/AutoScheduleUtils.cpp @@ -379,35 +379,5 @@ bool inline_all_element_wise_functions(const vector &outputs, return inlined; } -namespace { -void check(const Expr &input, Expr expected) { - Expr result = simplify(substitute_var_estimates(input)); - expected = simplify(expected); - if (!equal(result, expected)) { - internal_error - << "\nsubstitute_var_estimates() failure:\n" - << "Input: " << input << "\n" - << "Result: " << result << "\n" - << "Expected result: " << expected << "\n"; - } -} -} // anonymous namespace - -void propagate_estimate_test() { - Param p; - p.set_estimate(10); - - ImageParam img(Int(32), 2); - img.dim(0).set_estimate(-3, 33); - img.dim(1).set_estimate(5, 55); - - Var x("x"), y("y"); - check(p + x + y, x + y + 10); - check(img.dim(0).min() + img.dim(1).min() + x, x + 2); - check(img.dim(0).extent() + img.dim(1).min() + img.dim(1).extent() * x, 55 * x + 38); - - std::cout << "Propagate estimate test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/AutoScheduleUtils.h b/src/AutoScheduleUtils.h index be97e1a8d906..bf6a71edec64 100644 --- a/src/AutoScheduleUtils.h +++ b/src/AutoScheduleUtils.h @@ -130,8 +130,6 @@ bool inline_all_element_wise_functions(const std::vector &outputs, const std::vector &order, const std::map &env); -void propagate_estimate_test(); - } // namespace Internal } // namespace Halide diff --git a/src/Bounds.cpp b/src/Bounds.cpp index cc773087f437..7aeff2f87bad 100644 --- a/src/Bounds.cpp +++ b/src/Bounds.cpp @@ -3354,523 +3354,5 @@ Expr span_of_bounds(const Interval &bounds) { } } -namespace { - -void check(const Scope &scope, const Expr &e, const Expr &correct_min, const Expr &correct_max) { - FuncValueBounds fb; - Interval result = bounds_of_expr_in_scope(e, scope, fb); - result = simplify(result); - if (!equal(result.min, correct_min)) { - internal_error << "In bounds of " << e << ":\n" - << "Incorrect min: " << result.min << "\n" - << "Should have been: " << correct_min << "\n"; - } - if (!equal(result.max, correct_max)) { - internal_error << "In bounds of " << e << ":\n" - << "Incorrect max: " << result.max << "\n" - << "Should have been: " << correct_max << "\n"; - } -} - -void check_constant_bound(const Scope &scope, const Expr &e, const Expr &correct_min, const Expr &correct_max) { - FuncValueBounds fb; - Interval result = bounds_of_expr_in_scope(e, scope, fb, true); - result = simplify(result); - if (!equal(result.min, correct_min)) { - internal_error << "In find constant bound of " << e << ":\n" - << "Incorrect min constant bound: " << result.min << "\n" - << "Should have been: " << correct_min << "\n"; - } - if (!equal(result.max, correct_max)) { - internal_error << "In find constant bound of " << e << ":\n" - << "Incorrect max constant bound: " << result.max << "\n" - << "Should have been: " << correct_max << "\n"; - } -} - -void check_constant_bound(const Expr &e, const Expr &correct_min, const Expr &correct_max) { - Scope scope; - check_constant_bound(scope, e, correct_min, correct_max); -} - -void constant_bound_test() { - using namespace ConciseCasts; - - { - Param a; - Param b; - check_constant_bound(a >> b, i16(-32768), i16(32767)); - } - - { - Param x("x"), y("y"); - x.set_range(10, 20); - y.set_range(5, 30); - check_constant_bound(clamp(x, 5, 30), 10, 20); - check_constant_bound(clamp(x, 15, 30), 15, 20); - check_constant_bound(clamp(x, 15, 17), 15, 17); - check_constant_bound(clamp(x, 5, 15), 10, 15); - - check_constant_bound(x + y, 15, 50); - check_constant_bound(x - y, -20, 15); - check_constant_bound(x * y, 50, 600); - check_constant_bound(x / y, 0, 4); - - check_constant_bound(select(x > 4, 3 * x - y / 2, max(x + y + 2, x - 20)), 15, 58); - check_constant_bound(select(x < 4, 3 * x - y / 2, max(x + y + 2, x - 20)), 17, 52); - check_constant_bound(select(x >= 11, 3 * x - y / 2, max(x + y + 2, x - 20)), 15, 58); - } - - { - Param x("x"), y("y"); - x.set_range(Expr((uint8_t)10), Expr((uint8_t)20)); - y.set_range(Expr((uint8_t)5), Expr((uint8_t)30)); - check_constant_bound(clamp(x, 5, 30), Expr((uint8_t)10), Expr((uint8_t)20)); - check_constant_bound(clamp(x, 15, 30), Expr((uint8_t)15), Expr((uint8_t)20)); - check_constant_bound(clamp(x, 15, 17), Expr((uint8_t)15), Expr((uint8_t)17)); - check_constant_bound(clamp(x, 5, 15), Expr((uint8_t)10), Expr((uint8_t)15)); - - check_constant_bound(x + y, Expr((uint8_t)15), Expr((uint8_t)50)); - check_constant_bound(x / y, Expr((uint8_t)0), Expr((uint8_t)4)); - - check_constant_bound(select(x > 4, 3 * x - y / 2, max(x + y + 2, x + 20)), - Expr((uint8_t)15), Expr((uint8_t)58)); - check_constant_bound(select(x < 4, 3 * x - y / 2, max(x + y + 2, x + 20)), - Expr((uint8_t)30), Expr((uint8_t)52)); - check_constant_bound(select(x >= 11, 3 * x - y / 2, max(x + y + 2, x + 20)), - Expr((uint8_t)15), Expr((uint8_t)58)); - - // These two overflow - check_constant_bound(x - y, Expr((uint8_t)0), Expr((uint8_t)255)); - check_constant_bound(x * y, Expr((uint8_t)0), Expr((uint8_t)255)); - - check_constant_bound(absd(x, y), Expr((uint8_t)0), Expr((uint8_t)20)); - check_constant_bound(absd(cast(x), cast(y)), Expr((uint16_t)0), Expr((uint16_t)20)); - } - - { - Param x("x"), y("y"); - x.set_range(Expr((float)10), Expr((float)20)); - y.set_range(Expr((float)5), Expr((float)30)); - - check_constant_bound(absd(x, y), Expr((float)0), Expr((float)20)); - } - - { - Param i("i"), x("x"), y("y"), d("d"); - Expr cl = i16(i); - Expr cr1 = i16(x); - Expr cr2 = i16(y); - Expr fraction = (d & (int16_t)((1 << 7) - 1)); - Expr cr = i16((((cr2 - cr1) * fraction) >> 7) + cr1); - - check_constant_bound(absd(cr, cl), Expr((uint16_t)0), Expr((uint16_t)509)); - check_constant_bound(i16(absd(cr, cl)), Expr((int16_t)0), Expr((int16_t)509)); - } - - check_constant_bound(Load::make(Int(32), "buf", 0, Buffer<>(), Parameter(), const_true(), ModulusRemainder()) * 20, - Interval::neg_inf(), Interval::pos_inf()); - - { - // Ensure that unnecessary integer overflow doesn't happen - // in cases involving unsigned integer math - Param e1("e1"); // range 0..0xffff, type=uint16 - Expr e2 = cast(e1); // range 0..0xffff, type=uint32 - Expr e3 = e2 * e2; // range 0..0xfffe0001, type=uint32 - check_constant_bound(e3, Expr((uint32_t)0), Expr((uint32_t)0xfffe0001)); - } - - { - RDom r(0, 4); - - // bounds of an expression with impure >= 32 bit expr will be unbounded - Expr e32 = sum(cast(r.x)); - check_constant_bound(e32, Interval::neg_inf(), Interval::pos_inf()); - - // bounds of an expression with impure < 32 bit expr will be bounds-of-type - Expr e16 = sum(cast(r.x)); - check_constant_bound(e16, Int(16).min(), Int(16).max()); - } - - { - Param x("x"), y("y"); - x.set_range(2, 10); - - check_constant_bound(count_leading_zeros(x), i32(28), i32(30)); - check_constant_bound(count_leading_zeros(cast(x)), i16(12), i16(14)); - - check_constant_bound(count_leading_zeros(y), i32(0), i32(32)); - check_constant_bound(count_leading_zeros(cast(y)), i16(0), i16(16)); - } -} - -void boxes_touched_test() { - Type t = Int(32); - Expr x = Variable::make(t, "x"); - Expr y = Variable::make(t, "y"); - Expr z = Variable::make(t, "z"); - Expr w = Variable::make(t, "w"); - - Scope scope; - scope.push("y", Interval(Expr(0), Expr(10))); - - Stmt stmt = Provide::make("f", {10}, {x, y, z, w}, const_true()); - stmt = IfThenElse::make(y > 4, stmt, Stmt()); - stmt = IfThenElse::make(z > 18, stmt, Stmt()); - stmt = LetStmt::make("w", z + 3, stmt); - stmt = LetStmt::make("z", x + 2, stmt); - stmt = LetStmt::make("x", y + 10, stmt); - - Box expected({Interval(15, 20), Interval(5, 10), Interval(19, 22), Interval(22, 25)}); - Box result = box_provided(stmt, "f", scope); - internal_assert(expected.size() == result.size()) - << "Expect dim size of " << expected.size() - << ", got " << result.size() << " instead\n"; - for (size_t i = 0; i < result.size(); ++i) { - const Interval &correct = expected[i]; - Interval b = result[i]; - b = simplify(b); - if (!equal(correct.min, b.min)) { - internal_error << "In bounds of dim " << i << ":\n" - << "Incorrect min: " << b.min << "\n" - << "Should have been: " << correct.min << "\n"; - } - if (!equal(correct.max, b.max)) { - internal_error << "In bounds of dim " << i << ":\n" - << "Incorrect max: " << b.max << "\n" - << "Should have been: " << correct.max << "\n"; - } - } -} - -} // anonymous namespace - -void bounds_test() { - using namespace Halide::ConciseCasts; - - constant_bound_test(); - - Scope scope; - Var x("x"), y("y"); - scope.push("x", Interval(Expr(0), Expr(10))); - - check(scope, x, 0, 10); - check(scope, x + 1, 1, 11); - check(scope, (x + 1) * 2, 2, 22); - check(scope, x * x, 0, 100); - check(scope, 5 - x, -5, 5); - check(scope, x * (5 - x), -50, 50); // We don't expect bounds analysis to understand correlated terms - check(scope, Select::make(x < 4, x, x + 100), 0, 110); - check(scope, x + y, y, y + 10); - check(scope, x * y, min(y, 0) * 10, max(y, 0) * 10); - check(scope, x / (x + y), -10, 10); - check(scope, 11 / (x + 1), 1, 11); - check(scope, Load::make(Int(8), "buf", x, Buffer<>(), Parameter(), const_true(), ModulusRemainder()), - i8(-128), i8(127)); - check(scope, y + (Let::make("y", x + 3, y - x + 10)), y + 3, y + 23); // Once again, we don't know that y is correlated with x - check(scope, clamp(1000 / (x - 2), x - 10, x + 10), -10, 20); - check(scope, cast(x / 2), u16(0), u16(5)); - check(scope, cast((x + 10) / 2), u16(5), u16(10)); - check(scope, x < 20, make_bool(true), make_bool(true)); - check(scope, x < 5, make_bool(false), make_bool(true)); - check(scope, Broadcast::make(x >= 11, 3), make_bool(false), make_bool(false)); - check(scope, Ramp::make(x + 5, 1, 5) > Broadcast::make(2, 5), make_bool(true), make_bool(true)); - - check(scope, print(x, y), 0, 10); - check(scope, print_when(x > y, x, y), 0, 10); - - check(scope, select(y == 5, 0, 3), select(y == 5, 0, 3), select(y == 5, 0, 3)); - check(scope, select(y == 5, x, -3 * x + 8), select(y == 5, 0, -22), select(y == 5, 10, 8)); - check(scope, select(y == x, x, -3 * x + 8), -22, select(y <= 10 && 0 <= y, 10, 8)); - - check(scope, cast(abs(cast(x ^ y))), 0, 32768); - check(scope, cast(x), 0.0f, 10.0f); - - check(scope, cast(abs(cast(x))), 0, 10); - check(scope, abs(2 + x), u32(2), u32(12)); - check(scope, abs(x - 11), u32(1), u32(11)); - check(scope, abs(x - 5), u32(0), u32(5)); - check(scope, abs(2 + cast(x)), 2.f, 12.f); - check(scope, abs(cast(x) - 11), 1.f, 11.f); - check(scope, abs(cast(x) - 5), 0.f, 5.f); - check(scope, abs(2 + cast(x)), u8(2), u8(12)); - check(scope, abs(cast(x) - 11), u8(1), u8(11)); - check(scope, abs(cast(x) - 5), u8(0), u8(5)); - scope.push("x", Interval(123, Interval::pos_inf())); - check(scope, abs(x), u32(123), Interval::pos_inf()); - scope.pop("x"); - scope.push("x", Interval(Interval::neg_inf(), -123)); - check(scope, abs(x), u32(123), Interval::pos_inf()); - scope.pop("x"); - - // Check some vectors - check(scope, Ramp::make(x * 2, 5, 5), 0, 40); - check(scope, Broadcast::make(x * 2, 5), 0, 20); - check(scope, Broadcast::make(3, 4), 3, 3); - - // Check some operations that may overflow - check(scope, (cast(x) + 250), u8(0), u8(255)); - check(scope, (cast(x) + 10) * 20, u8(0), u8(255)); - check(scope, (cast(x) + 10) * (cast(x) + 5), u8(0), u8(255)); - check(scope, (cast(x) + 10) - (cast(x) + 5), u8(0), u8(255)); - - // Check some operations that we should be able to prove do not overflow - check(scope, (cast(x) + 240), u8(240), u8(250)); - check(scope, (cast(x) + 10) * 10, u8(100), u8(200)); - check(scope, (cast(x) + 10) * (cast(x)), u8(0), u8(200)); - check(scope, (cast(x) + 20) - (cast(x) + 5), u8(5), u8(25)); - - // Check div/mod by unbounded unknowns. div and mod can only ever - // make things smaller in magnitude. - scope.push("x", Interval::everything()); - check(scope, -3 / x, -3, 3); - check(scope, 3 / x, -3, 3); - check(scope, y / x, -cast(abs(y)), cast(abs(y))); - check(scope, -3 % x, 0, Interval::pos_inf()); - check(scope, 3 % x, 0, 3); - // Mod can't make values negative - check(scope, y % x, 0, Interval::pos_inf()); - // Mod can't make positive values larger - check(scope, max(y, 0) % x, 0, max(y, 0)); - scope.pop("x"); - - // Check some bitwise ops. - check(scope, (cast(x) & make_const(UInt(8), 7)), u8(0), u8(7)); - check(scope, (make_const(UInt(8), 3) & make_const(UInt(8), 2)), u8(2), u8(2)); - check(scope, (make_one(UInt(8)) | make_const(UInt(8), 2)), u8(3), u8(3)); - check(scope, (make_const(UInt(8), 3) ^ make_const(UInt(8), 2)), u8(1), u8(1)); - check(scope, (~make_const(UInt(8), 3)), u8(0xfc), u8(0xfc)); - check(scope, cast(x + 5) & cast(x + 3), u8(0), u8(13)); - check(scope, cast(x - 5) & cast(x + 3), i8(0), i8(13)); - check(scope, cast(2 * x - 5) & cast(x - 3), i8(-128), i8(15)); - check(scope, cast(x + 5) | cast(x + 3), u8(5), u8(255)); - check(scope, cast(x + 5) | cast(x + 3), i8(3), i8(127)); - check(scope, ~cast(x), u8(-11), u8(-1)); - check(scope, (cast(x) >> make_one(UInt(8))), u8(0), u8(5)); - check(scope, (make_const(UInt(8), 10) >> make_one(UInt(8))), u8(5), u8(5)); - check(scope, (cast(x + 3) << make_one(UInt(8))), u8(6), u8(26)); - check(scope, (cast(x + 3) << make_const(UInt(8), 7)), u8(0), u8(255)); // Overflows - check(scope, (make_const(UInt(8), 5) << make_one(UInt(8))), u8(10), u8(10)); - check(scope, (x << 12), 0, 10 << 12); - check(scope, x & 4095, 0, 10); // LHS known to be positive - check(scope, x & 123, 0, 10); // Doesn't have to be a precise bitmask - check(scope, (x - 1) & 4095, 0, 4095); // LHS could be -1 - - // Regression tests on shifts (produced by z3). - { - ScopedBinding xb(scope, "x", Interval(-123, Interval::pos_inf())); - ScopedBinding yb(scope, "y", Interval(-6, 0)); - // -123 << 0 = -123 - check(scope, x << y, -123, Interval::pos_inf()); - } - { - ScopedBinding xb(scope, "x", Interval(-123, Interval::pos_inf())); - ScopedBinding yb(scope, "y", Interval(-6, Interval::pos_inf())); - // A negative value can increase in magnitude if the rhs is positive. - check(scope, x << y, Interval::neg_inf(), Interval::pos_inf()); - } - { - ScopedBinding xb(scope, "x", Interval(-123, Interval::pos_inf())); - Var c("c"); - ScopedBinding yb(scope, "y", Interval(-6, c)); - // Can't prove anything about the upper bound of y. - check(scope, x << y, min((-123) << c, -123), Interval::pos_inf()); - } - { - ScopedBinding xb(scope, "x", Interval(-123, Interval::pos_inf())); - ScopedBinding yb(scope, "y", Interval(-6, 4)); - // -123 << 4 = -1968 - check(scope, x << y, -1968, Interval::pos_inf()); - } - { - ScopedBinding xb(scope, "x", Interval(24, Interval::pos_inf())); - ScopedBinding yb(scope, "y", Interval(Interval::neg_inf(), -1)); - // Cannot change sign, only can decrease magnitude. - check(scope, x << y, 0, Interval::pos_inf()); - } - // Overflow testing (for types with defined overflow). - { - Type uint32 = UInt(32); - Expr a = Variable::make(uint32, "a"); - Expr b = Variable::make(uint32, "b"); - ScopedBinding ab(scope, "a", Interval(UIntImm::make(uint32, 0), simplify(uint32.max() / 4 + 2))); - ScopedBinding bb(scope, "b", Interval(UIntImm::make(uint32, 0), uint32.max())); - // Overflow should be detected - check(scope, a + b, Interval::neg_inf(), Interval::pos_inf()); - check(scope, a * b, Interval::neg_inf(), Interval::pos_inf()); - } - { - Type int16 = Int(16); - Expr a = Variable::make(int16, "a"); - Expr b = Variable::make(int16, "b"); - ScopedBinding ab(scope, "a", Interval(int16.min(), int16.max())); - ScopedBinding bb(scope, "b", Interval(IntImm::make(int16, -4), IntImm::make(int16, -1))); - check(scope, a * -1, int16.min(), int16.max()); - // int16.min() / -1 should be caught as overflow. - check(scope, a / -1, int16.min(), int16.max()); - check(scope, a / b, int16.min(), int16.max()); - } - { - Expr zero = UIntImm::make(UInt(1), 0); - Expr one = UIntImm::make(UInt(1), 1); - check(scope, Ramp::make(zero, one, 3), zero, one); - } - - // If we clamp something unbounded as one type, the bounds should - // propagate through casts whenever the cast can be proved to not - // overflow. - check(scope, - cast(clamp(cast(x ^ y), 0.0f, 4095.0f)), - u16(0), u16(4095)); - - check(scope, - cast(clamp(cast(x ^ y), make_zero(UInt(16)), make_const(UInt(16), 128))), - u8(0), u8(128)); - - Expr u8_1 = cast(Load::make(Int(8), "buf", x, Buffer<>(), Parameter(), const_true(), ModulusRemainder())); - Expr u8_2 = cast(Load::make(Int(8), "buf", x + 17, Buffer<>(), Parameter(), const_true(), ModulusRemainder())); - check(scope, cast(u8_1) + cast(u8_2), - u16(0), u16(255 * 2)); - - check(scope, saturating_cast(clamp(x, 5, 10)), make_const(UInt(8), 5), make_const(UInt(8), 10)); - { - scope.push("x", Interval(UInt(32).min(), UInt(32).max())); - check(scope, saturating_cast(max(cast(x), make_const(UInt(32), 5))), make_const(Int(32), 5), Int(32).max()); - scope.pop("x"); - } - { - Expr z = Variable::make(Float(32), "z"); - scope.push("z", Interval(make_const(Float(32), -1), make_one(Float(32)))); - check(scope, saturating_cast(z), make_const(Int(32), -1), make_one(Int(32))); - check(scope, saturating_cast(z), make_const(Float(64), -1), make_one(Float(64))); - check(scope, saturating_cast(z), make_const(Float(16), -1), make_one(Float(16))); - check(scope, saturating_cast(z), make_zero(UInt(8)), make_one(UInt(8))); - scope.pop("z"); - } - { - Expr z = Variable::make(UInt(32), "z"); - scope.push("z", Interval(UInt(32).max(), UInt(32).max())); - check(scope, saturating_cast(z), Int(32).max(), Int(32).max()); - scope.pop("z"); - } - - { - Scope scope; - Expr x = Variable::make(UInt(16), "x"); - Expr y = Variable::make(UInt(16), "y"); - scope.push("x", Interval(u16(0), u16(10))); - scope.push("y", Interval(u16(2), u16(4))); - - Expr e = clamp(x / y, u16(0), u16(128)); - check(scope, e, u16(0), u16(5)); - check_constant_bound(scope, e, u16(0), u16(5)); - } - - { - Param x("x"); - Param y("y"); - x.set_range(i16(-32), i16(-16)); - y.set_range(i16(0), i16(4)); - check_constant_bound((x >> y), i16(-32), i16(-1)); - } - - { - Param x("x"), y("y"); - x.set_range(u16(10), u16(20)); - y.set_range(u16(0), u16(30)); - Scope scope; - scope.push("y", Interval(u16(2), u16(4))); - - check_constant_bound(scope, x + y, u16(12), u16(24)); - } - - { - Scope scope; - Interval i = Interval::everything(); - i.min = 17; - internal_assert(i.has_lower_bound()); - internal_assert(!i.has_upper_bound()); - scope.push("y", i); - Var x("x"), y("y"); - check(scope, select(x == y * 2, y, y - 10), - 7, Interval::pos_inf()); - check(scope, select(x == y * 2, y - 10, y), - select(x < 34, 17, 7), Interval::pos_inf()); - } - - vector input_site_1 = {2 * x}; - vector input_site_2 = {2 * x + 1}; - vector output_site = {x + 1}; - - Buffer in(10); - in.set_name("input"); - - Stmt loop = For::make("x", 3, 12, ForType::Serial, Partition::Auto, DeviceAPI::Host, - Provide::make("output", - {Add::make(Call::make(in, input_site_1), - Call::make(in, input_site_2))}, - output_site, - const_true())); - - map r; - r = boxes_required(loop); - internal_assert(r.find("output") == r.end()); - internal_assert(r.find("input") != r.end()); - internal_assert(equal(simplify(r["input"][0].min), 6)); - internal_assert(equal(simplify(r["input"][0].max), 25)); - r = boxes_provided(loop); - internal_assert(r.find("output") != r.end()); - internal_assert(equal(simplify(r["output"][0].min), 4)); - internal_assert(equal(simplify(r["output"][0].max), 13)); - - Box r2({Interval(Expr(5), Expr(19))}); - merge_boxes(r2, r["output"]); - internal_assert(equal(simplify(r2[0].min), 4)); - internal_assert(equal(simplify(r2[0].max), 19)); - - boxes_touched_test(); - - // Check a deeply-nested bitwise expr to ensure it doesn't take n^2 time - // (this clause took ~30s on a typical laptop before the fix, ~10ms after) - { - Expr a = Variable::make(UInt(16), "t42"); - Expr b = Variable::make(UInt(16), "t43"); - Expr c = Variable::make(UInt(16), "t44"); - Expr d = Variable::make(Int(32), "d"); - Expr x = Variable::make(Int(32), "x"); - Expr y = Variable::make(Int(32), "y"); - Expr e1 = select(c >= Expr((uint16_t)128), c - Expr((uint16_t)128), c); - Expr e2 = Let::make("t44", (((((((((((((((((u16(0) << u16(1)) | u16((u8(d) & u8(1)))) << u16(1)) | u16(((u8(d) >> u8(1)) & u8(1)))) << u16(1)) | (u16(x) & u16(1))) << u16(1)) | (u16(y) & u16(1))) << u16(1)) | (a & u16(1))) << u16(1)) | (b & u16(1))) << u16(1)) | ((a >> u16(1)) & u16(1))) << u16(1)) | ((b >> u16(1)) & u16(1))) >> u16(1)), e1); - Expr e3 = Let::make("t43", u16(y) >> u16(1), e2); - Expr e4 = Let::make("t42", u16(x) >> u16(1), e3); - - check_constant_bound(e4, u16(0), u16(65535)); - } - - // Test case from https://github.com/halide/Halide/pull/7377 - { - Var x; - Expr e = Load::make(Int(32), "buf", max(x, -x), Buffer<>{}, Parameter{}, const_true(), ModulusRemainder{}); - e = Let::make(x.name(), 37, e); - Scope scope; - scope.push("y", {0, 100}); - Interval in = bounds_of_expr_in_scope(e, scope); - internal_assert(in.is_single_point()); - } - - // Test case from https://github.com/halide/Halide/pull/7379 - { - Var x; - Expr e = Load::make(Int(32), "buf", -x / x, Buffer<>{}, Parameter{}, const_true(), ModulusRemainder{}); - e = Let::make(x.name(), 37, e); - Scope scope; - scope.push("y", {0, 100}); - Interval in = bounds_of_expr_in_scope(e, scope); - internal_assert(in.is_single_point()); - } - - std::cout << "Bounds test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/Bounds.h b/src/Bounds.h index a06980492d60..e5b30d503976 100644 --- a/src/Bounds.h +++ b/src/Bounds.h @@ -192,8 +192,6 @@ FuncValueBounds compute_function_value_bounds(const std::vector &or /* Find an upper bound of bounds.max - bounds.min. */ Expr span_of_bounds(const Interval &bounds); -void bounds_test(); - } // namespace Internal } // namespace Halide diff --git a/src/CPlusPlusMangle.cpp b/src/CPlusPlusMangle.cpp index dfcf17b921e9..d56175185a66 100644 --- a/src/CPlusPlusMangle.cpp +++ b/src/CPlusPlusMangle.cpp @@ -625,488 +625,6 @@ std::string cplusplus_function_mangled_name(const std::string &name, const std:: } } -// All code below is for tests. - -namespace { - -constexpr int kTestTargetCount = 8; - -struct MangleResult { - const char *expected; - const char *label; -}; - -MangleResult ItaniumABIMangling_main[] = { - {"_Z13test_functionv", "int32_t test_function(void)"}, - {"_ZN3foo13test_functionEv", "int32_t foo::test_function(void)"}, - {"_ZN3foo3bar13test_functionEv", "int32_t foo::bar::test_function(void)"}, - {"_ZN3foo3bar13test_functionEi", "int32_t foo::test_function(int32_t)"}, - {"_ZN3foo3bar13test_functionEiP15halide_buffer_t", "int32_t foo::test_function(int32_t, struct halide_buffer_t *)"}, - {"_ZN14test_namespace14test_namespace13test_functionENS0_15enclosing_class11test_structE", - "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct)"}, - {"_ZN3foo3bar13test_functionEiP15halide_buffer_tS2_", "foo::bar::test_function(int, halide_buffer_t*, halide_buffer_t*)"}, - {"_ZN14test_namespace14test_namespace13test_functionEPNS_11test_structEPKS1_", "test_namespace::test_namespace::test_function(test_namespace::test_struct*, test_namespace::test_struct const*)"}, - {"_ZN14test_namespace14test_namespace13test_functionENS0_15enclosing_class11test_structES2_", - "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct, test_namespace::test_namespace::enclosing_class::test_struct)"}, - {"_ZSt13test_functionv", "std::test_function()"}, - {"_ZNSt3foo13test_functionEv", "std::foo::test_function()"}, - {"_ZSt13test_functionNSt15enclosing_class11test_structE", "std::test_function(std::enclosing_class::test_struct)"}, - {"_ZN14test_namespace14test_namespace13test_functionEPNS_10test_classE", "test_namespace::test_namespace::test_function(test_namespace::test_class*)"}, - {"_ZN14test_namespace14test_namespace13test_functionEPNS_10test_unionE", "test_namespace::test_namespace::test_function(test_namespace::test_union*)"}, - {"_ZN14test_namespace14test_namespace13test_functionEPNS_9test_enumE", "test_namespace::test_namespace::test_function(test_namespace::test_enum*)"}, -}; - -MangleResult win32_expecteds[] = { - {"\001?test_function@@YAHXZ", "int32_t test_function(void)"}, - {"\001?test_function@foo@@YAHXZ", "int32_t foo::test_function(void)"}, - {"\001?test_function@bar@foo@@YAHXZ", "int32_t foo::bar::test_function(void)"}, - {"\001?test_function@bar@foo@@YAHH@Z", "int32_t foo::test_function(int32_t)"}, - {"\001?test_function@bar@foo@@YAHHPAUhalide_buffer_t@@@Z", "int32_t foo::test_function(int32_t, struct halide_buffer_t *)"}, - {"\001?test_function@test_namespace@1@YAHUtest_struct@enclosing_class@11@@Z", - "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct)"}, - {"\001?test_function@bar@foo@@YAHHPAUhalide_buffer_t@@0@Z", "foo::bar::test_function(int, halide_buffer_t*, halide_buffer_t*)"}, - {"\001?test_function@test_namespace@1@YAHPAUtest_struct@1@PBU21@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_struct*, test_namespace::test_struct const*)"}, - {"\001?test_function@test_namespace@1@YAHUtest_struct@enclosing_class@11@0@Z", - "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct, test_namespace::test_namespace::enclosing_class::test_struct)"}, - {"\001?test_function@std@@YAHXZ", "std::test_function()"}, - {"\001?test_function@foo@std@@YAHXZ", "std::foo::test_function()"}, - {"\001?test_function@std@@YAHUtest_struct@enclosing_class@1@@Z", "std::test_function(std::enclosing_class::test_struct)"}, - {"\001?test_function@test_namespace@1@YAHPAVtest_class@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_class*)"}, - {"\001?test_function@test_namespace@1@YAHPATtest_union@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_union*)"}, - {"\001?test_function@test_namespace@1@YAHPAVtest_enum@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_enum*)"}, -}; - -MangleResult win64_expecteds[] = { - {"\001?test_function@@YAHXZ", "int32_t test_function(void)"}, - {"\001?test_function@foo@@YAHXZ", "int32_t foo::test_function(void)"}, - {"\001?test_function@bar@foo@@YAHXZ", "int32_t foo::bar::test_function(void)"}, - {"\001?test_function@bar@foo@@YAHH@Z", "int32_t foo::test_function(int32_t)"}, - {"\001?test_function@bar@foo@@YAHHPEAUhalide_buffer_t@@@Z", "int32_t foo::test_function(int32_t, struct halide_buffer_t *)"}, - {"\001?test_function@test_namespace@1@YAHUtest_struct@enclosing_class@11@@Z", - "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct)"}, - {"\001?test_function@bar@foo@@YAHHPEAUhalide_buffer_t@@0@Z", "foo::bar::test_function(int, halide_buffer_t*, halide_buffer_t*)"}, - {"\001?test_function@test_namespace@1@YAHPEAUtest_struct@1@PEBU21@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_struct*, test_namespace::test_struct const*)"}, - {"\001?test_function@test_namespace@1@YAHUtest_struct@enclosing_class@11@0@Z", - "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct, test_namespace::test_namespace::enclosing_class::test_struct)"}, - {"\001?test_function@std@@YAHXZ", "std::test_function()"}, - {"\001?test_function@foo@std@@YAHXZ", "std::foo::test_function()"}, - {"\001?test_function@std@@YAHUtest_struct@enclosing_class@1@@Z", "std::test_function(std::enclosing_class::test_struct)"}, - {"\001?test_function@test_namespace@1@YAHPEAVtest_class@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_class*)"}, - {"\001?test_function@test_namespace@1@YAHPEATtest_union@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_union*)"}, - {"\001?test_function@test_namespace@1@YAHPEAVtest_enum@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_enum*)"}, -}; - -MangleResult all_types_by_target[kTestTargetCount] = { - {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, - {"_Z13test_functionbahstijlmfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, float, double)"}, - {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, - {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, float, double)"}, - {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, - {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, float, double)"}, - {"\001?test_function@@YAH_NCEFGHI_J_KMN@Z", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, - {"\001?test_function@@YAH_NCEFGHI_J_KMN@Z", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, -}; - -const char *many_type_subs_itanium = "_Z13test_functionPN14test_namespace2s0EPNS_2s1EPNS_2s2EPNS_2s3EPNS_2s4EPNS_2s5EPNS_2s6EPNS_2s7EPNS_2s8EPNS_2s9EPNS_3s10EPNS_3s11EPNS_3s12EPNS_3s13EPNS_3s14EPNS_3s15EPNS_3s16EPNS_3s17EPNS_3s18EPNS_3s19EPNS_3s20EPNS_3s21EPNS_3s22EPNS_3s23EPNS_3s24EPNS_3s25EPNS_3s26EPNS_3s27EPNS_3s28EPNS_3s29EPNS_3s30EPNS_3s31EPNS_3s32EPNS_3s33EPNS_3s34EPNS_3s35EPNS_3s36EPNS_3s37EPNS_3s38EPNS_3s39EPNS_3s40EPNS_3s41EPNS_3s42EPNS_3s43EPNS_3s44EPNS_3s45EPNS_3s46EPNS_3s47EPNS_3s48EPNS_3s49EPNS_3s50EPNS_3s51EPNS_3s52EPNS_3s53EPNS_3s54EPNS_3s55EPNS_3s56EPNS_3s57EPNS_3s58EPNS_3s59EPNS_3s60EPNS_3s61EPNS_3s62EPNS_3s63EPNS_3s64EPNS_3s65EPNS_3s66EPNS_3s67EPNS_3s68EPNS_3s69EPNS_3s70EPNS_3s71EPNS_3s72EPNS_3s73EPNS_3s74EPNS_3s75EPNS_3s76EPNS_3s77EPNS_3s78EPNS_3s79EPNS_3s80EPNS_3s81EPNS_3s82EPNS_3s83EPNS_3s84EPNS_3s85EPNS_3s86EPNS_3s87EPNS_3s88EPNS_3s89EPNS_3s90EPNS_3s91EPNS_3s92EPNS_3s93EPNS_3s94EPNS_3s95EPNS_3s96EPNS_3s97EPNS_3s98EPNS_3s99ES1_S3_S5_S7_S9_SB_SD_SF_SH_SJ_SL_SN_SP_SR_ST_SV_SX_SZ_S11_S13_S15_S17_S19_S1B_S1D_S1F_S1H_S1J_S1L_S1N_S1P_S1R_S1T_S1V_S1X_S1Z_S21_S23_S25_S27_S29_S2B_S2D_S2F_S2H_S2J_S2L_S2N_S2P_S2R_S2T_S2V_S2X_S2Z_S31_S33_S35_S37_S39_S3B_S3D_S3F_S3H_S3J_S3L_S3N_S3P_S3R_S3T_S3V_S3X_S3Z_S41_S43_S45_S47_S49_S4B_S4D_S4F_S4H_S4J_S4L_S4N_S4P_S4R_S4T_S4V_S4X_S4Z_S51_S53_S55_S57_S59_S5B_S5D_S5F_S5H_S5J_"; - -const char *many_type_subs_win32 = "\001?test_function@@YAHPAUs0@test_namespace@@PAUs1@2@PAUs2@2@PAUs3@2@PAUs4@2@PAUs5@2@PAUs6@2@PAUs7@2@PAUs8@2@PAUs9@2@PAUs10@2@PAUs11@2@PAUs12@2@PAUs13@2@PAUs14@2@PAUs15@2@PAUs16@2@PAUs17@2@PAUs18@2@PAUs19@2@PAUs20@2@PAUs21@2@PAUs22@2@PAUs23@2@PAUs24@2@PAUs25@2@PAUs26@2@PAUs27@2@PAUs28@2@PAUs29@2@PAUs30@2@PAUs31@2@PAUs32@2@PAUs33@2@PAUs34@2@PAUs35@2@PAUs36@2@PAUs37@2@PAUs38@2@PAUs39@2@PAUs40@2@PAUs41@2@PAUs42@2@PAUs43@2@PAUs44@2@PAUs45@2@PAUs46@2@PAUs47@2@PAUs48@2@PAUs49@2@PAUs50@2@PAUs51@2@PAUs52@2@PAUs53@2@PAUs54@2@PAUs55@2@PAUs56@2@PAUs57@2@PAUs58@2@PAUs59@2@PAUs60@2@PAUs61@2@PAUs62@2@PAUs63@2@PAUs64@2@PAUs65@2@PAUs66@2@PAUs67@2@PAUs68@2@PAUs69@2@PAUs70@2@PAUs71@2@PAUs72@2@PAUs73@2@PAUs74@2@PAUs75@2@PAUs76@2@PAUs77@2@PAUs78@2@PAUs79@2@PAUs80@2@PAUs81@2@PAUs82@2@PAUs83@2@PAUs84@2@PAUs85@2@PAUs86@2@PAUs87@2@PAUs88@2@PAUs89@2@PAUs90@2@PAUs91@2@PAUs92@2@PAUs93@2@PAUs94@2@PAUs95@2@PAUs96@2@PAUs97@2@PAUs98@2@PAUs99@2@0123456789PAUs10@2@PAUs11@2@PAUs12@2@PAUs13@2@PAUs14@2@PAUs15@2@PAUs16@2@PAUs17@2@PAUs18@2@PAUs19@2@PAUs20@2@PAUs21@2@PAUs22@2@PAUs23@2@PAUs24@2@PAUs25@2@PAUs26@2@PAUs27@2@PAUs28@2@PAUs29@2@PAUs30@2@PAUs31@2@PAUs32@2@PAUs33@2@PAUs34@2@PAUs35@2@PAUs36@2@PAUs37@2@PAUs38@2@PAUs39@2@PAUs40@2@PAUs41@2@PAUs42@2@PAUs43@2@PAUs44@2@PAUs45@2@PAUs46@2@PAUs47@2@PAUs48@2@PAUs49@2@PAUs50@2@PAUs51@2@PAUs52@2@PAUs53@2@PAUs54@2@PAUs55@2@PAUs56@2@PAUs57@2@PAUs58@2@PAUs59@2@PAUs60@2@PAUs61@2@PAUs62@2@PAUs63@2@PAUs64@2@PAUs65@2@PAUs66@2@PAUs67@2@PAUs68@2@PAUs69@2@PAUs70@2@PAUs71@2@PAUs72@2@PAUs73@2@PAUs74@2@PAUs75@2@PAUs76@2@PAUs77@2@PAUs78@2@PAUs79@2@PAUs80@2@PAUs81@2@PAUs82@2@PAUs83@2@PAUs84@2@PAUs85@2@PAUs86@2@PAUs87@2@PAUs88@2@PAUs89@2@PAUs90@2@PAUs91@2@PAUs92@2@PAUs93@2@PAUs94@2@PAUs95@2@PAUs96@2@PAUs97@2@PAUs98@2@PAUs99@2@@Z"; - -const char *many_type_subs_win64 = "\001?test_function@@YAHPEAUs0@test_namespace@@PEAUs1@2@PEAUs2@2@PEAUs3@2@PEAUs4@2@PEAUs5@2@PEAUs6@2@PEAUs7@2@PEAUs8@2@PEAUs9@2@PEAUs10@2@PEAUs11@2@PEAUs12@2@PEAUs13@2@PEAUs14@2@PEAUs15@2@PEAUs16@2@PEAUs17@2@PEAUs18@2@PEAUs19@2@PEAUs20@2@PEAUs21@2@PEAUs22@2@PEAUs23@2@PEAUs24@2@PEAUs25@2@PEAUs26@2@PEAUs27@2@PEAUs28@2@PEAUs29@2@PEAUs30@2@PEAUs31@2@PEAUs32@2@PEAUs33@2@PEAUs34@2@PEAUs35@2@PEAUs36@2@PEAUs37@2@PEAUs38@2@PEAUs39@2@PEAUs40@2@PEAUs41@2@PEAUs42@2@PEAUs43@2@PEAUs44@2@PEAUs45@2@PEAUs46@2@PEAUs47@2@PEAUs48@2@PEAUs49@2@PEAUs50@2@PEAUs51@2@PEAUs52@2@PEAUs53@2@PEAUs54@2@PEAUs55@2@PEAUs56@2@PEAUs57@2@PEAUs58@2@PEAUs59@2@PEAUs60@2@PEAUs61@2@PEAUs62@2@PEAUs63@2@PEAUs64@2@PEAUs65@2@PEAUs66@2@PEAUs67@2@PEAUs68@2@PEAUs69@2@PEAUs70@2@PEAUs71@2@PEAUs72@2@PEAUs73@2@PEAUs74@2@PEAUs75@2@PEAUs76@2@PEAUs77@2@PEAUs78@2@PEAUs79@2@PEAUs80@2@PEAUs81@2@PEAUs82@2@PEAUs83@2@PEAUs84@2@PEAUs85@2@PEAUs86@2@PEAUs87@2@PEAUs88@2@PEAUs89@2@PEAUs90@2@PEAUs91@2@PEAUs92@2@PEAUs93@2@PEAUs94@2@PEAUs95@2@PEAUs96@2@PEAUs97@2@PEAUs98@2@PEAUs99@2@0123456789PEAUs10@2@PEAUs11@2@PEAUs12@2@PEAUs13@2@PEAUs14@2@PEAUs15@2@PEAUs16@2@PEAUs17@2@PEAUs18@2@PEAUs19@2@PEAUs20@2@PEAUs21@2@PEAUs22@2@PEAUs23@2@PEAUs24@2@PEAUs25@2@PEAUs26@2@PEAUs27@2@PEAUs28@2@PEAUs29@2@PEAUs30@2@PEAUs31@2@PEAUs32@2@PEAUs33@2@PEAUs34@2@PEAUs35@2@PEAUs36@2@PEAUs37@2@PEAUs38@2@PEAUs39@2@PEAUs40@2@PEAUs41@2@PEAUs42@2@PEAUs43@2@PEAUs44@2@PEAUs45@2@PEAUs46@2@PEAUs47@2@PEAUs48@2@PEAUs49@2@PEAUs50@2@PEAUs51@2@PEAUs52@2@PEAUs53@2@PEAUs54@2@PEAUs55@2@PEAUs56@2@PEAUs57@2@PEAUs58@2@PEAUs59@2@PEAUs60@2@PEAUs61@2@PEAUs62@2@PEAUs63@2@PEAUs64@2@PEAUs65@2@PEAUs66@2@PEAUs67@2@PEAUs68@2@PEAUs69@2@PEAUs70@2@PEAUs71@2@PEAUs72@2@PEAUs73@2@PEAUs74@2@PEAUs75@2@PEAUs76@2@PEAUs77@2@PEAUs78@2@PEAUs79@2@PEAUs80@2@PEAUs81@2@PEAUs82@2@PEAUs83@2@PEAUs84@2@PEAUs85@2@PEAUs86@2@PEAUs87@2@PEAUs88@2@PEAUs89@2@PEAUs90@2@PEAUs91@2@PEAUs92@2@PEAUs93@2@PEAUs94@2@PEAUs95@2@PEAUs96@2@PEAUs97@2@PEAUs98@2@PEAUs99@2@@Z"; - -MangleResult many_type_subs[kTestTargetCount] = { - {many_type_subs_itanium, "The expanded prototype is very long."}, - {many_type_subs_itanium, "No really, too large to put here."}, - {many_type_subs_itanium, "wc -l says 4394 characters."}, - {many_type_subs_itanium, "Feel free to run c++filt if you want to..."}, - {many_type_subs_itanium, "longity long long"}, - {many_type_subs_itanium, "It's been a long, long, long..."}, - {many_type_subs_win32, "Not gonna do it."}, - {many_type_subs_win64, "Wouldn't be prudent."}}; - -const char *many_name_subs_itanium = "_Z13test_functionPN15test_namespace01sEPN15test_namespace11sEPN15test_namespace21sEPN15test_namespace31sEPN15test_namespace41sEPN15test_namespace51sEPN15test_namespace61sEPN15test_namespace71sEPN15test_namespace81sEPN15test_namespace91sEPN16test_namespace101sEPN16test_namespace111sEPN16test_namespace121sEPN16test_namespace131sEPN16test_namespace141sEPN16test_namespace151sEPN16test_namespace161sEPN16test_namespace171sEPN16test_namespace181sEPN16test_namespace191sEPN16test_namespace201sEPN16test_namespace211sEPN16test_namespace221sEPN16test_namespace231sEPN16test_namespace241sES1_S4_S7_SA_SD_SG_SJ_SM_SP_SS_SV_SY_S11_S14_S17_S1A_S1D_S1G_S1J_S1M_S1P_S1S_S1V_S1Y_S21_"; - -const char *many_name_subs_win32 = "\001?test_function@@YAHPAUs@test_namespace0@@PAU1test_namespace1@@PAU1test_namespace2@@PAU1test_namespace3@@PAU1test_namespace4@@PAU1test_namespace5@@PAU1test_namespace6@@PAU1test_namespace7@@PAU1test_namespace8@@PAU1test_namespace9@@PAU1test_namespace10@@PAU1test_namespace11@@PAU1test_namespace12@@PAU1test_namespace13@@PAU1test_namespace14@@PAU1test_namespace15@@PAU1test_namespace16@@PAU1test_namespace17@@PAU1test_namespace18@@PAU1test_namespace19@@PAU1test_namespace20@@PAU1test_namespace21@@PAU1test_namespace22@@PAU1test_namespace23@@PAU1test_namespace24@@0123456789PAU1test_namespace10@@PAU1test_namespace11@@PAU1test_namespace12@@PAU1test_namespace13@@PAU1test_namespace14@@PAU1test_namespace15@@PAU1test_namespace16@@PAU1test_namespace17@@PAU1test_namespace18@@PAU1test_namespace19@@PAU1test_namespace20@@PAU1test_namespace21@@PAU1test_namespace22@@PAU1test_namespace23@@PAU1test_namespace24@@@Z"; - -const char *many_name_subs_win64 = "\001?test_function@@YAHPEAUs@test_namespace0@@PEAU1test_namespace1@@PEAU1test_namespace2@@PEAU1test_namespace3@@PEAU1test_namespace4@@PEAU1test_namespace5@@PEAU1test_namespace6@@PEAU1test_namespace7@@PEAU1test_namespace8@@PEAU1test_namespace9@@PEAU1test_namespace10@@PEAU1test_namespace11@@PEAU1test_namespace12@@PEAU1test_namespace13@@PEAU1test_namespace14@@PEAU1test_namespace15@@PEAU1test_namespace16@@PEAU1test_namespace17@@PEAU1test_namespace18@@PEAU1test_namespace19@@PEAU1test_namespace20@@PEAU1test_namespace21@@PEAU1test_namespace22@@PEAU1test_namespace23@@PEAU1test_namespace24@@0123456789PEAU1test_namespace10@@PEAU1test_namespace11@@PEAU1test_namespace12@@PEAU1test_namespace13@@PEAU1test_namespace14@@PEAU1test_namespace15@@PEAU1test_namespace16@@PEAU1test_namespace17@@PEAU1test_namespace18@@PEAU1test_namespace19@@PEAU1test_namespace20@@PEAU1test_namespace21@@PEAU1test_namespace22@@PEAU1test_namespace23@@PEAU1test_namespace24@@@Z"; - -const char *many_name_subs_proto = "test_function(test_namespace0::s*, test_namespace1::s*, test_namespace2::s*, test_namespace3::s*, test_namespace4::s*, test_namespace5::s*, test_namespace6::s*, test_namespace7::s*, test_namespace8::s*, test_namespace9::s*, test_namespace10::s*, test_namespace11::s*, test_namespace12::s*, test_namespace13::s*, test_namespace14::s*, test_namespace15::s*, test_namespace16::s*, test_namespace17::s*, test_namespace18::s*, test_namespace19::s*, test_namespace20::s*, test_namespace21::s*, test_namespace22::s*, test_namespace23::s*, test_namespace24::s*, test_namespace0::s*, test_namespace1::s*, test_namespace2::s*, test_namespace3::s*, test_namespace4::s*, test_namespace5::s*, test_namespace6::s*, test_namespace7::s*, test_namespace8::s*, test_namespace9::s*, test_namespace10::s*, test_namespace11::s*, test_namespace12::s*, test_namespace13::s*, test_namespace14::s*, test_namespace15::s*, test_namespace16::s*, test_namespace17::s*, test_namespace18::s*, test_namespace19::s*, test_namespace20::s*, test_namespace21::s*, test_namespace22::s*, test_namespace23::s*, test_namespace24::s*)"; - -MangleResult many_name_subs[kTestTargetCount] = { - {many_name_subs_itanium, many_name_subs_proto}, - {many_name_subs_itanium, many_name_subs_proto}, - {many_name_subs_itanium, many_name_subs_proto}, - {many_name_subs_itanium, many_name_subs_proto}, - {many_name_subs_itanium, many_name_subs_proto}, - {many_name_subs_itanium, many_name_subs_proto}, - {many_name_subs_win32, many_name_subs_proto}, - {many_name_subs_win64, many_name_subs_proto}}; - -MangleResult stacked_indirections[kTestTargetCount] = { - {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, - {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, - {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, - {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, - {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, - {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, - {"\001?test_function@@YAHPBHPBQBHPBQBQBHPBQBQBQBHPBQBQBQBQBHPBQBQBQBQBQBHPBQBQBQBQBQBQBHPBQBQBQBQBQBQBQBH@Z", ""}, - {"\001?test_function@@YAHPEBHPEBQEBHPEBQEBQEBHPEBQEBQEBQEBHPEBQEBQEBQEBQEBHPEBQEBQEBQEBQEBQEBHPEBQEBQEBQEBQEBQEBQEBHPEBQEBQEBQEBQEBQEBQEBQEBH@Z", ""}}; - -MangleResult all_mods_itanium[] = { - {"_Z13test_function1sRS_OS_", "test_function(s, s&, s&&)"}, - {"_Z13test_function1sRKS_OS0_", "test_function(s, s const&, s const&&)"}, - {"_Z13test_function1sRVS_OS0_", "test_function(s, s volatile&, s volatile&&)"}, - {"_Z13test_function1sRVKS_OS0_", "test_function(s, s const volatile&, s const volatile&&)"}, - {"_Z13test_function1sRrS_OS0_", "test_function(s, s restrict&, s restrict&&)"}, - {"_Z13test_function1sRrKS_OS0_", "test_function(s, s const restrict&, s const restrict&&)"}, - {"_Z13test_function1sRrVS_OS0_", "test_function(s, s volatile restrict&, s volatile restrict&&)"}, - {"_Z13test_function1sRrVKS_OS0_", "test_function(s, s const volatile restrict&, s const volatile restrict&&)"}, - {"_Z13test_functionP1sRS0_OS0_", "test_function(s*, s*&, s*&&)"}, - {"_Z13test_functionPK1sRS1_OS1_", "test_function(s const*, s const*&, s const*&&)"}, - {"_Z13test_functionPV1sRS1_OS1_", "test_function(s volatile*, s volatile*&, s volatile*&&)"}, - {"_Z13test_functionPVK1sRS1_OS1_", "test_function(s const volatile*, s const volatile*&, s const volatile*&&)"}, - {"_Z13test_functionPr1sRS1_OS1_", "test_function(s restrict*, s restrict*&, s restrict*&&)"}, - {"_Z13test_functionPrK1sRS1_OS1_", "test_function(s const restrict*, s const restrict*&, s const restrict*&&)"}, - {"_Z13test_functionPrV1sRS1_OS1_", "test_function(s volatile restrict*, s volatile restrict*&, s volatile restrict*&&)"}, - {"_Z13test_functionPrVK1sRS1_OS1_", "test_function(s const volatile restrict*, s const volatile restrict*&, s const volatile restrict*&&)"}}; - -MangleResult all_mods_win32[] = { - {"\001?test_function@@YAHUs@@AAU1@$$QAU1@@Z", "test_function(s, s&, s&&)"}, - {"\001?test_function@@YAHUs@@ABU1@$$QBU1@@Z", "test_function(s, s const&, s const&&)"}, - {"\001?test_function@@YAHUs@@ACU1@$$QCU1@@Z", "test_function(s, s volatile&, s volatile&&)"}, - {"\001?test_function@@YAHUs@@ADU1@$$QDU1@@Z", "test_function(s, s const volatile&, s const volatile&&)"}, - {"\001?test_function@@YAHUs@@AAU1@$$QAU1@@Z", "test_function(s, s restrict&, s restrict&&)"}, - {"\001?test_function@@YAHUs@@ABU1@$$QBU1@@Z", "test_function(s, s const restrict&, s const restrict&&)"}, - {"\001?test_function@@YAHUs@@ACU1@$$QCU1@@Z", "test_function(s, s volatile restrict&, s volatile restrict&&)"}, - {"\001?test_function@@YAHUs@@ADU1@$$QDU1@@Z", "test_function(s, s const volatile restrict&, s const volatile restrict&&)"}, - {"\001?test_function@@YAHPAUs@@AAPAU1@$$QAPAU1@@Z", "test_function(s*, s*&, s*&&)"}, - {"\001?test_function@@YAHPBUs@@AAPBU1@$$QAPBU1@@Z", "test_function(s const*, s const*&, s const*&&)"}, - {"\001?test_function@@YAHPCUs@@AAPCU1@$$QAPCU1@@Z", "test_function(s volatile*, s volatile*&, s volatile*&&)"}, - {"\001?test_function@@YAHPDUs@@AAPDU1@$$QAPDU1@@Z", "test_function(s const volatile*, s const volatile*&, s const volatile*&&)"}, - {"\001?test_function@@YAHPAUs@@AAPAU1@$$QAPAU1@@Z", "test_function(s restrict*, s restrict*&, s restrict*&&)"}, - {"\001?test_function@@YAHPBUs@@AAPBU1@$$QAPBU1@@Z", "test_function(s const restrict*, s const restrict*&, s const restrict*&&)"}, - {"\001?test_function@@YAHPCUs@@AAPCU1@$$QAPCU1@@Z", "test_function(s volatile restrict*, s volatile restrict*&, s volatile restrict*&&)"}, - {"\001?test_function@@YAHPDUs@@AAPDU1@$$QAPDU1@@Z", "test_function(s const volatile restrict*, s const volatile restrict*&, s const volatile restrict*&&)"}}; - -MangleResult all_mods_win64[] = { - {"\001?test_function@@YAHUs@@AEAU1@$$QEAU1@@Z", "test_function(s, s&, s&&)"}, - {"\001?test_function@@YAHUs@@AEBU1@$$QEBU1@@Z", "test_function(s, s const&, s const&&)"}, - {"\001?test_function@@YAHUs@@AECU1@$$QECU1@@Z", "test_function(s, s volatile&, s volatile&&)"}, - {"\001?test_function@@YAHUs@@AEDU1@$$QEDU1@@Z", "test_function(s, s const volatile&, s const volatile&&)"}, - {"\001?test_function@@YAHUs@@AEAU1@$$QEAU1@@Z", "test_function(s, s restrict&, s restrict&&)"}, - {"\001?test_function@@YAHUs@@AEBU1@$$QEBU1@@Z", "test_function(s, s const restrict&, s const restrict&&)"}, - {"\001?test_function@@YAHUs@@AECU1@$$QECU1@@Z", "test_function(s, s volatile restrict&, s volatile restrict&&)"}, - {"\001?test_function@@YAHUs@@AEDU1@$$QEDU1@@Z", "test_function(s, s const volatile restrict&, s const volatile restrict&&)"}, - {"\001?test_function@@YAHPEAUs@@AEAPEAU1@$$QEAPEAU1@@Z", "test_function(s*, s*&, s*&&)"}, - {"\001?test_function@@YAHPEBUs@@AEAPEBU1@$$QEAPEBU1@@Z", "test_function(s const*, s const*&, s const*&&)"}, - {"\001?test_function@@YAHPECUs@@AEAPECU1@$$QEAPECU1@@Z", "test_function(s volatile*, s volatile*&, s volatile*&&)"}, - {"\001?test_function@@YAHPEDUs@@AEAPEDU1@$$QEAPEDU1@@Z", "test_function(s const volatile*, s const volatile*&, s const volatile*&&)"}, - {"\001?test_function@@YAHPEAUs@@AEAPEAU1@$$QEAPEAU1@@Z", "test_function(s restrict*, s restrict*&, s restrict*&&)"}, - {"\001?test_function@@YAHPEBUs@@AEAPEBU1@$$QEAPEBU1@@Z", "test_function(s const restrict*, s const restrict*&, s const restrict*&&)"}, - {"\001?test_function@@YAHPECUs@@AEAPECU1@$$QEAPECU1@@Z", "test_function(s volatile restrict*, s volatile restrict*&, s volatile restrict*&&)"}, - {"\001?test_function@@YAHPEDUs@@AEAPEDU1@$$QEAPEDU1@@Z", "test_function(s const volatile restrict*, s const volatile restrict*&, s const volatile restrict*&&)"}, -}; - -MangleResult two_void_stars_itanium[] = { - {"_Z13test_functionPvS_", "test_function(void *, void *)"}, -}; - -MangleResult two_void_stars_win64[] = { - {"\001?test_function@@YAHPEAX0@Z", "test_function(void *, void *)"}, -}; - -MangleResult two_void_stars_win32[] = { - {"\001?test_function@@YAHPAX0@Z", "test_function(void *, void *)"}, -}; - -void check_result(const MangleResult *expecteds, size_t &expected_index, - const Target &target, const std::string &mangled_name) { - internal_assert(mangled_name == expecteds[expected_index].expected) << "Mangling for " << expecteds[expected_index].label << " expected\n " << expecteds[expected_index].expected << " got\n " << mangled_name << "\nfor target " << target.to_string(); - expected_index++; -} - -void main_tests(const MangleResult *expecteds, const Target &target) { - size_t expecteds_index = 0; - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {}, Int(32), {}, target)); - - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"foo"}, Int(32), {}, target)); - - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"foo", "bar"}, Int(32), {}, target)); - - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"foo", "bar"}, Int(32), - {ExternFuncArgument(42)}, target)); - - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"foo", "bar"}, Int(32), - {ExternFuncArgument(42), ExternFuncArgument(Buffer<>())}, target)); - - halide_handle_cplusplus_type enclosed_type_info(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "test_struct"), - {"test_namespace", "test_namespace"}, - {halide_cplusplus_type_name(halide_cplusplus_type_name::Class, - "enclosing_class")})); - Type test_type(Handle(&enclosed_type_info)); - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), - {ExternFuncArgument(make_zero(test_type))}, target)); - - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"foo", "bar"}, Int(32), - {ExternFuncArgument(42), ExternFuncArgument(Buffer<>()), - ExternFuncArgument(Buffer<>())}, - target)); - - halide_handle_cplusplus_type qual1(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "test_struct"), - { - "test_namespace", - }, - {}, {halide_handle_cplusplus_type::Pointer})); - Type qual1_type(Handle(&qual1)); - halide_handle_cplusplus_type qual2(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "test_struct"), - { - "test_namespace", - }, - {}, {halide_handle_cplusplus_type::Pointer | halide_handle_cplusplus_type::Const})); - Type qual2_type(Handle(&qual2)); - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), - {ExternFuncArgument(make_zero(qual1_type)), - ExternFuncArgument(make_zero(qual2_type))}, - target)); - - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), - {ExternFuncArgument(make_zero(test_type)), - ExternFuncArgument(make_zero(test_type))}, - target)); - - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"std"}, Int(32), {}, target)); - - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"std", "foo"}, Int(32), {}, target)); - - halide_handle_cplusplus_type std_enclosed_type_info(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "test_struct"), {"std"}, - {halide_cplusplus_type_name(halide_cplusplus_type_name::Class, "enclosing_class")})); - Type std_test_type(Handle(&std_enclosed_type_info)); - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"std"}, Int(32), - {ExternFuncArgument(make_zero(std_test_type))}, target)); - - halide_handle_cplusplus_type class_type_info(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Class, "test_class"), - { - "test_namespace", - }, - {}, {halide_handle_cplusplus_type::Pointer})); - Type class_type(Handle(&class_type_info)); - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), - { - ExternFuncArgument(make_zero(class_type)), - }, - target)); - - halide_handle_cplusplus_type union_type_info(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Union, "test_union"), - { - "test_namespace", - }, - {}, {halide_handle_cplusplus_type::Pointer})); - Type union_type(Handle(&union_type_info)); - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), - { - ExternFuncArgument(make_zero(union_type)), - }, - target)); - - halide_handle_cplusplus_type enum_type_info(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Class, "test_enum"), - { - "test_namespace", - }, - {}, {halide_handle_cplusplus_type::Pointer})); - Type enum_type(Handle(&enum_type_info)); - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), - { - ExternFuncArgument(make_zero(enum_type)), - }, - target)); -} - -} // namespace - -void cplusplus_mangle_test() { - Target targets[kTestTargetCount]{ - Target(Target::Linux, Target::X86, 32), - Target(Target::Linux, Target::X86, 64), - Target(Target::OSX, Target::X86, 32), - Target(Target::OSX, Target::X86, 64), - Target(Target::IOS, Target::ARM, 32), - Target(Target::IOS, Target::ARM, 64), - Target(Target::Windows, Target::X86, 32), - Target(Target::Windows, Target::X86, 64)}; - MangleResult *expecteds[kTestTargetCount]{ - ItaniumABIMangling_main, ItaniumABIMangling_main, - ItaniumABIMangling_main, ItaniumABIMangling_main, - ItaniumABIMangling_main, ItaniumABIMangling_main, - win32_expecteds, win64_expecteds}; - - size_t i = 0; - for (const auto &target : targets) { - main_tests(expecteds[i++], target); - } - - { - // Test all primitive types. - std::vector args; - args.emplace_back(make_zero(Bool())); - args.emplace_back(make_zero(Int(8))); - args.emplace_back(make_zero(UInt(8))); - args.emplace_back(make_zero(Int(16))); - args.emplace_back(make_zero(UInt(16))); - args.emplace_back(make_zero(Int(32))); - args.emplace_back(make_zero(UInt(32))); - args.emplace_back(make_zero(Int(64))); - args.emplace_back(make_zero(UInt(64))); - args.emplace_back(make_zero(Float(32))); - args.emplace_back(make_zero(Float(64))); - - size_t expecteds_index = 0; - for (const auto &target : targets) { - check_result(all_types_by_target, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); - } - } - - { - // Test a whole ton of substitutions on type. - std::vector type_info; - for (int i = 0; i < 100; i++) { - std::stringstream oss; - oss << i; - halide_handle_cplusplus_type t(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s" + oss.str()), - { - "test_namespace", - }, - {}, {halide_handle_cplusplus_type::Pointer})); - type_info.push_back(t); - } - std::vector args; - args.reserve(200); - for (int i = 0; i < 200; i++) { - args.emplace_back(make_zero(Handle(&type_info[i % 100]))); - } - - size_t expecteds_index = 0; - for (const auto &target : targets) { - check_result(many_type_subs, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); - } - } - - { - // Test a whole ton of substitutions on names. - std::vector type_info; - for (int i = 0; i < 25; i++) { - std::stringstream oss; - oss << i; - halide_handle_cplusplus_type t(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s"), - { - "test_namespace" + oss.str(), - }, - {}, {halide_handle_cplusplus_type::Pointer})); - type_info.push_back(t); - } - std::vector args; - args.reserve(50); - for (int i = 0; i < 50; i++) { - args.emplace_back(make_zero(Handle(&type_info[i % 25]))); - } - - size_t expecteds_index = 0; - for (const auto &target : targets) { - check_result(many_name_subs, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); - } - } - - { - // Stack up a bunch of pointers and qualifiers. - // int test_function(int * const, int *const*const, int *const*const*const*, ...); - std::vector type_info; - for (size_t i = 1; i <= 8; i++) { - std::vector mods; - mods.reserve(i); - for (size_t j = 0; j < i; j++) { - mods.push_back(halide_handle_cplusplus_type::Pointer | halide_handle_cplusplus_type::Const); - } - halide_handle_cplusplus_type t(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Simple, "int32_t"), - {}, {}, mods)); - type_info.push_back(t); - } - std::vector args; - args.reserve(type_info.size()); - for (const auto &ti : type_info) { - args.emplace_back(make_zero(Handle(&ti))); - } - size_t expecteds_index = 0; - for (const auto &target : targets) { - check_result(stacked_indirections, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); - } - } - - { - // Test all qualifiers and all ref arguments - for (const auto &target : targets) { - size_t expecteds_index = 0; - for (uint8_t mods = 0; mods < 16; mods++) { - halide_handle_cplusplus_type t1(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s"), {}, {}, {mods})); - halide_handle_cplusplus_type t2(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s"), {}, {}, {mods}, halide_handle_cplusplus_type::LValueReference)); - halide_handle_cplusplus_type t3(halide_handle_cplusplus_type( - halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s"), {}, {}, {mods}, halide_handle_cplusplus_type::RValueReference)); - std::vector args; - args.emplace_back(make_zero(Handle(&t1))); - args.emplace_back(make_zero(Handle(&t2))); - args.emplace_back(make_zero(Handle(&t3))); - - MangleResult *expecteds = (target.os == Target::Windows) ? (target.bits == 64 ? all_mods_win64 : all_mods_win32) : all_mods_itanium; - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); - } - } - } - - { - // Test two void * arguments to ensure substititon handles void * correctly. - // (This is a special case as "void *" is represented using nullptr for the type info.) - for (const auto &target : targets) { - size_t expecteds_index = 0; - std::vector args; - args.emplace_back(make_zero(Handle(nullptr))); - args.emplace_back(make_zero(Handle(nullptr))); - - MangleResult *expecteds = (target.os == Target::Windows) ? (target.bits == 64 ? two_void_stars_win64 : two_void_stars_win32) : two_void_stars_itanium; - check_result(expecteds, expecteds_index, target, - cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); - } - } -} - } // namespace Internal } // namespace Halide diff --git a/src/CPlusPlusMangle.h b/src/CPlusPlusMangle.h index 9bb36aac0173..ed64c9ab841f 100644 --- a/src/CPlusPlusMangle.h +++ b/src/CPlusPlusMangle.h @@ -27,8 +27,6 @@ std::string cplusplus_function_mangled_name(const std::string &name, const std::vector &args, const Target &target); -void cplusplus_mangle_test(); - } // namespace Internal } // namespace Halide diff --git a/src/CSE.cpp b/src/CSE.cpp index 11dcd8178804..e46f94e5fa0e 100644 --- a/src/CSE.cpp +++ b/src/CSE.cpp @@ -386,181 +386,5 @@ Stmt common_subexpression_elimination(const Stmt &s, bool lift_all) { return CSEEveryExprInStmt(lift_all)(s); } -// Testing code. - -namespace { - -// Normalize all names in an expr so that expr compares can be done -// without worrying about mere name differences. -class NormalizeVarNames : public IRMutator { - int counter = 0; - - map new_names; - - using IRMutator::visit; - - Expr visit(const Variable *var) override { - map::iterator iter = new_names.find(var->name); - if (iter == new_names.end()) { - return var; - } else { - return Variable::make(var->type, iter->second); - } - } - - Expr visit(const Let *let) override { - string new_name = "t" + std::to_string(counter++); - new_names[let->name] = new_name; - Expr value = mutate(let->value); - Expr body = mutate(let->body); - return Let::make(new_name, value, body); - } - -public: - NormalizeVarNames() = default; -}; - -void check(const Expr &in, const Expr &correct) { - Expr result = common_subexpression_elimination(in); - result = NormalizeVarNames()(result); - internal_assert(equal(result, correct)) - << "Incorrect CSE:\n" - << in - << "\nbecame:\n" - << result - << "\ninstead of:\n" - << correct << "\n"; -} - -// Construct a nested block of lets. Variables of the form "tn" refer -// to expr n in the vector. -Expr ssa_block(vector exprs) { - Expr e = exprs.back(); - for (size_t i = exprs.size() - 1; i > 0; i--) { - string name = "t" + std::to_string(i - 1); - e = Let::make(name, exprs[i - 1], e); - } - return e; -} - -} // namespace - -void cse_test() { - Expr x = Variable::make(Int(32), "x"); - Expr y = Variable::make(Int(32), "y"); - - Expr t[32], tf[32]; - for (int i = 0; i < 32; i++) { - t[i] = Variable::make(Int(32), "t" + std::to_string(i)); - tf[i] = Variable::make(Float(32), "t" + std::to_string(i)); - } - Expr e, correct; - - // This is fine as-is. - e = ssa_block({sin(x), tf[0] * tf[0]}); - check(e, e); - - // Test a simple case. - e = ((x * x + x) * (x * x + x)) + x * x; - e += e; - correct = ssa_block({x * x, // x*x - t[0] + x, // x*x + x - t[1] * t[1] + t[0], // (x*x + x)*(x*x + x) + x*x - t[2] + t[2]}); - check(e, correct); - - // Check for idempotence (also checks a case with lets) - check(correct, correct); - - // Check a case with redundant lets - e = ssa_block({x * x, - x * x, - t[0] / t[1], - t[1] / t[1], - t[2] % t[3], - (t[4] + x * x) + x * x}); - correct = ssa_block({x * x, - t[0] / t[0], - (t[1] % t[1] + t[0]) + t[0]}); - check(e, correct); - - // Check a case with nested lets with shared subexpressions - // between the lets, and repeated names. - Expr e1 = ssa_block({x * x, // a = x*x - t[0] + x, // b = a + x - t[1] * t[1] * t[0]}); // c = b * b * a - Expr e2 = ssa_block({x * x, // a again - t[0] - x, // d = a - x - t[1] * t[1] * t[0]}); // e = d * d * a - e = ssa_block({e1 + x * x, // f = c + a - e1 + e2, // g = c + e - t[0] + t[0] * t[1]}); // h = f + f * g - - correct = ssa_block({x * x, // t0 = a = x*x - t[0] + x, // t1 = b = a + x = t0 + x - t[1] * t[1] * t[0], // t2 = c = b * b * a = t1 * t1 * t0 - t[2] + t[0], // t3 = f = c + a = t2 + t0 - t[0] - x, // t4 = d = a - x = t0 - x - t[3] + t[3] * (t[2] + t[4] * t[4] * t[0])}); // h (with g substituted in) - check(e, correct); - - // Test it scales OK. - e = x; - for (int i = 0; i < 100; i++) { - e = e * e + e + i; - e = e * e - e * i; - } - Expr result = common_subexpression_elimination(e); - - { - Expr pred = x * x + y * y > 0; - Expr index = select(x * x + y * y > 0, x * x + y * y + 2, x * x + y * y + 10); - Expr load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), const_true(), ModulusRemainder()); - Expr pred_load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), pred, ModulusRemainder()); - e = select(x * y > 10, x * y + 2, x * y + 3 + load) + pred_load; - - Expr t2 = Variable::make(Bool(), "t2"); - Expr cse_load = Load::make(Int(32), "buf", t[3], Buffer<>(), Parameter(), const_true(), ModulusRemainder()); - Expr cse_pred_load = Load::make(Int(32), "buf", t[3], Buffer<>(), Parameter(), t2, ModulusRemainder()); - correct = ssa_block({x * y, - x * x + y * y, - t[1] > 0, - select(t2, t[1] + 2, t[1] + 10), - select(t[0] > 10, t[0] + 2, t[0] + 3 + cse_load) + cse_pred_load}); - - check(e, correct); - } - - { - Expr pred = x * x + y * y > 0; - Expr index = select(x * x + y * y > 0, x * x + y * y + 2, x * x + y * y + 10); - Expr load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), const_true(), ModulusRemainder()); - Expr pred_load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), pred, ModulusRemainder()); - e = select(x * y > 10, x * y + 2, x * y + 3 + pred_load) + pred_load; - - Expr t2 = Variable::make(Bool(), "t2"); - Expr cse_load = Load::make(Int(32), "buf", select(t2, t[1] + 2, t[1] + 10), Buffer<>(), Parameter(), const_true(), ModulusRemainder()); - Expr cse_pred_load = Load::make(Int(32), "buf", select(t2, t[1] + 2, t[1] + 10), Buffer<>(), Parameter(), t2, ModulusRemainder()); - correct = ssa_block({x * y, - x * x + y * y, - t[1] > 0, - cse_pred_load, - select(t[0] > 10, t[0] + 2, t[0] + 3 + t[3]) + t[3]}); - - check(e, correct); - } - - { - Expr halide_func = Call::make(Int(32), "dummy", {0}, Call::Halide); - e = halide_func * halide_func; - Expr t0 = Variable::make(halide_func.type(), "t0"); - // It's okay to CSE Halide call within an expr - correct = Let::make("t0", halide_func, t0 * t0); - check(e, correct); - } - - std::cout << "common_subexpression_elimination test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/CSE.h b/src/CSE.h index ebc7e7cb4651..cee9c7f5a0ca 100644 --- a/src/CSE.h +++ b/src/CSE.h @@ -29,8 +29,6 @@ Expr common_subexpression_elimination(const Expr &, bool lift_all = false); * statement. Does not introduce let statements. */ Stmt common_subexpression_elimination(const Stmt &, bool lift_all = false); -void cse_test(); - } // namespace Internal } // namespace Halide diff --git a/src/CodeGen_C.cpp b/src/CodeGen_C.cpp index d6f577924c09..3552744b6c19 100644 --- a/src/CodeGen_C.cpp +++ b/src/CodeGen_C.cpp @@ -206,6 +206,23 @@ class TypeInfoGatherer : public IRGraphVisitor { } // namespace +// The binary2cpp-generated blobs above have internal linkage as far as the +// exported-symbols list for libHalide is concerned (only `halide_*` C-style +// symbols that are actually part of the runtime API get exported, along with +// the C++-mangled Halide:: namespace), so test/correctness/codegen_c.cpp +// can't reference them directly; these accessors re-expose them for testing. +string codegen_c_test_prologue_source() { + return normalize_line_endings(halide_c_template_CodeGen_C_prologue); +} + +string codegen_c_test_runtime_header_source() { + return normalize_line_endings(halide_internal_runtime_header_HalideRuntime_h); +} + +string codegen_c_test_inlined_c_source() { + return normalize_line_endings(halide_internal_initmod_inlined_c); +} + CodeGen_C::CodeGen_C(ostream &s, const Target &t, OutputKind output_kind, const std::string &guard) : IRPrinter(s), id("$$ BAD ID $$"), target(t), output_kind(output_kind) { @@ -2555,197 +2572,5 @@ void CodeGen_C::visit(const Shuffle *op) { print_assignment(op->type, rhs.str()); } -void CodeGen_C::test() { - LoweredArgument buffer_arg("buf", Argument::OutputBuffer, Int(32), 3, ArgumentEstimates{}); - LoweredArgument float_arg("alpha", Argument::InputScalar, Float(32), 0, ArgumentEstimates{}); - LoweredArgument int_arg("beta", Argument::InputScalar, Int(32), 0, ArgumentEstimates{}); - LoweredArgument user_context_arg("__user_context", Argument::InputScalar, type_of(), 0, ArgumentEstimates{}); - vector args = {buffer_arg, float_arg, int_arg, user_context_arg}; - Var x("x"); - Param alpha("alpha"); - Param beta("beta"); - Expr e = Select::make(alpha > 4.0f, print_when(x < 1, 3), 2); - Stmt s = Store::make("buf", e, x, Parameter(), const_true(), ModulusRemainder()); - s = LetStmt::make("x", beta + 1, s); - s = Block::make(s, Free::make("tmp.stack")); - s = Allocate::make("tmp.stack", Int(32), MemoryType::Stack, {127}, const_true(), s); - s = Allocate::make("tmp.heap", Int(32), MemoryType::Heap, {43, beta}, const_true(), s); - Expr buf = Variable::make(Handle(), "buf.buffer"); - s = LetStmt::make("buf", Call::make(Handle(), Call::buffer_get_host, {buf}, Call::Extern), s); - - Module m("", get_host_target()); - m.append(LoweredFunc("test1", args, s, LinkageType::External)); - - ostringstream source; - { - CodeGen_C cg(source, Target("host"), CodeGen_C::CImplementation); - cg.compile(m); - } - - string correct_source = - normalize_line_endings(halide_c_template_CodeGen_C_prologue) + '\n' + - normalize_line_endings(halide_internal_runtime_header_HalideRuntime_h) + '\n' + - normalize_line_endings(halide_internal_initmod_inlined_c) + '\n' + - '\n' + kDefineMustUseResult + normalize_line_endings(R"GOLDEN_CODE( -#ifndef HALIDE_FUNCTION_ATTRS -#define HALIDE_FUNCTION_ATTRS -#endif - - - -#ifdef __cplusplus -extern "C" { -#endif - -HALIDE_FUNCTION_ATTRS -int test1(struct halide_buffer_t *_buf_buffer, float _alpha, int32_t _beta, void const *__user_context) { - void * const _ucon = const_cast(__user_context); - halide_maybe_unused(_ucon); - auto *_0 = _halide_buffer_get_host(_buf_buffer); - auto _buf = _0; - halide_maybe_unused(_buf); - { - int64_t _1 = 43; - int64_t _2 = _1 * _beta; - if ((_2 > ((int64_t(1) << 31) - 1)) || ((_2 * sizeof(int32_t )) > ((int64_t(1) << 31) - 1))) - { - halide_error(_ucon, "32-bit signed overflow computing size of allocation tmp.heap\n"); - return -1; - } // overflow test tmp.heap - int64_t _3 = _2; - int32_t *_tmp_heap = (int32_t *)halide_malloc(_ucon, sizeof(int32_t )*_3); - if (!((_tmp_heap != nullptr) || (_3 == 0))) - { - int32_t _4 = halide_error_out_of_memory(_ucon); - return _4; - } - HalideFreeHelper _tmp_heap_free(_ucon, _tmp_heap); - { - int32_t _tmp_stack[127]; - int32_t _5 = _beta + 1; - int32_t _6; - bool _7 = _5 < 1; - if (_7) - { - char b0[1024]; - snprintf(b0, 1024, "%lld%s", (long long)(3), "\n"); - auto *_8 = b0; - halide_print(_ucon, _8); - int32_t _9 = 0; - int32_t _10 = return_second(_9, 3); - _6 = _10; - } // if _7 - else - { - _6 = 3; - } // if _7 else - int32_t _11 = _6; - float _12 = float_from_bits(1082130432 /* 4 */); - bool _13 = _alpha > _12; - int32_t _14 = (int32_t)(_13 ? _11 : 2); - ((int32_t *)_buf)[_5] = _14; - } // alloc _tmp_stack - _tmp_heap_free.free(); - } // alloc _tmp_heap - return 0; -} - -#ifdef __cplusplus -} // extern "C" -#endif - -)GOLDEN_CODE"); - - const auto compare_srcs = [](const string &actual, const string &expected) { - if (actual != expected) { - int diff = 0; - while (actual[diff] == expected[diff]) { - diff++; - } - int diff_end = diff + 1; - while (diff > 0 && actual[diff] != '\n') { - diff--; - } - while (diff_end < (int)actual.size() && actual[diff_end] != '\n') { - diff_end++; - } - - internal_error - << "Correct source code:\n" - << expected - << "Actual source code:\n" - << actual - << "Difference starts at:\n" - << "Correct: " << expected.substr(diff, diff_end - diff) << "\n" - << "Actual: " << actual.substr(diff, diff_end - diff) << "\n"; - } - }; - - compare_srcs(source.str(), correct_source); - - ostringstream function_info; - { - CodeGen_C cg(function_info, Target("host-no_runtime"), CodeGen_C::CPlusPlusFunctionInfoHeader, "Function/Info/Test"); - cg.compile(m); - } - - string correct_function_info = normalize_line_endings(R"GOLDEN_CODE(#ifndef HALIDE_FUNCTION_INFO__Function___Info___Test -#define HALIDE_FUNCTION_INFO__Function___Info___Test - -/* MACHINE GENERATED By Halide. */ - -#if !(__cplusplus >= 201703L || _MSVC_LANG >= 201703L) -#error "This file requires C++17 or later; please upgrade your compiler." -#endif - -#include "HalideRuntime.h" - - -/** - * This function returns a constexpr array of information about a Halide-generated - * function's argument signature (e.g., number of arguments, type of each, etc). - * While this is a subset of the information provided by the existing _metadata - * function, it has the distinct advantage of allowing one to use the information - * it at compile time (rather than runtime). This can be quite useful for producing - * e.g. automatic call wrappers, etc. - * - * For instance, to compute the number of Buffers in a Function, one could do something - * like: - * - * using namespace HalideFunctionInfo; - * - * template - * constexpr size_t count_buffers(const std::array args) { - * size_t buffer_count = 0; - * for (const auto a : args) { - * if (a.kind == InputBuffer || a.kind == OutputBuffer) { - * buffer_count++; - * } - * } - * return buffer_count; - * } - * - * constexpr size_t count = count_buffers(metadata_tester_argument_info()); - * - * The value of `count` will be computed entirely at compile-time, with no runtime - * impact aside from the numerical value of the constant. - */ - -inline constexpr std::array<::HalideFunctionInfo::ArgumentInfo, 4> test1_argument_info() { - return {{ - {"buf", ::HalideFunctionInfo::OutputBuffer, 3, halide_type_t{halide_type_int, 32}}, - {"alpha", ::HalideFunctionInfo::InputScalar, 0, halide_type_t{halide_type_float, 32}}, - {"beta", ::HalideFunctionInfo::InputScalar, 0, halide_type_t{halide_type_int, 32}}, - {"__user_context", ::HalideFunctionInfo::InputScalar, 0, halide_type_t{halide_type_handle, 64}}, - }}; -} -#endif -)GOLDEN_CODE"); - - compare_srcs(function_info.str(), correct_function_info); - - std::cout << "CodeGen_C test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/CodeGen_C.h b/src/CodeGen_C.h index 4c97d6907067..3a4826c257e5 100644 --- a/src/CodeGen_C.h +++ b/src/CodeGen_C.h @@ -54,8 +54,6 @@ class CodeGen_C : public IRPrinter { return target; } - static void test(); - protected: enum class IntegerSuffixStyle { PlainC = 0, @@ -307,6 +305,18 @@ class CodeGen_C : public IRPrinter { void emit_halide_free_helper(const std::string &alloc_name, const std::string &free_function); }; +/** @name Internal-only accessors for test/correctness/codegen_c.cpp + * The underlying binary2cpp-generated blobs aren't visible outside + * libHalide (they're filtered out of the exported-symbols list), so + * expose their (CRLF-normalized) contents through ordinary functions + * in the Halide::Internal namespace instead. + */ +///@{ +std::string codegen_c_test_prologue_source(); +std::string codegen_c_test_runtime_header_source(); +std::string codegen_c_test_inlined_c_source(); +///@} + } // namespace Internal } // namespace Halide diff --git a/src/Deinterleave.cpp b/src/Deinterleave.cpp index 90e341ad4938..878f96fa493a 100644 --- a/src/Deinterleave.cpp +++ b/src/Deinterleave.cpp @@ -15,8 +15,6 @@ namespace Halide { namespace Internal { -using std::pair; - namespace { class StoreCollector : public IRMutator { @@ -812,45 +810,5 @@ Stmt rewrite_interleavings(const Stmt &s) { return Interleaver()(s); } -namespace { -void check(Expr a, const Expr &even, const Expr &odd) { - a = simplify(a); - Expr correct_even = extract_even_lanes(a); - Expr correct_odd = extract_odd_lanes(a); - if (!equal(correct_even, even)) { - internal_error << correct_even << " != " << even << "\n"; - } - if (!equal(correct_odd, odd)) { - internal_error << correct_odd << " != " << odd << "\n"; - } -} -} // namespace - -void deinterleave_vector_test() { - std::pair result; - Expr x = Variable::make(Int(32), "x"); - Expr ramp = Ramp::make(x + 4, 3, 8); - Expr ramp_a = Ramp::make(x + 4, 6, 4); - Expr ramp_b = Ramp::make(x + 7, 6, 4); - Expr broadcast = Broadcast::make(x + 4, 16); - Expr broadcast_a = Broadcast::make(x + 4, 8); - const Expr &broadcast_b = broadcast_a; - - check(ramp, ramp_a, ramp_b); - check(broadcast, broadcast_a, broadcast_b); - - check(Load::make(ramp.type(), "buf", ramp, Buffer<>(), Parameter(), const_true(ramp.type().lanes()), ModulusRemainder()), - Load::make(ramp_a.type(), "buf", ramp_a, Buffer<>(), Parameter(), const_true(ramp_a.type().lanes()), ModulusRemainder()), - Load::make(ramp_b.type(), "buf", ramp_b, Buffer<>(), Parameter(), const_true(ramp_b.type().lanes()), ModulusRemainder())); - - Expr vec_x = Variable::make(Int(32, 4), "vec_x"); - Expr vec_y = Variable::make(Int(32, 4), "vec_y"); - check(Shuffle::make({vec_x, vec_y}, {0, 4, 2, 6, 4, 2, 3, 7, 1, 2, 3, 4}), - Shuffle::make({vec_x, vec_y}, {0, 2, 4, 3, 1, 3}), - Shuffle::make({vec_x, vec_y}, {4, 6, 2, 7, 2, 4})); - - std::cout << "deinterleave_vector test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/Deinterleave.h b/src/Deinterleave.h index 485641f71a5f..a1fb74ab6e91 100644 --- a/src/Deinterleave.h +++ b/src/Deinterleave.h @@ -27,8 +27,6 @@ Expr extract_lane(const Expr &vec, int lane); * intrinsic */ Stmt rewrite_interleavings(const Stmt &s); -void deinterleave_vector_test(); - } // namespace Internal } // namespace Halide diff --git a/src/Generator.cpp b/src/Generator.cpp index e2e96596cbeb..84e5da2a29c5 100644 --- a/src/Generator.cpp +++ b/src/Generator.cpp @@ -2071,205 +2071,5 @@ RegisterGenerator::RegisterGenerator(const char *registered_name, GeneratorFacto Internal::GeneratorRegistry::register_factory(registered_name, std::move(generator_factory)); } -void generator_test() { - GeneratorContext context(get_host_target().without_feature(Target::Profile)); - - // Verify that the Generator's internal phase actually prevents unsupported - // order of operations. - { - class Tester : public Generator { - public: - GeneratorParam gp0{"gp0", 0}; - GeneratorParam gp1{"gp1", 1.f}; - GeneratorParam gp2{"gp2", 2}; - - Input input{"input"}; - Output output{"output", Int(32), 1}; - - void generate() { - internal_assert(gp0 == 1); - internal_assert(gp1 == 2.f); - internal_assert(gp2 == (uint64_t)2); // unchanged - Var x; - output(x) = input + gp0; - } - void schedule() { - // empty - } - }; - - Tester tester; - tester.init_from_context(context); - internal_assert(tester.phase == GeneratorBase::Created); - - // Verify that calling GeneratorParam::set() works. - tester.gp0.set(1); - - tester.set_inputs_vector({{StubInput(42)}}); - internal_assert(tester.phase == GeneratorBase::InputsSet); - - // tester.set_inputs_vector({{StubInput(43)}}); // This will assert-fail. - - // Also ok to call in this phase. - tester.gp1.set(2.f); - - tester.call_generate(); - internal_assert(tester.phase == GeneratorBase::GenerateCalled); - - // tester.set_inputs_vector({{StubInput(44)}}); // This will assert-fail. - // tester.gp2.set(2); // This will assert-fail. - - tester.call_schedule(); - internal_assert(tester.phase == GeneratorBase::ScheduleCalled); - - // tester.set_inputs_vector({{StubInput(45)}}); // This will assert-fail. - // tester.gp2.set(2); // This will assert-fail. - // tester.sp2.set(202); // This will assert-fail. - } - - // Verify that set_inputs() works properly, even if the specific subtype of Generator is not known. - { - class Tester : public Generator { - public: - Input input_int{"input_int"}; - Input input_float{"input_float"}; - Input input_byte{"input_byte"}; - Input input_scalar_array{"input_scalar_array"}; - Input input_func_typed{"input_func_typed", Int(16), 1}; - Input input_func_untyped{"input_func_untyped", 1}; - Input input_func_array{"input_func_array", 1}; - Input> input_buffer_typed{"input_buffer_typed"}; - Input> input_buffer_untyped{"input_buffer_untyped"}; - Output output{"output", Float(32), 1}; - - void generate() { - Var x; - output(x) = input_int + - input_float + - input_byte + - input_scalar_array[3] + - input_func_untyped(x) + - input_func_typed(x) + - input_func_array[0](x) + - input_buffer_typed(x, 0, 0) + - input_buffer_untyped(x, Halide::_); - } - void schedule() { - // nothing - } - }; - - Tester tester_instance; - tester_instance.init_from_context(context); - // Use a base-typed reference to verify the code below doesn't know about subtype - GeneratorBase &tester = tester_instance; - - const int i = 1234; - const float f = 2.25f; - const uint8_t b = 0x42; - const std::vector a = {1, 2, 3, 4}; - Var x; - Func fn_typed, fn_untyped; - fn_typed(x) = make_const(Int(16), 38); - fn_untyped(x) = 32.f; - const std::vector fn_array = {fn_untyped, fn_untyped}; - - Buffer buf_typed(1, 1, 1); - Buffer buf_untyped(1); - - buf_typed.fill(33); - buf_untyped.fill(34); - - // set_inputs() requires inputs in Input<>-decl-order, - // and all inputs match type exactly. - tester.set_inputs(i, f, b, a, fn_typed, fn_untyped, fn_array, buf_typed, buf_untyped); - tester.call_generate(); - tester.call_schedule(); - - Buffer im = tester_instance.realize({1}); - internal_assert(im.dimensions() == 1); - internal_assert(im.dim(0).extent() == 1); - internal_assert(im(0) == 1475.25f) << "Expected 1475.25 but saw " << im(0); - } - - // Verify that array inputs and outputs are typed correctly. - { - class Tester : public Generator { - public: - Input expr_array_input{"expr_array_input"}; - Input func_array_input{"input_func_array"}; - Input[]> buffer_array_input{"buffer_array_input"}; - - Input expr_array_output{"expr_array_output"}; - Output func_array_output{"func_array_output"}; - Output[]> buffer_array_output{"buffer_array_output"}; - - void generate() { - } - }; - - Tester tester_instance; - - static_assert(std::is_same_v, "type mismatch"); - static_assert(std::is_same_v, "type mismatch"); - - static_assert(std::is_same_v, "type mismatch"); - static_assert(std::is_same_v, "type mismatch"); - - static_assert(std::is_same_v, "type mismatch"); - static_assert(std::is_same_v, "type mismatch"); - } - - class GPTester : public Generator { - public: - GeneratorParam gp{"gp", 0}; - Output output{"output", Int(32), 0}; - - void generate() { - internal_assert(get_target().has_feature(Target::Profile)); - output() = 0; - } - void schedule() { - } - - // Test that we can override init_from_context() to modify the target - // we use. (Generally speaking, your code probably should ever need to - // do this; this code only does it for testing purposes. See comments - // in Generator.h.) - void init_from_context(const GeneratorContext &context) override { - auto t = context.target().with_feature(Target::Profile); - Generator::init_from_context(context.with_target(t)); - } - }; - GPTester gp_tester; - gp_tester.init_from_context(context); - // Accessing the GeneratorParam will assert-fail if we - // don't do some minimal setup here. - gp_tester.set_inputs_vector({}); - gp_tester.call_generate(); - gp_tester.call_schedule(); - auto &gp = gp_tester.gp; - - // Verify that RDom parameter-pack variants can convert GeneratorParam to Expr - RDom rdom(0, gp, 0, gp); - - // Verify that Func parameter-pack variants can convert GeneratorParam to Expr - Var x, y; - Func f, g; - f(x, y) = x + y; - g(x, y) = f(gp, gp); // check Func::operator() overloads - g(rdom.x, rdom.y) += f(rdom.x, rdom.y); - g.update(0).reorder(rdom.y, rdom.x); // check Func::reorder() overloads for RDom::operator RVar() - - // Verify that print() parameter-pack variants can convert GeneratorParam to Expr - print(f(0, 0), g(1, 1), gp); - print_when(true, f(0, 0), g(1, 1), gp); - - // Verify that Tuple parameter-pack variants can convert GeneratorParam to Expr - Tuple t(gp, gp, gp); - - std::cout << "Generator test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/IREquality.cpp b/src/IREquality.cpp index 76c8b5598e87..ff33a504386c 100644 --- a/src/IREquality.cpp +++ b/src/IREquality.cpp @@ -535,89 +535,5 @@ bool graph_less_than_impl(const IRNode &a, const IRNode &b) { return Comparer<128>(cache).compare(a, b) == Order::LessThan; } -// Testing code -namespace { - -Order flip_result(Order r) { - switch (r) { - case Order::Equal: - r = Order::Equal; - break; - case Order::LessThan: - r = Order::GreaterThan; - break; - case Order::GreaterThan: - r = Order::LessThan; - break; - } - return r; -} - -std::ostream &operator<<(std::ostream &s, Order o) { - switch (o) { - case Order::Equal: - s << "Equal"; - break; - case Order::LessThan: - s << "LessThan"; - break; - case Order::GreaterThan: - s << "GreaterThan"; - break; - } - return s; -} - -void check_equal(const Expr &a, const Expr &b) { - const IRNode *cache[256] = {}; - Order r = Comparer<128>(cache).compare(*(a.get()), *(b.get())); - internal_assert(r == Order::Equal) - << "Error in ir_equality_test: " << r - << " instead of " << Order::Equal - << " when comparing:\n" - << a - << "\nand\n" - << b << "\n"; -} - -void check_not_equal(const Expr &a, const Expr &b) { - const IRNode *cache[256] = {}; - Order r1 = Comparer<128>(cache).compare(*(a.get()), *(b.get())); - Order r2 = Comparer<128>(cache).compare(*(b.get()), *(a.get())); - internal_assert(r1 != Order::Equal && - flip_result(r1) == r2) - << "Error in ir_equality_test: " << r1 - << " is not the opposite of " << r2 - << " when comparing:\n" - << a - << "\nand\n" - << b << "\n"; -} - -} // namespace - -void ir_equality_test() { - Expr x = Variable::make(Int(32), "x"); - check_equal(Ramp::make(x, 4, 3), Ramp::make(x, 4, 3)); - check_not_equal(Ramp::make(x, 2, 3), Ramp::make(x, 4, 3)); - - check_equal(x, Variable::make(Int(32), "x")); - check_not_equal(x, Variable::make(Int(32), "y")); - - // Something that will hang if IREquality has poor computational - // complexity. - Expr e1 = x, e2 = x; - for (int i = 0; i < 100; i++) { - e1 = e1 * e1 + e1; - e2 = e2 * e2 + e2; - } - check_equal(e1, e2); - // These are only discovered to be not equal way down the tree: - e2 = e2 * e2 + e2; - check_not_equal(e1, e2); - - std::cout << "ir_equality_test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/IREquality.h b/src/IREquality.h index beb5e60ae823..c6987f873c4e 100644 --- a/src/IREquality.h +++ b/src/IREquality.h @@ -157,8 +157,6 @@ struct IRGraphDeepCompare { } }; -void ir_equality_test(); - } // namespace Internal } // namespace Halide diff --git a/src/IRMatch.cpp b/src/IRMatch.cpp index 7649fa9c2152..62e0b3392ac3 100644 --- a/src/IRMatch.cpp +++ b/src/IRMatch.cpp @@ -15,42 +15,6 @@ using std::map; using std::string; using std::vector; -void expr_match_test() { - vector matches; - Expr w = Variable::make(Int(32), "*"); - Expr fw = Variable::make(Float(32), "*"); - Expr x = Variable::make(Int(32), "x"); - Expr y = Variable::make(Int(32), "y"); - Expr fx = Variable::make(Float(32), "fx"); - Expr fy = Variable::make(Float(32), "fy"); - - Expr vec_wild = Variable::make(Int(32, 4), "*"); - - internal_assert(expr_match(w, 3, matches) && - equal(matches[0], 3)); - - internal_assert(expr_match(w + 3, (y * 2) + 3, matches) && - equal(matches[0], y * 2)); - - internal_assert(expr_match(fw * 17 + cast(w + cast(fw)), - (81.0f * fy) * 17 + cast(x / 2 + cast(x + 4.5f)), matches) && - matches.size() == 3 && - equal(matches[0], 81.0f * fy) && - equal(matches[1], x / 2) && - equal(matches[2], x + 4.5f)); - - internal_assert(!expr_match(fw + 17, fx + 18, matches) && - matches.empty()); - internal_assert(!expr_match((w * 2) + 17, fx + 17, matches) && - matches.empty()); - internal_assert(!expr_match(w * 3, 3 * x, matches) && - matches.empty()); - - internal_assert(expr_match(vec_wild * 3, Ramp::make(x, y, 4) * 3, matches)); - - std::cout << "expr_match test passed\n"; -} - namespace { class IRMatch : public IRVisitor { diff --git a/src/IRMatch.h b/src/IRMatch.h index f0ec0c57a263..6fa4cadc4eae 100644 --- a/src/IRMatch.h +++ b/src/IRMatch.h @@ -54,8 +54,6 @@ bool expr_match(const Expr &pattern, const Expr &expr, std::map args(1); - args[0] = x % 3; - Expr call = Call::make(i32, "buf", args, Call::Extern); - Stmt store2 = Store::make("out", call + 1, x, Parameter(), const_true(), ModulusRemainder(3, 5)); - Stmt for_loop2 = For::make("x", 0, y, ForType::Vectorized, Partition::Auto, DeviceAPI::Host, store2); - - Stmt producer = ProducerConsumer::make_produce("buf", for_loop); - Stmt consumer = ProducerConsumer::make_consume("buf", for_loop2); - Stmt pipeline = Block::make(producer, consumer); - - Stmt assertion = AssertStmt::make(y >= 3, Call::make(Int(32), "halide_error_param_too_small_i64", - {string("y"), y, 3}, Call::Extern)); - Stmt block = Block::make(assertion, pipeline); - Stmt let_stmt = LetStmt::make("y", 17, block); - Stmt allocate = Allocate::make("buf", f32, MemoryType::Stack, {1023}, const_true(), let_stmt); - - ostringstream source; - source << allocate; - std::string correct_source = - "allocate buf[float32 * 1023] in Stack\n" - "let y = 17\n" - "assert(y >= 3, halide_error_param_too_small_i64(\"y\", y, 3))\n" - "produce buf {\n" - " parallel (x, -2, y + 2) {\n" - " buf[y - 1] = (x*17)/(x - 3)\n" - " }\n" - "}\n" - "consume buf {\n" - " vectorized (x, 0, y) {\n" - " out[x] = buf(x % 3) + 1\n" - " }\n" - "}\n"; - - if (source.str() != correct_source) { - internal_error << "Correct output:\n" - << correct_source - << "Actual output:\n" - << source.str(); - } - std::cout << "IRPrinter test passed\n"; -} - std::ostream &operator<<(std::ostream &stream, IRNodeType type) { #define CASE(e) \ case IRNodeType::e: \ diff --git a/src/IRPrinter.h b/src/IRPrinter.h index 571edbc24a1c..028932a5ed6a 100644 --- a/src/IRPrinter.h +++ b/src/IRPrinter.h @@ -163,8 +163,6 @@ class IRPrinter : public IRVisitor { * trailing punctuation. */ void print_list(const std::vector &exprs); - static void test(); - protected: Indentation get_indent() const { return Indentation{indent}; diff --git a/src/ModulusRemainder.cpp b/src/ModulusRemainder.cpp index f4f9752823e2..99dfdec43c2d 100644 --- a/src/ModulusRemainder.cpp +++ b/src/ModulusRemainder.cpp @@ -342,38 +342,6 @@ ModulusRemainder ComputeModulusRemainder::analyze(const Expr &e) { return result; } -namespace { -void check(const Expr &e, int64_t m, int64_t r) { - ModulusRemainder result = modulus_remainder(e); - if (result.modulus != m || result.remainder != r) { - std::cerr << "Test failed for modulus_remainder:\n"; - std::cerr << "Expression: " << e << "\n"; - std::cerr << "Correct modulus, remainder = " << m << ", " << r << "\n"; - std::cerr << "Computed modulus, remainder = " - << result.modulus << ", " - << result.remainder << "\n"; - exit(1); - } -} -} // namespace - -void modulus_remainder_test() { - Expr x = Variable::make(Int(32), "x"); - Expr y = Variable::make(Int(32), "y"); - - check((30 * x + 3) + (40 * y + 2), 10, 5); - check((6 * x + 3) * (4 * y + 1), 2, 1); - check(max(30 * x - 24, 40 * y + 31), 5, 1); - check(10 * x - 33 * y, 1, 0); - check(10 * x - 35 * y, 5, 0); - check(123, 0, 123); - check(Let::make("y", x * 3 + 4, y * 3 + 4), 9, 7); - // Check overflow - check((5045320 * x + 4) * (405713 * y + 3) * (8000123 * x + 4354), 1, 0); - - std::cout << "modulus_remainder test passed\n"; -} - int64_t gcd(int64_t a, int64_t b) { // We don't care about factors of -1, so we're going to do this unsigned so // that we can take an absolute value without worrying about INT64_MIN. diff --git a/src/ModulusRemainder.h b/src/ModulusRemainder.h index 97fca42273fe..bdc7f9928b6e 100644 --- a/src/ModulusRemainder.h +++ b/src/ModulusRemainder.h @@ -89,8 +89,6 @@ HALIDE_MUST_USE_RESULT bool reduce_expr_modulo(const Expr &e, int64_t modulus, i HALIDE_MUST_USE_RESULT bool reduce_expr_modulo(const Expr &e, int64_t modulus, int64_t *remainder, const Scope &scope); ///@} -void modulus_remainder_test(); - /** The greatest common divisor of two integers. Returns a positive result, * unless both args are INT64_MIN. */ int64_t gcd(int64_t, int64_t); diff --git a/src/Monotonic.cpp b/src/Monotonic.cpp index 63e4f01f2ef1..71118e5aee07 100644 --- a/src/Monotonic.cpp +++ b/src/Monotonic.cpp @@ -539,100 +539,5 @@ Monotonic is_monotonic(const Expr &e, const std::string &var, const Scope= y); - check_increasing(x > y); - - check_decreasing(-x); - check_decreasing(x * -4); - check_decreasing(x / -4); - check_decreasing(y - x); - check_decreasing(x < y); - check_decreasing(x <= y); - - check_unknown(x == y); - check_unknown(x != y); - check_increasing(y <= x); - check_increasing(y < x); - check_decreasing(x <= y); - check_decreasing(x < y); - check_unknown(x * y); - - // Not constant despite having constant args, because there's a side-effect. - check_unknown(Call::make(Int(32), "foo", {Expr(3)}, Call::Extern)); - - check_increasing(select(y == 2, x, x + 4)); - check_decreasing(select(y == 2, -x, x * -4)); - - check_unknown(select(x > 2, x - 2, x)); - check_unknown(select(x < 2, x, x - 2)); - check_unknown(select(x > 2, -x + 2, -x)); - check_unknown(select(x < 2, -x, -x + 2)); - check_increasing(select(x > 2, x - 1, x)); - check_increasing(select(x < 2, x, x - 1)); - check_decreasing(select(x > 2, -x + 1, -x)); - check_decreasing(select(x < 2, -x, -x + 1)); - - check_unknown(select(x < 2, x, x - 5)); - check_unknown(select(x > 2, x - 5, x)); - - check_unknown(select(x > 0, y, z)); - - check_increasing(select(0 < x, promise_clamped(x - 1, x - 1, z) + 1, promise_clamped(x, x, z))); - - check_constant(y); - - check_increasing(select(x < 17, y, y + 1)); - check_increasing(select(x > 17, y, y - 1)); - check_decreasing(select(x < 17, y, y - 1)); - check_decreasing(select(x > 17, y, y + 1)); - - check_increasing(select(x % 2 == 0, x + 3, x + 3)); - - check_constant(select(y > 3, y + 23, y - 65)); - - check_decreasing(select(2 <= x, 0, 1)); - check_increasing(select(2 <= x, 0, 1) + x); - check_decreasing(-min(x, 16)); - - check_unknown(select(0 < x, max(min(x, 4), 3), 4)); - - std::cout << "is_monotonic test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/Monotonic.h b/src/Monotonic.h index c8ba66195961..b619e73160bd 100644 --- a/src/Monotonic.h +++ b/src/Monotonic.h @@ -34,8 +34,6 @@ Monotonic is_monotonic(const Expr &e, const std::string &var, const Scope &expected) { - std::vector result; - split_into_ands(pred, result); - bool is_equal = true; - - if (result.size() != expected.size()) { - is_equal = false; - } else { - for (size_t i = 0; i < expected.size(); ++i) { - if (!equal(simplify(result[i]), simplify(expected[i]))) { - is_equal = false; - break; - } - } - } - - if (!is_equal) { - std::cout << "Expect predicate " << pred << " to be split into:\n"; - for (const auto &e : expected) { - std::cout << " " << e << "\n"; - } - std::cout << "Got:\n"; - for (const auto &e : result) { - std::cout << " " << e << "\n"; - } - internal_error << "\n"; - } -} - -} // namespace - -void split_predicate_test() { - Expr x = Var("x"), y = Var("y"), z = Var("z"), w = Var("w"); - - { - std::vector expected; - expected.push_back(z < 10); - check(z < 10, expected); - } - - { - std::vector expected; - expected.push_back((x < y) || (x == 10)); - check((x < y) || (x == 10), expected); - } - - { - std::vector expected; - expected.push_back(x < y); - expected.push_back(x == 10); - check((x < y) && (x == 10), expected); - } - - { - std::vector expected; - expected.push_back(x < y); - expected.push_back(x == 10); - expected.push_back(y == z); - check((x < y) && (x == 10) && (y == z), expected); - } - - { - std::vector expected; - expected.push_back((w == 1) || ((x == 10) && (y == z))); - check((w == 1) || ((x == 10) && (y == z)), expected); - } - - { - std::vector expected; - expected.push_back(x < y); - expected.push_back((w == 1) || ((x == 10) && (y == z))); - check((x < y) && ((w == 1) || ((x == 10) && (y == z))), expected); - } - - std::cout << "Split predicate test passed\n"; -} - struct ReductionDomainContents { mutable RefCount ref_count; std::vector domain; diff --git a/src/Reduction.h b/src/Reduction.h index d93bf741cd09..d04c94c74895 100644 --- a/src/Reduction.h +++ b/src/Reduction.h @@ -112,8 +112,6 @@ class ReductionDomain { void mutate(IRMutator *); }; -void split_predicate_test(); - } // namespace Internal } // namespace Halide diff --git a/src/SpirvIR.cpp b/src/SpirvIR.cpp index 6455a23999b2..9b85c60549fc 100644 --- a/src/SpirvIR.cpp +++ b/src/SpirvIR.cpp @@ -3910,64 +3910,3 @@ const std::string &spirv_op_name(SpvId op) { } // namespace Halide #endif // WITH_SPIRV - -namespace Halide { -namespace Internal { - -void spirv_ir_test() { - -#ifdef WITH_SPIRV - SpvBinary binary; - SpvInstruction label_inst = SpvFactory::label(777); - assert(label_inst.result_id() == 777); - assert(label_inst.op_code() == SpvOpLabel); - label_inst.encode(binary); - assert(binary.size() == 2); // encodes to 2x 32-bit words [Length|OpCode, ResultId] - - SpvBuilder builder; - SpvId void_type_id = builder.reserve_id(SpvVoidTypeId); - SpvInstruction void_inst = SpvFactory::void_type(void_type_id); - builder.current_module().add_type(void_inst); - - SpvId int_type_id = builder.declare_type(Int(32)); - SpvId uint_type_id = builder.declare_type(UInt(32)); - SpvId float_type_id = builder.declare_type(Float(32)); - - SpvBuilder::ParamTypes param_types = {int_type_id, uint_type_id, float_type_id}; - SpvId kernel_func_id = builder.add_function("kernel_func", void_type_id, param_types); - SpvFunction kernel_func = builder.lookup_function(kernel_func_id); - - builder.enter_function(kernel_func); - SpvId intrinsic_type_id = builder.declare_type(Type(Type::UInt, 32, 3)); - SpvId intrinsic_id = builder.declare_global_variable("InputVar", intrinsic_type_id, SpvStorageClassInput); - - SpvId output_type_id = builder.declare_type(Type(Type::UInt, 32, 1)); - SpvId output_id = builder.declare_global_variable("OutputVar", output_type_id, SpvStorageClassOutput); - - SpvBuilder::Variables entry_point_variables = {intrinsic_id, output_id}; - builder.add_entry_point(kernel_func_id, SpvExecutionModelKernel, entry_point_variables); - - SpvBuilder::Literals annotation_literals = {SpvBuiltInWorkgroupId}; - builder.add_annotation(intrinsic_id, SpvDecorationBuiltIn, annotation_literals); - - SpvId intrinsic_loaded_id = builder.reserve_id(); - builder.append(SpvFactory::load(intrinsic_type_id, intrinsic_loaded_id, intrinsic_id)); - - float float_value = 32.0f; - SpvId float_src_id = builder.add_constant(Float(32), &float_value); - SpvId converted_value_id = builder.reserve_id(SpvResultId); - builder.append(SpvFactory::convert(SpvOpConvertFToU, uint_type_id, converted_value_id, float_src_id)); - builder.append(SpvFactory::store(output_id, converted_value_id)); - builder.leave_function(); - - binary.clear(); - builder.encode(binary); - - std::cout << "SpirV IR test passed\n"; -#else - std::cout << "SpirV IR test *disabled*" << std::endl; -#endif -} - -} // namespace Internal -} // namespace Halide diff --git a/src/SpirvIR.h b/src/SpirvIR.h index 6e5b60b4ba90..d9cf8fdecf4f 100644 --- a/src/SpirvIR.h +++ b/src/SpirvIR.h @@ -808,13 +808,4 @@ std::ostream &operator<<(std::ostream &stream, const SpvInstruction &); #endif // WITH_SPIRV -namespace Halide { -namespace Internal { - -/** Internal test for SPIR-V IR **/ -void spirv_ir_test(); - -} // namespace Internal -} // namespace Halide - #endif // HALIDE_SPIRV_IR_H diff --git a/src/UniquifyVariableNames.cpp b/src/UniquifyVariableNames.cpp index 6eec453afc63..c4485765862a 100644 --- a/src/UniquifyVariableNames.cpp +++ b/src/UniquifyVariableNames.cpp @@ -1,15 +1,12 @@ #include "UniquifyVariableNames.h" -#include "IREquality.h" #include "IRMutator.h" #include "IROperator.h" #include "IRVisitor.h" #include "Scope.h" -#include "Var.h" namespace Halide { namespace Internal { -using std::pair; using std::string; using std::vector; @@ -170,85 +167,5 @@ Stmt uniquify_variable_names(const Stmt &s) { return UniquifyVariableNames(&finder.free_vars)(s); } -namespace { -void check(vector> in, - vector> out) { - Stmt in_stmt = Evaluate::make(0), out_stmt = Evaluate::make(0); - for (const auto &[var, value] : reverse_view(in)) { - in_stmt = LetStmt::make(var.name(), value, in_stmt); - } - for (const auto &[var, value] : reverse_view(out)) { - out_stmt = LetStmt::make(var.name(), value, out_stmt); - } - - Stmt s = uniquify_variable_names(in_stmt); - - internal_assert(equal(s, out_stmt)) - << "Failure in uniquify_variable_names\n" - << "Input:\n" - << in_stmt << "\n" - << "Produced:\n" - << s << "\n" - << "Correct output:\n" - << out_stmt << "\n"; -} -} // namespace - -void uniquify_variable_names_test() { - Var x("x"), x_1("x_1"), x_2("x_2"), x_3{"x_3"}; - Var y("y"), y_1("y_1"), y_2("y_2"), y_3{"y_3"}; - - // Stmts with all names already unique should be unchanged - check({{x, 3}, - {y, x}}, - {{x, 3}, - {y, x}}); - - // Shadowed definitions of Vars should be given unique names - check({{x, 3}, - {y, x}, - {x, x + y}, - {y, x + y}, - {x, x + y}, - {y, x + y}}, - {{x, 3}, - {y, x}, - {x_1, x + y}, - {y_1, x_1 + y}, - {x_2, x_1 + y_1}, - {y_2, x_2 + y_1}}); - - // Check a case with a free var after then end of the scope of a let of the same name - check({{x, Let::make(y.name(), 3, y)}, // y is bound - {x, y}}, // This is not the same y. It's free and can't be renamed. - {{x, Let::make(y_1.name(), 3, y_1)}, // We rename the bound one - {x_1, y}}); - - // An existing in-scope use of one of the names that would be - // autogenerated should be skipped over - check({{x_1, 8}, - {x, 3}, - {y, x}, - {x, x + y}, - {y, x + y}, - {x, x + y}, - {y, x + y}}, - {{x_1, 8}, - {x, 3}, - {y, x}, - {x_2, x + y}, - {y_1, x_2 + y}, - {x_3, x_2 + y_1}, - {y_2, x_3 + y_1}}); - - // Check parallel bindings. The scope doesn't overlap so they can keep their name - check({{x, Let::make(y.name(), 3, y)}, - {x, Let::make(y.name(), 4, y)}}, - {{x, Let::make(y.name(), 3, y)}, - {x_1, Let::make(y.name(), 4, y)}}); - - std::cout << "uniquify_variable_names test passed\n"; -} - } // namespace Internal } // namespace Halide diff --git a/src/UniquifyVariableNames.h b/src/UniquifyVariableNames.h index efe142d6d2a5..4340f443bd4f 100644 --- a/src/UniquifyVariableNames.h +++ b/src/UniquifyVariableNames.h @@ -15,8 +15,6 @@ namespace Internal { * semantic equivalence. */ Stmt uniquify_variable_names(const Stmt &s); -void uniquify_variable_names_test(); - } // namespace Internal } // namespace Halide diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 39a0062d1ecb..ac271532b281 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,11 +1,12 @@ include(HalideTestHelpers) include(CheckCXXCompilerFlag) -# Internal tests are a special case. -# HalideTestHelpers depends on this test being present. -add_executable(_test_internal internal.cpp) +# _test_internal is not itself a test; it exists purely so that the other +# test executables below can reuse its precompiled header via +# target_precompile_headers(REUSE_FROM). HalideTestHelpers depends on this +# target being present. +add_executable(_test_internal EXCLUDE_FROM_ALL pch_helper.cpp) target_link_libraries(_test_internal PRIVATE Halide::Test Halide::TerminateHandler) -target_include_directories(_test_internal PRIVATE "${Halide_SOURCE_DIR}/src") target_precompile_headers(_test_internal PRIVATE ) if (Halide_CCACHE_BUILD) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -16,8 +17,6 @@ if (Halide_CCACHE_BUILD) endif () endif () -add_halide_test(_test_internal GROUPS internal) - Halide_feature(WITH_TEST_AUTO_SCHEDULE "Build autoscheduler tests" AUTO DEPENDS WITH_AUTOSCHEDULERS) if (WITH_TEST_AUTO_SCHEDULE) add_subdirectory(autoschedulers) diff --git a/test/correctness/CMakeLists.txt b/test/correctness/CMakeLists.txt index 781b111a09e6..01ae18b7cc81 100644 --- a/test/correctness/CMakeLists.txt +++ b/test/correctness/CMakeLists.txt @@ -6,6 +6,7 @@ tests( # keep-sorted start case=no align_bounds.cpp argmax.cpp + associativity.cpp async_device_copy.cpp autodiff.cpp bad_likely.cpp @@ -24,6 +25,7 @@ tests( bounds_inference_chunk.cpp bounds_inference_complex.cpp bounds_inference_outer_split.cpp + bounds_internal.cpp bounds_of_abs.cpp bounds_of_cast.cpp bounds_of_func.cpp @@ -46,6 +48,7 @@ tests( chunk_sharing.cpp circular_reference_leak.cpp code_explosion.cpp + codegen_c.cpp compare_vars.cpp compile_to.cpp compile_to_bitcode.cpp @@ -64,7 +67,9 @@ tests( constant_type.cpp constraints.cpp convolution_multiple_kernels.cpp + cplusplus_mangle.cpp cross_compilation.cpp + cse.cpp cse_name_collision.cpp cse_nan.cpp cuda_8_bit_dot_product.cpp @@ -84,6 +89,7 @@ tests( deep_inline_chain.cpp deferred_loop_level.cpp deinterleave4.cpp + deinterleave_vector.cpp device_buffer_copies_with_profile.cpp device_buffer_copy.cpp device_copy_at_inner_loop.cpp @@ -101,6 +107,7 @@ tests( error_macro_unreachable.cpp exception.cpp explicit_inline_reductions.cpp + expr_match.cpp extern_bounds_inference.cpp extern_consumer.cpp extern_error.cpp @@ -132,6 +139,7 @@ tests( fuzz_schedule.cpp gameoflife.cpp gather.cpp + generator_internal.cpp gpu_alloc_group_profiling.cpp gpu_allocation_cache.cpp gpu_arg_types.cpp @@ -201,8 +209,11 @@ tests( intrinsics.cpp invalid_gpu_loop_nests.cpp inverse.cpp + ir_equality.cpp + ir_printer.cpp irmatch.cpp irprinter.cpp + is_monotonic.cpp isnan.cpp issue_3926.cpp iterate_over_circle.cpp @@ -232,6 +243,7 @@ tests( metal_precompiled_shaders.cpp min_extent.cpp mod.cpp + modulus_remainder.cpp multi_output_pipeline_with_bad_sizes.cpp multi_splits_with_diff_tail_strategies.cpp multi_way_select.cpp @@ -266,6 +278,7 @@ tests( print.cpp print_loop_nest.cpp process_some_tiles.cpp + propagate_estimate.cpp pseudostack_shares_slots.cpp python_extension_gen.cpp pytorch.cpp @@ -309,9 +322,11 @@ tests( specialize.cpp specialize_to_gpu.cpp specialize_trim_condition.cpp + spirv_ir.cpp split_by_non_factor.cpp split_factor_type.cpp split_fuse_rvar.cpp + split_predicate.cpp split_reuse_inner_name_bug.cpp split_store_compute.cpp stable_realization_order.cpp @@ -353,6 +368,7 @@ tests( uninitialized_read.cpp unique_func_image.cpp unique_name.cpp + uniquify_variable_names.cpp unroll_dynamic_loop.cpp unroll_huge_mux.cpp unroll_loop_with_implied_constant_bounds.cpp @@ -463,6 +479,17 @@ target_link_libraries(correctness_image_io PRIVATE Halide::ImageIO) # Make sure the test that needs Halide::ThreadPool has it target_link_libraries(correctness_gpu_allocation_cache PRIVATE Halide::ThreadPool) +# spirv_ir tests SpirvIR.h, which is an internal-only header (not part of the +# public Halide.h umbrella) used by the Vulkan backend, so it needs access to +# src/ and to the vendored SPIR-V headers, plus the WITH_SPIRV define that the +# Halide library itself is unconditionally built with. The SPIRV-Headers +# import target set up in src/CMakeLists.txt is directory-scoped there, so we +# need our own find_package call to see it here. +find_package(SPIRV-Headers 1.5.5 REQUIRED HINTS "${Halide_SOURCE_DIR}/dependencies/spirv") +target_include_directories(correctness_spirv_ir PRIVATE "${Halide_SOURCE_DIR}/src") +target_link_libraries(correctness_spirv_ir PRIVATE SPIRV-Headers::SPIRV-Headers) +target_compile_definitions(correctness_spirv_ir PRIVATE WITH_SPIRV) + # Tests which use external funcs need to enable exports. set_target_properties( # keep-sorted start diff --git a/test/correctness/associativity.cpp b/test/correctness/associativity.cpp new file mode 100644 index 000000000000..424070b59fda --- /dev/null +++ b/test/correctness/associativity.cpp @@ -0,0 +1,325 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +using std::map; +using std::string; +using std::vector; + +namespace { + +std::string print_args(const string &f, const vector &args, const vector &exprs) { + std::ostringstream stream; + stream << f << "("; + for (size_t i = 0; i < args.size(); ++i) { + stream << args[i]; + if (i != args.size() - 1) { + stream << ", "; + } + } + stream << ") = "; + + if (exprs.size() == 1) { + stream << exprs[0]; + } else if (exprs.size() > 1) { + stream << "Tuple("; + for (size_t i = 0; i < exprs.size(); ++i) { + stream << exprs[i]; + if (i != exprs.size() - 1) { + stream << ", "; + } + } + stream << ")"; + } + return stream.str(); +} + +void check_associativity(const string &f, const vector &args, const vector &exprs, + const AssociativeOp &assoc_op) { + auto result = prove_associativity(f, args, exprs); + internal_assert(result.associative() == assoc_op.associative()) + << "Checking associativity: " << print_args(f, args, exprs) << "\n" + << " Expect is associative: " << assoc_op.associative() << "\n" + << " instead of " << result.associative() << "\n"; + if (assoc_op.associative()) { + map replacement; + for (size_t i = 0; i < assoc_op.size(); ++i) { + internal_assert(equal(result.pattern.identities[i], assoc_op.pattern.identities[i])) + << "Checking associativity: " << print_args(f, args, exprs) << "\n" + << " Index: " << i << "\n" + << " Expect identity: " << assoc_op.pattern.identities[i] << "\n" + << " instead of " << result.pattern.identities[i] << "\n"; + internal_assert(equal(result.xs[i].expr, assoc_op.xs[i].expr)) + << "Checking associativity: " << print_args(f, args, exprs) << "\n" + << " Index: " << i << "\n" + << " Expect x: " << assoc_op.xs[i].expr << "\n" + << " instead of " << result.xs[i].expr << "\n"; + internal_assert(equal(result.ys[i].expr, assoc_op.ys[i].expr)) + << "Checking associativity: " << print_args(f, args, exprs) << "\n" + << " Index: " << i << "\n" + << " Expect y: " << assoc_op.ys[i].expr << "\n" + << " instead of " << result.ys[i].expr << "\n"; + + if (result.xs[i].expr.defined()) { + replacement.emplace(assoc_op.xs[i].var, Variable::make(result.xs[i].expr.type(), result.xs[i].var)); + } + if (result.ys[i].expr.defined()) { + replacement.emplace(assoc_op.ys[i].var, Variable::make(result.ys[i].expr.type(), result.ys[i].var)); + } + } + for (size_t i = 0; i < assoc_op.size(); ++i) { + Expr expected_op = substitute(replacement, assoc_op.pattern.ops[i]); + + internal_assert(equal(result.pattern.ops[i], expected_op)) + << "Checking associativity: " << print_args(f, args, exprs) << "\n" + << " Index: " << i << "\n" + << " Expect bin op: " << expected_op << "\n" + << " instead of " << result.pattern.ops[i] << "\n"; + + debug(5) << "\nExpected op: " << expected_op << "\n"; + debug(5) << "Operator: " << result.pattern.ops[i] << "\n"; + debug(5) << " identity: " << result.pattern.identities[i] << "\n"; + debug(5) << " x: " << result.xs[i].var << " -> " << result.xs[i].expr << "\n"; + debug(5) << " y: " << result.ys[i].var << " -> " << result.ys[i].expr << "\n"; + } + } +} + +} // namespace + +int main() { + typedef AssociativeOp::Replacement Replacement; + + { + // Tests for saturating addition + Type t = UInt(8); + Expr x = Variable::make(t, "x"); + Expr y = Variable::make(t, "y"); + Expr x_idx = Variable::make(Int(32), "x_idx"); + Expr f_call_0 = Call::make(t, "f", {x_idx}, Call::CallType::Halide, FunctionPtr(), 0); + + for (const Expr &e : {cast(min(cast(x) + y, 255)), + select(x > 255 - y, make_const(UInt(8), 255), x + y), + select(x < ~y, x + y, make_const(UInt(8), 255)), + saturating_add(x, y), + saturating_add(y, x), + saturating_cast(widening_add(x, y))}) { + check_associativity("f", {x_idx}, {substitute("x", f_call_0, e)}, + AssociativeOp( + AssociativePattern(solve_expression(e, "x").result, + make_const(t, 0), true), + {Replacement("x", f_call_0)}, + {Replacement("y", y)}, + true)); + } + } + + { + // Tests for logical And/Or + Type t = UInt(1); + Expr x = Variable::make(t, "x"); + Expr y = Variable::make(t, "y"); + Expr x_idx = Variable::make(Int(32), "x_idx"); + Expr f_call_0 = Call::make(t, "f", {x_idx}, Call::CallType::Halide, FunctionPtr(), 0); + + // f(x) = y && f(x) + check_associativity("f", {x_idx}, {And::make(y, f_call_0)}, + AssociativeOp( + AssociativePattern(And::make(x, y), const_true(), true), + {Replacement("x", f_call_0)}, + {Replacement("y", y)}, + true)); + + // f(x) = y || f(x) + check_associativity("f", {x_idx}, {Or::make(y, f_call_0)}, + AssociativeOp( + AssociativePattern(Or::make(x, y), const_false(), true), + {Replacement("x", f_call_0)}, + {Replacement("y", y)}, + true)); + } + + { + // Tests for 1D reduction + Type t = Int(32); + Expr x = Variable::make(t, "x"); + Expr y = Variable::make(t, "y"); + Expr z = Variable::make(t, "z"); + Expr rx = Variable::make(t, "rx"); + Expr f_call_0 = Call::make(t, "f", {x}, Call::CallType::Halide, FunctionPtr(), 0); + Expr g_call_0 = Call::make(t, "g", {rx}, Call::CallType::Halide, FunctionPtr(), 0); + + // f(x) = f(x) + check_associativity("f", {x}, {f_call_0}, + AssociativeOp( + AssociativePattern(x, make_const(t, 0), true), + {Replacement("x", f_call_0)}, + {Replacement("", Expr())}, + true)); + + // f(x) = min(f(x), y + int16(z)) + check_associativity("f", {x}, {min(f_call_0, y + Cast::make(Int(16), z))}, + AssociativeOp( + AssociativePattern(min(x, y), t.max(), true), + {Replacement("x", f_call_0)}, + {Replacement("y", y + Cast::make(Int(16), z))}, + true)); + + // f(x) = f(x) + g(rx) + y + z + check_associativity("f", {x}, {y + z + f_call_0}, + AssociativeOp( + AssociativePattern(x + y, make_const(t, 0), true), + {Replacement("x", f_call_0)}, + {Replacement("y", y + z)}, + true)); + + // f(x) = max(y, f(x)) + check_associativity("f", {x}, {max(y, f_call_0)}, + AssociativeOp( + AssociativePattern(max(x, y), t.min(), true), + {Replacement("x", f_call_0)}, + {Replacement("y", y)}, + true)); + + // f(x) = max(f(x) + g(rx), g(rx)) -> not associative + check_associativity("f", {x}, {max(f_call_0 + g_call_0, g_call_0)}, AssociativeOp()); + + // f(x) = max(f(x) + g(rx), f(x) - 3) -> f(x) + max(g(rx) - 3) + check_associativity("f", {x}, {max(f_call_0 + g_call_0, f_call_0 - 3)}, + AssociativeOp( + AssociativePattern(x + y, 0, true), + {Replacement("x", f_call_0)}, + {Replacement("y", max(g_call_0, -3))}, + true)); + + // f(x) = max(max(min(f(x), g(rx) + 2), f(x)), g(rx) + 2) -> can be simplified into max(f(x), g(rx) + 2) + check_associativity("f", {x}, {max(max(min(f_call_0, g_call_0 + 2), f_call_0), g_call_0 + 2)}, + AssociativeOp( + AssociativePattern(max(x, y), t.min(), true), + {Replacement("x", f_call_0)}, + {Replacement("y", g_call_0 + 2)}, + true)); + + // f(x) = max(x0, f(x)) -> x0 may conflict with the wildcard associative op pattern + Expr x0 = Variable::make(t, "x0"); + check_associativity("f", {x}, {max(x0, f_call_0)}, + AssociativeOp( + AssociativePattern(max(x, y), t.min(), true), + {Replacement("x", f_call_0)}, + {Replacement("y", x0)}, + true)); + } + + { + // Tests for multi-dimensional reduction (with mixed types) + Type t = Int(32); + Expr x = Variable::make(t, "x"); + Expr y = Variable::make(t, "y"); + Expr z = Variable::make(t, "z"); + Expr rx = Variable::make(t, "rx"); + + vector ts = {Int(32), Int(32), Float(32)}; + vector xs(3), ys(3), zs(3); + for (size_t i = 0; i < xs.size(); ++i) { + xs[i] = Variable::make(ts[i], "x" + std::to_string(i)); + ys[i] = Variable::make(ts[i], "y" + std::to_string(i)); + zs[i] = Variable::make(ts[i], "z" + std::to_string(i)); + } + + Expr f_call_0 = Call::make(ts[0], "f", {x}, Call::CallType::Halide, FunctionPtr(), 0); + Expr f_call_1 = Call::make(ts[1], "f", {x}, Call::CallType::Halide, FunctionPtr(), 1); + Expr f_call_2 = Call::make(ts[2], "f", {x}, Call::CallType::Halide, FunctionPtr(), 2); + Expr g_call_0 = Call::make(ts[0], "g", {rx}, Call::CallType::Halide, FunctionPtr(), 0); + Expr g_call_1 = Call::make(ts[1], "g", {rx}, Call::CallType::Halide, FunctionPtr(), 1); + + // f(x) = Tuple(f(x)[0], f(x)[2] + z) + check_associativity("f", {x}, {f_call_0, f_call_1 + cast(ts[1], z)}, + AssociativeOp( + AssociativePattern({xs[0], xs[1] + ys[1]}, + {make_const(ts[0], 0), make_const(ts[1], 0)}, + true), + {Replacement("x0", f_call_0), Replacement("x1", f_call_1)}, + {Replacement("", Expr()), Replacement("y1", cast(ts[1], z))}, + true)); + + // f(x) = Tuple(min(f(x)[0], g(rx)), f(x)[1]*g(x)*2, f(x)[2] + z) + check_associativity("f", {x}, {min(f_call_0, g_call_0), f_call_1 * g_call_0 * 2, f_call_2 + cast(ts[2], z)}, + AssociativeOp( + AssociativePattern( + {min(xs[0], ys[0]), xs[1] * ys[1], xs[2] + ys[2]}, + {ts[0].max(), make_const(ts[1], 1), make_const(ts[2], 0)}, + true), + {Replacement("x0", f_call_0), Replacement("x1", f_call_1), Replacement("x2", f_call_2)}, + {Replacement("y0", g_call_0), Replacement("y1", g_call_0 * 2), Replacement("y2", cast(ts[2], z))}, + true)); + + // Complex multiplication: f(x) = Tuple(f(x)[0]*g(r.x)[0] - f(x)[1]*g(r.x)[1], f(x)[0]*g(r.x)[1] + f(x)[1]*g(r.x)[0]) + check_associativity("f", {x}, {f_call_0 * g_call_0 - f_call_1 * g_call_1, f_call_0 * g_call_1 + f_call_1 * g_call_0}, + AssociativeOp( + AssociativePattern( + {xs[0] * ys[0] - ys[1] * xs[1], xs[1] * ys[0] + ys[1] * xs[0]}, + {make_const(ts[0], 1), make_const(ts[1], 0)}, + true), + {Replacement("x0", f_call_0), Replacement("x1", f_call_1)}, + {Replacement("y0", g_call_0), Replacement("y1", g_call_1)}, + true)); + + // 1D argmin: f(x) = Tuple(min(f(x)[0], g(r.x)[0]), select(f(x)[0] < g(r.x)[0], f(x)[1], g(r.x)[1]) + check_associativity("f", {x}, {min(f_call_0, g_call_0), select(f_call_0 < g_call_0, f_call_1, g_call_1)}, + AssociativeOp( + AssociativePattern( + {min(xs[0], ys[0]), select(xs[0] < ys[0], xs[1], ys[1])}, + {ts[0].max(), make_const(ts[1], 0)}, + true), + {Replacement("x0", f_call_0), Replacement("x1", f_call_1)}, + {Replacement("y0", g_call_0), Replacement("y1", g_call_1)}, + true)); + } + + { + Type t = Int(32); + Expr x = Variable::make(t, "x"); + Expr y = Variable::make(t, "y"); + Expr rx = Variable::make(t, "rx"); + Expr ry = Variable::make(t, "ry"); + + vector ts = {UInt(8), Int(32), Int(16), Float(32)}; + vector xs(4), ys(4), zs(4); + for (size_t i = 0; i < xs.size(); ++i) { + xs[i] = Variable::make(ts[i], "x" + std::to_string(i)); + ys[i] = Variable::make(ts[i], "y" + std::to_string(i)); + zs[i] = Variable::make(ts[i], "z" + std::to_string(i)); + } + + Expr f_xy_call_0 = Call::make(ts[0], "f", {x, y}, Call::CallType::Halide, FunctionPtr(), 0); + Expr f_xy_call_1 = Call::make(ts[1], "f", {x, y}, Call::CallType::Halide, FunctionPtr(), 1); + Expr f_xy_call_2 = Call::make(ts[2], "f", {x, y}, Call::CallType::Halide, FunctionPtr(), 2); + Expr f_xy_call_3 = Call::make(ts[3], "f", {x, y}, Call::CallType::Halide, FunctionPtr(), 3); + Expr g_xy_call_0 = Call::make(ts[0], "g", {rx, ry}, Call::CallType::Halide, FunctionPtr(), 0); + + // 2D argmin + sum + // f(x, y) = Tuple(min(f(x, y)[0], g(r.x, r.y)[0]), + // f(x, y)[1] + r.x, + // select(f(x, y)[0] < g(r.x, r.y)[0], f(x)[2], r.x), + // select(f(x, y)[0] < g(r.x, r.y)[0], f(x)[3], r.y)) + check_associativity("f", {x, y}, + {min(f_xy_call_0, g_xy_call_0), + f_xy_call_1 + rx, + select(f_xy_call_0 < g_xy_call_0, f_xy_call_2, cast(Int(16), rx)), + select(f_xy_call_0 < g_xy_call_0, f_xy_call_3, cast(Float(32), ry))}, + AssociativeOp( + AssociativePattern( + {min(xs[0], ys[0]), xs[1] + ys[1], select(xs[0] < ys[0], xs[2], ys[2]), select(xs[0] < ys[0], xs[3], ys[3])}, + {ts[0].max(), make_const(ts[1], 0), make_const(ts[2], 0), make_const(ts[3], 0)}, + true), + {Replacement("x0", f_xy_call_0), Replacement("x1", f_xy_call_1), + Replacement("x2", f_xy_call_2), Replacement("x3", f_xy_call_3)}, + {Replacement("y0", g_xy_call_0), Replacement("y1", rx), + Replacement("y2", cast(Int(16), rx)), Replacement("y3", cast(Float(32), ry))}, + true)); + } + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/bounds_internal.cpp b/test/correctness/bounds_internal.cpp new file mode 100644 index 000000000000..81b0667ae66c --- /dev/null +++ b/test/correctness/bounds_internal.cpp @@ -0,0 +1,540 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +using std::map; +using std::string; +using std::vector; + +namespace { + +// Bounds.cpp has a private overload of simplify() for Interval that isn't +// part of the public API; reimplement the small piece these tests need. +Interval simplify(const Interval &i) { + Interval result; + result.min = simplify(i.min); + if (i.is_single_point()) { + result.max = result.min; + } else { + result.max = simplify(i.max); + } + return result; +} + +void check(const Scope &scope, const Expr &e, const Expr &correct_min, const Expr &correct_max) { + FuncValueBounds fb; + Interval result = bounds_of_expr_in_scope(e, scope, fb); + result = simplify(result); + if (!equal(result.min, correct_min)) { + internal_error << "In bounds of " << e << ":\n" + << "Incorrect min: " << result.min << "\n" + << "Should have been: " << correct_min << "\n"; + } + if (!equal(result.max, correct_max)) { + internal_error << "In bounds of " << e << ":\n" + << "Incorrect max: " << result.max << "\n" + << "Should have been: " << correct_max << "\n"; + } +} + +void check_constant_bound(const Scope &scope, const Expr &e, const Expr &correct_min, const Expr &correct_max) { + FuncValueBounds fb; + Interval result = bounds_of_expr_in_scope(e, scope, fb, true); + result = simplify(result); + if (!equal(result.min, correct_min)) { + internal_error << "In find constant bound of " << e << ":\n" + << "Incorrect min constant bound: " << result.min << "\n" + << "Should have been: " << correct_min << "\n"; + } + if (!equal(result.max, correct_max)) { + internal_error << "In find constant bound of " << e << ":\n" + << "Incorrect max constant bound: " << result.max << "\n" + << "Should have been: " << correct_max << "\n"; + } +} + +void check_constant_bound(const Expr &e, const Expr &correct_min, const Expr &correct_max) { + Scope scope; + check_constant_bound(scope, e, correct_min, correct_max); +} + +void constant_bound_test() { + using namespace ConciseCasts; + + { + Param a; + Param b; + check_constant_bound(a >> b, i16(-32768), i16(32767)); + } + + { + Param x("x"), y("y"); + x.set_range(10, 20); + y.set_range(5, 30); + check_constant_bound(clamp(x, 5, 30), 10, 20); + check_constant_bound(clamp(x, 15, 30), 15, 20); + check_constant_bound(clamp(x, 15, 17), 15, 17); + check_constant_bound(clamp(x, 5, 15), 10, 15); + + check_constant_bound(x + y, 15, 50); + check_constant_bound(x - y, -20, 15); + check_constant_bound(x * y, 50, 600); + check_constant_bound(x / y, 0, 4); + + check_constant_bound(select(x > 4, 3 * x - y / 2, max(x + y + 2, x - 20)), 15, 58); + check_constant_bound(select(x < 4, 3 * x - y / 2, max(x + y + 2, x - 20)), 17, 52); + check_constant_bound(select(x >= 11, 3 * x - y / 2, max(x + y + 2, x - 20)), 15, 58); + } + + { + Param x("x"), y("y"); + x.set_range(Expr((uint8_t)10), Expr((uint8_t)20)); + y.set_range(Expr((uint8_t)5), Expr((uint8_t)30)); + check_constant_bound(clamp(x, 5, 30), Expr((uint8_t)10), Expr((uint8_t)20)); + check_constant_bound(clamp(x, 15, 30), Expr((uint8_t)15), Expr((uint8_t)20)); + check_constant_bound(clamp(x, 15, 17), Expr((uint8_t)15), Expr((uint8_t)17)); + check_constant_bound(clamp(x, 5, 15), Expr((uint8_t)10), Expr((uint8_t)15)); + + check_constant_bound(x + y, Expr((uint8_t)15), Expr((uint8_t)50)); + check_constant_bound(x / y, Expr((uint8_t)0), Expr((uint8_t)4)); + + check_constant_bound(select(x > 4, 3 * x - y / 2, max(x + y + 2, x + 20)), + Expr((uint8_t)15), Expr((uint8_t)58)); + check_constant_bound(select(x < 4, 3 * x - y / 2, max(x + y + 2, x + 20)), + Expr((uint8_t)30), Expr((uint8_t)52)); + check_constant_bound(select(x >= 11, 3 * x - y / 2, max(x + y + 2, x + 20)), + Expr((uint8_t)15), Expr((uint8_t)58)); + + // These two overflow + check_constant_bound(x - y, Expr((uint8_t)0), Expr((uint8_t)255)); + check_constant_bound(x * y, Expr((uint8_t)0), Expr((uint8_t)255)); + + check_constant_bound(absd(x, y), Expr((uint8_t)0), Expr((uint8_t)20)); + check_constant_bound(absd(cast(x), cast(y)), Expr((uint16_t)0), Expr((uint16_t)20)); + } + + { + Param x("x"), y("y"); + x.set_range(Expr((float)10), Expr((float)20)); + y.set_range(Expr((float)5), Expr((float)30)); + + check_constant_bound(absd(x, y), Expr((float)0), Expr((float)20)); + } + + { + Param i("i"), x("x"), y("y"), d("d"); + Expr cl = i16(i); + Expr cr1 = i16(x); + Expr cr2 = i16(y); + Expr fraction = (d & (int16_t)((1 << 7) - 1)); + Expr cr = i16((((cr2 - cr1) * fraction) >> 7) + cr1); + + check_constant_bound(absd(cr, cl), Expr((uint16_t)0), Expr((uint16_t)509)); + check_constant_bound(i16(absd(cr, cl)), Expr((int16_t)0), Expr((int16_t)509)); + } + + check_constant_bound(Load::make(Int(32), "buf", 0, Buffer<>(), Parameter(), const_true(), ModulusRemainder()) * 20, + Interval::neg_inf(), Interval::pos_inf()); + + { + // Ensure that unnecessary integer overflow doesn't happen + // in cases involving unsigned integer math + Param e1("e1"); // range 0..0xffff, type=uint16 + Expr e2 = cast(e1); // range 0..0xffff, type=uint32 + Expr e3 = e2 * e2; // range 0..0xfffe0001, type=uint32 + check_constant_bound(e3, Expr((uint32_t)0), Expr((uint32_t)0xfffe0001)); + } + + { + RDom r(0, 4); + + // bounds of an expression with impure >= 32 bit expr will be unbounded + Expr e32 = sum(cast(r.x)); + check_constant_bound(e32, Interval::neg_inf(), Interval::pos_inf()); + + // bounds of an expression with impure < 32 bit expr will be bounds-of-type + Expr e16 = sum(cast(r.x)); + check_constant_bound(e16, Int(16).min(), Int(16).max()); + } + + { + Param x("x"), y("y"); + x.set_range(2, 10); + + check_constant_bound(count_leading_zeros(x), i32(28), i32(30)); + check_constant_bound(count_leading_zeros(cast(x)), i16(12), i16(14)); + + check_constant_bound(count_leading_zeros(y), i32(0), i32(32)); + check_constant_bound(count_leading_zeros(cast(y)), i16(0), i16(16)); + } +} + +void boxes_touched_test() { + Type t = Int(32); + Expr x = Variable::make(t, "x"); + Expr y = Variable::make(t, "y"); + Expr z = Variable::make(t, "z"); + Expr w = Variable::make(t, "w"); + + Scope scope; + scope.push("y", Interval(Expr(0), Expr(10))); + + Stmt stmt = Provide::make("f", {10}, {x, y, z, w}, const_true()); + stmt = IfThenElse::make(y > 4, stmt, Stmt()); + stmt = IfThenElse::make(z > 18, stmt, Stmt()); + stmt = LetStmt::make("w", z + 3, stmt); + stmt = LetStmt::make("z", x + 2, stmt); + stmt = LetStmt::make("x", y + 10, stmt); + + Box expected({Interval(15, 20), Interval(5, 10), Interval(19, 22), Interval(22, 25)}); + Box result = box_provided(stmt, "f", scope); + internal_assert(expected.size() == result.size()) + << "Expect dim size of " << expected.size() + << ", got " << result.size() << " instead\n"; + for (size_t i = 0; i < result.size(); ++i) { + const Interval &correct = expected[i]; + Interval b = result[i]; + b = simplify(b); + if (!equal(correct.min, b.min)) { + internal_error << "In bounds of dim " << i << ":\n" + << "Incorrect min: " << b.min << "\n" + << "Should have been: " << correct.min << "\n"; + } + if (!equal(correct.max, b.max)) { + internal_error << "In bounds of dim " << i << ":\n" + << "Incorrect max: " << b.max << "\n" + << "Should have been: " << correct.max << "\n"; + } + } +} + +} // namespace + +int main() { + using namespace Halide::ConciseCasts; + + constant_bound_test(); + + Scope scope; + Var x("x"), y("y"); + scope.push("x", Interval(Expr(0), Expr(10))); + + check(scope, x, 0, 10); + check(scope, x + 1, 1, 11); + check(scope, (x + 1) * 2, 2, 22); + check(scope, x * x, 0, 100); + check(scope, 5 - x, -5, 5); + check(scope, x * (5 - x), -50, 50); // We don't expect bounds analysis to understand correlated terms + check(scope, Select::make(x < 4, x, x + 100), 0, 110); + check(scope, x + y, y, y + 10); + check(scope, x * y, min(y, 0) * 10, max(y, 0) * 10); + check(scope, x / (x + y), -10, 10); + check(scope, 11 / (x + 1), 1, 11); + check(scope, Load::make(Int(8), "buf", x, Buffer<>(), Parameter(), const_true(), ModulusRemainder()), + i8(-128), i8(127)); + check(scope, y + (Let::make("y", x + 3, y - x + 10)), y + 3, y + 23); // Once again, we don't know that y is correlated with x + check(scope, clamp(1000 / (x - 2), x - 10, x + 10), -10, 20); + check(scope, cast(x / 2), u16(0), u16(5)); + check(scope, cast((x + 10) / 2), u16(5), u16(10)); + check(scope, x < 20, make_bool(true), make_bool(true)); + check(scope, x < 5, make_bool(false), make_bool(true)); + check(scope, Broadcast::make(x >= 11, 3), make_bool(false), make_bool(false)); + check(scope, Ramp::make(x + 5, 1, 5) > Broadcast::make(2, 5), make_bool(true), make_bool(true)); + + check(scope, print(x, y), 0, 10); + check(scope, print_when(x > y, x, y), 0, 10); + + check(scope, select(y == 5, 0, 3), select(y == 5, 0, 3), select(y == 5, 0, 3)); + check(scope, select(y == 5, x, -3 * x + 8), select(y == 5, 0, -22), select(y == 5, 10, 8)); + check(scope, select(y == x, x, -3 * x + 8), -22, select(y <= 10 && 0 <= y, 10, 8)); + + check(scope, cast(abs(cast(x ^ y))), 0, 32768); + check(scope, cast(x), 0.0f, 10.0f); + + check(scope, cast(abs(cast(x))), 0, 10); + check(scope, abs(2 + x), u32(2), u32(12)); + check(scope, abs(x - 11), u32(1), u32(11)); + check(scope, abs(x - 5), u32(0), u32(5)); + check(scope, abs(2 + cast(x)), 2.f, 12.f); + check(scope, abs(cast(x) - 11), 1.f, 11.f); + check(scope, abs(cast(x) - 5), 0.f, 5.f); + check(scope, abs(2 + cast(x)), u8(2), u8(12)); + check(scope, abs(cast(x) - 11), u8(1), u8(11)); + check(scope, abs(cast(x) - 5), u8(0), u8(5)); + scope.push("x", Interval(123, Interval::pos_inf())); + check(scope, abs(x), u32(123), Interval::pos_inf()); + scope.pop("x"); + scope.push("x", Interval(Interval::neg_inf(), -123)); + check(scope, abs(x), u32(123), Interval::pos_inf()); + scope.pop("x"); + + // Check some vectors + check(scope, Ramp::make(x * 2, 5, 5), 0, 40); + check(scope, Broadcast::make(x * 2, 5), 0, 20); + check(scope, Broadcast::make(3, 4), 3, 3); + + // Check some operations that may overflow + check(scope, (cast(x) + 250), u8(0), u8(255)); + check(scope, (cast(x) + 10) * 20, u8(0), u8(255)); + check(scope, (cast(x) + 10) * (cast(x) + 5), u8(0), u8(255)); + check(scope, (cast(x) + 10) - (cast(x) + 5), u8(0), u8(255)); + + // Check some operations that we should be able to prove do not overflow + check(scope, (cast(x) + 240), u8(240), u8(250)); + check(scope, (cast(x) + 10) * 10, u8(100), u8(200)); + check(scope, (cast(x) + 10) * (cast(x)), u8(0), u8(200)); + check(scope, (cast(x) + 20) - (cast(x) + 5), u8(5), u8(25)); + + // Check div/mod by unbounded unknowns. div and mod can only ever + // make things smaller in magnitude. + scope.push("x", Interval::everything()); + check(scope, -3 / x, -3, 3); + check(scope, 3 / x, -3, 3); + check(scope, y / x, -cast(abs(y)), cast(abs(y))); + check(scope, -3 % x, 0, Interval::pos_inf()); + check(scope, 3 % x, 0, 3); + // Mod can't make values negative + check(scope, y % x, 0, Interval::pos_inf()); + // Mod can't make positive values larger + check(scope, max(y, 0) % x, 0, max(y, 0)); + scope.pop("x"); + + // Check some bitwise ops. + check(scope, (cast(x) & make_const(UInt(8), 7)), u8(0), u8(7)); + check(scope, (make_const(UInt(8), 3) & make_const(UInt(8), 2)), u8(2), u8(2)); + check(scope, (make_one(UInt(8)) | make_const(UInt(8), 2)), u8(3), u8(3)); + check(scope, (make_const(UInt(8), 3) ^ make_const(UInt(8), 2)), u8(1), u8(1)); + check(scope, (~make_const(UInt(8), 3)), u8(0xfc), u8(0xfc)); + check(scope, cast(x + 5) & cast(x + 3), u8(0), u8(13)); + check(scope, cast(x - 5) & cast(x + 3), i8(0), i8(13)); + check(scope, cast(2 * x - 5) & cast(x - 3), i8(-128), i8(15)); + check(scope, cast(x + 5) | cast(x + 3), u8(5), u8(255)); + check(scope, cast(x + 5) | cast(x + 3), i8(3), i8(127)); + check(scope, ~cast(x), u8(-11), u8(-1)); + check(scope, (cast(x) >> make_one(UInt(8))), u8(0), u8(5)); + check(scope, (make_const(UInt(8), 10) >> make_one(UInt(8))), u8(5), u8(5)); + check(scope, (cast(x + 3) << make_one(UInt(8))), u8(6), u8(26)); + check(scope, (cast(x + 3) << make_const(UInt(8), 7)), u8(0), u8(255)); // Overflows + check(scope, (make_const(UInt(8), 5) << make_one(UInt(8))), u8(10), u8(10)); + check(scope, (x << 12), 0, 10 << 12); + check(scope, x & 4095, 0, 10); // LHS known to be positive + check(scope, x & 123, 0, 10); // Doesn't have to be a precise bitmask + check(scope, (x - 1) & 4095, 0, 4095); // LHS could be -1 + + // Regression tests on shifts (produced by z3). + { + ScopedBinding xb(scope, "x", Interval(-123, Interval::pos_inf())); + ScopedBinding yb(scope, "y", Interval(-6, 0)); + // -123 << 0 = -123 + check(scope, x << y, -123, Interval::pos_inf()); + } + { + ScopedBinding xb(scope, "x", Interval(-123, Interval::pos_inf())); + ScopedBinding yb(scope, "y", Interval(-6, Interval::pos_inf())); + // A negative value can increase in magnitude if the rhs is positive. + check(scope, x << y, Interval::neg_inf(), Interval::pos_inf()); + } + { + ScopedBinding xb(scope, "x", Interval(-123, Interval::pos_inf())); + Var c("c"); + ScopedBinding yb(scope, "y", Interval(-6, c)); + // Can't prove anything about the upper bound of y. + check(scope, x << y, min((-123) << c, -123), Interval::pos_inf()); + } + { + ScopedBinding xb(scope, "x", Interval(-123, Interval::pos_inf())); + ScopedBinding yb(scope, "y", Interval(-6, 4)); + // -123 << 4 = -1968 + check(scope, x << y, -1968, Interval::pos_inf()); + } + { + ScopedBinding xb(scope, "x", Interval(24, Interval::pos_inf())); + ScopedBinding yb(scope, "y", Interval(Interval::neg_inf(), -1)); + // Cannot change sign, only can decrease magnitude. + check(scope, x << y, 0, Interval::pos_inf()); + } + // Overflow testing (for types with defined overflow). + { + Type uint32 = UInt(32); + Expr a = Variable::make(uint32, "a"); + Expr b = Variable::make(uint32, "b"); + ScopedBinding ab(scope, "a", Interval(UIntImm::make(uint32, 0), simplify(uint32.max() / 4 + 2))); + ScopedBinding bb(scope, "b", Interval(UIntImm::make(uint32, 0), uint32.max())); + // Overflow should be detected + check(scope, a + b, Interval::neg_inf(), Interval::pos_inf()); + check(scope, a * b, Interval::neg_inf(), Interval::pos_inf()); + } + { + Type int16 = Int(16); + Expr a = Variable::make(int16, "a"); + Expr b = Variable::make(int16, "b"); + ScopedBinding ab(scope, "a", Interval(int16.min(), int16.max())); + ScopedBinding bb(scope, "b", Interval(IntImm::make(int16, -4), IntImm::make(int16, -1))); + check(scope, a * -1, int16.min(), int16.max()); + // int16.min() / -1 should be caught as overflow. + check(scope, a / -1, int16.min(), int16.max()); + check(scope, a / b, int16.min(), int16.max()); + } + { + Expr zero = UIntImm::make(UInt(1), 0); + Expr one = UIntImm::make(UInt(1), 1); + check(scope, Ramp::make(zero, one, 3), zero, one); + } + + // If we clamp something unbounded as one type, the bounds should + // propagate through casts whenever the cast can be proved to not + // overflow. + check(scope, + cast(clamp(cast(x ^ y), 0.0f, 4095.0f)), + u16(0), u16(4095)); + + check(scope, + cast(clamp(cast(x ^ y), make_zero(UInt(16)), make_const(UInt(16), 128))), + u8(0), u8(128)); + + Expr u8_1 = cast(Load::make(Int(8), "buf", x, Buffer<>(), Parameter(), const_true(), ModulusRemainder())); + Expr u8_2 = cast(Load::make(Int(8), "buf", x + 17, Buffer<>(), Parameter(), const_true(), ModulusRemainder())); + check(scope, cast(u8_1) + cast(u8_2), + u16(0), u16(255 * 2)); + + check(scope, saturating_cast(clamp(x, 5, 10)), make_const(UInt(8), 5), make_const(UInt(8), 10)); + { + scope.push("x", Interval(UInt(32).min(), UInt(32).max())); + check(scope, saturating_cast(max(cast(x), make_const(UInt(32), 5))), make_const(Int(32), 5), Int(32).max()); + scope.pop("x"); + } + { + Expr z = Variable::make(Float(32), "z"); + scope.push("z", Interval(make_const(Float(32), -1), make_one(Float(32)))); + check(scope, saturating_cast(z), make_const(Int(32), -1), make_one(Int(32))); + check(scope, saturating_cast(z), make_const(Float(64), -1), make_one(Float(64))); + check(scope, saturating_cast(z), make_const(Float(16), -1), make_one(Float(16))); + check(scope, saturating_cast(z), make_zero(UInt(8)), make_one(UInt(8))); + scope.pop("z"); + } + { + Expr z = Variable::make(UInt(32), "z"); + scope.push("z", Interval(UInt(32).max(), UInt(32).max())); + check(scope, saturating_cast(z), Int(32).max(), Int(32).max()); + scope.pop("z"); + } + + { + Scope scope; + Expr x = Variable::make(UInt(16), "x"); + Expr y = Variable::make(UInt(16), "y"); + scope.push("x", Interval(u16(0), u16(10))); + scope.push("y", Interval(u16(2), u16(4))); + + Expr e = clamp(x / y, u16(0), u16(128)); + check(scope, e, u16(0), u16(5)); + check_constant_bound(scope, e, u16(0), u16(5)); + } + + { + Param x("x"); + Param y("y"); + x.set_range(i16(-32), i16(-16)); + y.set_range(i16(0), i16(4)); + check_constant_bound((x >> y), i16(-32), i16(-1)); + } + + { + Param x("x"), y("y"); + x.set_range(u16(10), u16(20)); + y.set_range(u16(0), u16(30)); + Scope scope; + scope.push("y", Interval(u16(2), u16(4))); + + check_constant_bound(scope, x + y, u16(12), u16(24)); + } + + { + Scope scope; + Interval i = Interval::everything(); + i.min = 17; + internal_assert(i.has_lower_bound()); + internal_assert(!i.has_upper_bound()); + scope.push("y", i); + Var x("x"), y("y"); + check(scope, select(x == y * 2, y, y - 10), + 7, Interval::pos_inf()); + check(scope, select(x == y * 2, y - 10, y), + select(x < 34, 17, 7), Interval::pos_inf()); + } + + vector input_site_1 = {2 * x}; + vector input_site_2 = {2 * x + 1}; + vector output_site = {x + 1}; + + Buffer in(10); + in.set_name("input"); + + Stmt loop = For::make("x", 3, 12, ForType::Serial, Partition::Auto, DeviceAPI::Host, + Provide::make("output", + {Add::make(Call::make(in, input_site_1), + Call::make(in, input_site_2))}, + output_site, + const_true())); + + map r; + r = boxes_required(loop); + internal_assert(r.find("output") == r.end()); + internal_assert(r.find("input") != r.end()); + internal_assert(equal(simplify(r["input"][0].min), 6)); + internal_assert(equal(simplify(r["input"][0].max), 25)); + r = boxes_provided(loop); + internal_assert(r.find("output") != r.end()); + internal_assert(equal(simplify(r["output"][0].min), 4)); + internal_assert(equal(simplify(r["output"][0].max), 13)); + + Box r2({Interval(Expr(5), Expr(19))}); + merge_boxes(r2, r["output"]); + internal_assert(equal(simplify(r2[0].min), 4)); + internal_assert(equal(simplify(r2[0].max), 19)); + + boxes_touched_test(); + + // Check a deeply-nested bitwise expr to ensure it doesn't take n^2 time + // (this clause took ~30s on a typical laptop before the fix, ~10ms after) + { + Expr a = Variable::make(UInt(16), "t42"); + Expr b = Variable::make(UInt(16), "t43"); + Expr c = Variable::make(UInt(16), "t44"); + Expr d = Variable::make(Int(32), "d"); + Expr x = Variable::make(Int(32), "x"); + Expr y = Variable::make(Int(32), "y"); + Expr e1 = select(c >= Expr((uint16_t)128), c - Expr((uint16_t)128), c); + Expr e2 = Let::make("t44", (((((((((((((((((u16(0) << u16(1)) | u16((u8(d) & u8(1)))) << u16(1)) | u16(((u8(d) >> u8(1)) & u8(1)))) << u16(1)) | (u16(x) & u16(1))) << u16(1)) | (u16(y) & u16(1))) << u16(1)) | (a & u16(1))) << u16(1)) | (b & u16(1))) << u16(1)) | ((a >> u16(1)) & u16(1))) << u16(1)) | ((b >> u16(1)) & u16(1))) >> u16(1)), e1); + Expr e3 = Let::make("t43", u16(y) >> u16(1), e2); + Expr e4 = Let::make("t42", u16(x) >> u16(1), e3); + + check_constant_bound(e4, u16(0), u16(65535)); + } + + // Test case from https://github.com/halide/Halide/pull/7377 + { + Var x; + Expr e = Load::make(Int(32), "buf", max(x, -x), Buffer<>{}, Parameter{}, const_true(), ModulusRemainder{}); + e = Let::make(x.name(), 37, e); + Scope scope; + scope.push("y", {0, 100}); + Interval in = bounds_of_expr_in_scope(e, scope); + internal_assert(in.is_single_point()); + } + + // Test case from https://github.com/halide/Halide/pull/7379 + { + Var x; + Expr e = Load::make(Int(32), "buf", -x / x, Buffer<>{}, Parameter{}, const_true(), ModulusRemainder{}); + e = Let::make(x.name(), 37, e); + Scope scope; + scope.push("y", {0, 100}); + Interval in = bounds_of_expr_in_scope(e, scope); + internal_assert(in.is_single_point()); + } + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/codegen_c.cpp b/test/correctness/codegen_c.cpp new file mode 100644 index 000000000000..481f4ea6b9fc --- /dev/null +++ b/test/correctness/codegen_c.cpp @@ -0,0 +1,237 @@ +#include "Halide.h" + +#include +#include + +using namespace Halide; +using namespace Halide::Internal; + +namespace { + +// On Windows, raw string literals and binary2cpp-generated arrays can +// contain \r\n line endings (from git checkout with core.autocrlf). +// Normalize to \n so that generated source files have consistent, +// platform-independent line endings. +std::string normalize_line_endings(const char *s) { + std::string result; + result.reserve(strlen(s)); + for (; *s; ++s) { + if (*s != '\r') { + result += *s; + } + } + return result; +} + +// HALIDE_MUST_USE_RESULT defined here is intended to exactly +// duplicate the definition in HalideRuntime.h (so that either or +// both can be present, in any order). +const std::string kDefineMustUseResult = normalize_line_endings(R"INLINE_CODE(#ifndef HALIDE_MUST_USE_RESULT +#ifdef __has_attribute +#if __has_attribute(nodiscard) +#define HALIDE_MUST_USE_RESULT [[nodiscard]] +#elif __has_attribute(warn_unused_result) +#define HALIDE_MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define HALIDE_MUST_USE_RESULT +#endif +#else +#define HALIDE_MUST_USE_RESULT +#endif +#endif +)INLINE_CODE"); + +} // namespace + +int main(int argc, char **argv) { + LoweredArgument buffer_arg("buf", Argument::OutputBuffer, Int(32), 3, ArgumentEstimates{}); + LoweredArgument float_arg("alpha", Argument::InputScalar, Float(32), 0, ArgumentEstimates{}); + LoweredArgument int_arg("beta", Argument::InputScalar, Int(32), 0, ArgumentEstimates{}); + LoweredArgument user_context_arg("__user_context", Argument::InputScalar, type_of(), 0, ArgumentEstimates{}); + std::vector args = {buffer_arg, float_arg, int_arg, user_context_arg}; + Var x("x"); + Param alpha("alpha"); + Param beta("beta"); + Expr e = Select::make(alpha > 4.0f, print_when(x < 1, 3), 2); + Stmt s = Store::make("buf", e, x, Parameter(), const_true(), ModulusRemainder()); + s = LetStmt::make("x", beta + 1, s); + s = Block::make(s, Free::make("tmp.stack")); + s = Allocate::make("tmp.stack", Int(32), MemoryType::Stack, {127}, const_true(), s); + s = Allocate::make("tmp.heap", Int(32), MemoryType::Heap, {43, beta}, const_true(), s); + Expr buf = Variable::make(Handle(), "buf.buffer"); + s = LetStmt::make("buf", Call::make(Handle(), Call::buffer_get_host, {buf}, Call::Extern), s); + + Module m("", get_host_target()); + m.append(LoweredFunc("test1", args, s, LinkageType::External)); + + std::ostringstream source; + { + CodeGen_C cg(source, Target("host"), CodeGen_C::CImplementation); + cg.compile(m); + } + + std::string correct_source = + codegen_c_test_prologue_source() + '\n' + + codegen_c_test_runtime_header_source() + '\n' + + codegen_c_test_inlined_c_source() + '\n' + + '\n' + kDefineMustUseResult + normalize_line_endings(R"GOLDEN_CODE( +#ifndef HALIDE_FUNCTION_ATTRS +#define HALIDE_FUNCTION_ATTRS +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + +HALIDE_FUNCTION_ATTRS +int test1(struct halide_buffer_t *_buf_buffer, float _alpha, int32_t _beta, void const *__user_context) { + void * const _ucon = const_cast(__user_context); + halide_maybe_unused(_ucon); + auto *_0 = _halide_buffer_get_host(_buf_buffer); + auto _buf = _0; + halide_maybe_unused(_buf); + { + int64_t _1 = 43; + int64_t _2 = _1 * _beta; + if ((_2 > ((int64_t(1) << 31) - 1)) || ((_2 * sizeof(int32_t )) > ((int64_t(1) << 31) - 1))) + { + halide_error(_ucon, "32-bit signed overflow computing size of allocation tmp.heap\n"); + return -1; + } // overflow test tmp.heap + int64_t _3 = _2; + int32_t *_tmp_heap = (int32_t *)halide_malloc(_ucon, sizeof(int32_t )*_3); + if (!((_tmp_heap != nullptr) || (_3 == 0))) + { + int32_t _4 = halide_error_out_of_memory(_ucon); + return _4; + } + HalideFreeHelper _tmp_heap_free(_ucon, _tmp_heap); + { + int32_t _tmp_stack[127]; + int32_t _5 = _beta + 1; + int32_t _6; + bool _7 = _5 < 1; + if (_7) + { + char b0[1024]; + snprintf(b0, 1024, "%lld%s", (long long)(3), "\n"); + auto *_8 = b0; + halide_print(_ucon, _8); + int32_t _9 = 0; + int32_t _10 = return_second(_9, 3); + _6 = _10; + } // if _7 + else + { + _6 = 3; + } // if _7 else + int32_t _11 = _6; + float _12 = float_from_bits(1082130432 /* 4 */); + bool _13 = _alpha > _12; + int32_t _14 = (int32_t)(_13 ? _11 : 2); + ((int32_t *)_buf)[_5] = _14; + } // alloc _tmp_stack + _tmp_heap_free.free(); + } // alloc _tmp_heap + return 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +)GOLDEN_CODE"); + + const auto compare_srcs = [](const std::string &actual, const std::string &expected) { + if (actual != expected) { + int diff = 0; + while (actual[diff] == expected[diff]) { + diff++; + } + int diff_end = diff + 1; + while (diff > 0 && actual[diff] != '\n') { + diff--; + } + while (diff_end < (int)actual.size() && actual[diff_end] != '\n') { + diff_end++; + } + + internal_error + << "Correct source code:\n" + << expected + << "Actual source code:\n" + << actual + << "Difference starts at:\n" + << "Correct: " << expected.substr(diff, diff_end - diff) << "\n" + << "Actual: " << actual.substr(diff, diff_end - diff) << "\n"; + } + }; + + compare_srcs(source.str(), correct_source); + + std::ostringstream function_info; + { + CodeGen_C cg(function_info, Target("host-no_runtime"), CodeGen_C::CPlusPlusFunctionInfoHeader, "Function/Info/Test"); + cg.compile(m); + } + + std::string correct_function_info = normalize_line_endings(R"GOLDEN_CODE(#ifndef HALIDE_FUNCTION_INFO__Function___Info___Test +#define HALIDE_FUNCTION_INFO__Function___Info___Test + +/* MACHINE GENERATED By Halide. */ + +#if !(__cplusplus >= 201703L || _MSVC_LANG >= 201703L) +#error "This file requires C++17 or later; please upgrade your compiler." +#endif + +#include "HalideRuntime.h" + + +/** + * This function returns a constexpr array of information about a Halide-generated + * function's argument signature (e.g., number of arguments, type of each, etc). + * While this is a subset of the information provided by the existing _metadata + * function, it has the distinct advantage of allowing one to use the information + * it at compile time (rather than runtime). This can be quite useful for producing + * e.g. automatic call wrappers, etc. + * + * For instance, to compute the number of Buffers in a Function, one could do something + * like: + * + * using namespace HalideFunctionInfo; + * + * template + * constexpr size_t count_buffers(const std::array args) { + * size_t buffer_count = 0; + * for (const auto a : args) { + * if (a.kind == InputBuffer || a.kind == OutputBuffer) { + * buffer_count++; + * } + * } + * return buffer_count; + * } + * + * constexpr size_t count = count_buffers(metadata_tester_argument_info()); + * + * The value of `count` will be computed entirely at compile-time, with no runtime + * impact aside from the numerical value of the constant. + */ + +inline constexpr std::array<::HalideFunctionInfo::ArgumentInfo, 4> test1_argument_info() { + return {{ + {"buf", ::HalideFunctionInfo::OutputBuffer, 3, halide_type_t{halide_type_int, 32}}, + {"alpha", ::HalideFunctionInfo::InputScalar, 0, halide_type_t{halide_type_float, 32}}, + {"beta", ::HalideFunctionInfo::InputScalar, 0, halide_type_t{halide_type_int, 32}}, + {"__user_context", ::HalideFunctionInfo::InputScalar, 0, halide_type_t{halide_type_handle, 64}}, + }}; +} +#endif +)GOLDEN_CODE"); + + compare_srcs(function_info.str(), correct_function_info); + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/cplusplus_mangle.cpp b/test/correctness/cplusplus_mangle.cpp new file mode 100644 index 000000000000..cdbeec6f020d --- /dev/null +++ b/test/correctness/cplusplus_mangle.cpp @@ -0,0 +1,489 @@ +#include "Halide.h" + +#include + +using namespace Halide; +using namespace Halide::Internal; + +namespace { + +constexpr int kTestTargetCount = 8; + +struct MangleResult { + const char *expected; + const char *label; +}; + +MangleResult ItaniumABIMangling_main[] = { + {"_Z13test_functionv", "int32_t test_function(void)"}, + {"_ZN3foo13test_functionEv", "int32_t foo::test_function(void)"}, + {"_ZN3foo3bar13test_functionEv", "int32_t foo::bar::test_function(void)"}, + {"_ZN3foo3bar13test_functionEi", "int32_t foo::test_function(int32_t)"}, + {"_ZN3foo3bar13test_functionEiP15halide_buffer_t", "int32_t foo::test_function(int32_t, struct halide_buffer_t *)"}, + {"_ZN14test_namespace14test_namespace13test_functionENS0_15enclosing_class11test_structE", + "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct)"}, + {"_ZN3foo3bar13test_functionEiP15halide_buffer_tS2_", "foo::bar::test_function(int, halide_buffer_t*, halide_buffer_t*)"}, + {"_ZN14test_namespace14test_namespace13test_functionEPNS_11test_structEPKS1_", "test_namespace::test_namespace::test_function(test_namespace::test_struct*, test_namespace::test_struct const*)"}, + {"_ZN14test_namespace14test_namespace13test_functionENS0_15enclosing_class11test_structES2_", + "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct, test_namespace::test_namespace::enclosing_class::test_struct)"}, + {"_ZSt13test_functionv", "std::test_function()"}, + {"_ZNSt3foo13test_functionEv", "std::foo::test_function()"}, + {"_ZSt13test_functionNSt15enclosing_class11test_structE", "std::test_function(std::enclosing_class::test_struct)"}, + {"_ZN14test_namespace14test_namespace13test_functionEPNS_10test_classE", "test_namespace::test_namespace::test_function(test_namespace::test_class*)"}, + {"_ZN14test_namespace14test_namespace13test_functionEPNS_10test_unionE", "test_namespace::test_namespace::test_function(test_namespace::test_union*)"}, + {"_ZN14test_namespace14test_namespace13test_functionEPNS_9test_enumE", "test_namespace::test_namespace::test_function(test_namespace::test_enum*)"}, +}; + +MangleResult win32_expecteds[] = { + {"\001?test_function@@YAHXZ", "int32_t test_function(void)"}, + {"\001?test_function@foo@@YAHXZ", "int32_t foo::test_function(void)"}, + {"\001?test_function@bar@foo@@YAHXZ", "int32_t foo::bar::test_function(void)"}, + {"\001?test_function@bar@foo@@YAHH@Z", "int32_t foo::test_function(int32_t)"}, + {"\001?test_function@bar@foo@@YAHHPAUhalide_buffer_t@@@Z", "int32_t foo::test_function(int32_t, struct halide_buffer_t *)"}, + {"\001?test_function@test_namespace@1@YAHUtest_struct@enclosing_class@11@@Z", + "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct)"}, + {"\001?test_function@bar@foo@@YAHHPAUhalide_buffer_t@@0@Z", "foo::bar::test_function(int, halide_buffer_t*, halide_buffer_t*)"}, + {"\001?test_function@test_namespace@1@YAHPAUtest_struct@1@PBU21@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_struct*, test_namespace::test_struct const*)"}, + {"\001?test_function@test_namespace@1@YAHUtest_struct@enclosing_class@11@0@Z", + "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct, test_namespace::test_namespace::enclosing_class::test_struct)"}, + {"\001?test_function@std@@YAHXZ", "std::test_function()"}, + {"\001?test_function@foo@std@@YAHXZ", "std::foo::test_function()"}, + {"\001?test_function@std@@YAHUtest_struct@enclosing_class@1@@Z", "std::test_function(std::enclosing_class::test_struct)"}, + {"\001?test_function@test_namespace@1@YAHPAVtest_class@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_class*)"}, + {"\001?test_function@test_namespace@1@YAHPATtest_union@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_union*)"}, + {"\001?test_function@test_namespace@1@YAHPAVtest_enum@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_enum*)"}, +}; + +MangleResult win64_expecteds[] = { + {"\001?test_function@@YAHXZ", "int32_t test_function(void)"}, + {"\001?test_function@foo@@YAHXZ", "int32_t foo::test_function(void)"}, + {"\001?test_function@bar@foo@@YAHXZ", "int32_t foo::bar::test_function(void)"}, + {"\001?test_function@bar@foo@@YAHH@Z", "int32_t foo::test_function(int32_t)"}, + {"\001?test_function@bar@foo@@YAHHPEAUhalide_buffer_t@@@Z", "int32_t foo::test_function(int32_t, struct halide_buffer_t *)"}, + {"\001?test_function@test_namespace@1@YAHUtest_struct@enclosing_class@11@@Z", + "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct)"}, + {"\001?test_function@bar@foo@@YAHHPEAUhalide_buffer_t@@0@Z", "foo::bar::test_function(int, halide_buffer_t*, halide_buffer_t*)"}, + {"\001?test_function@test_namespace@1@YAHPEAUtest_struct@1@PEBU21@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_struct*, test_namespace::test_struct const*)"}, + {"\001?test_function@test_namespace@1@YAHUtest_struct@enclosing_class@11@0@Z", + "test_namespace::test_namespace::test_function(test_namespace::test_namespace::enclosing_class::test_struct, test_namespace::test_namespace::enclosing_class::test_struct)"}, + {"\001?test_function@std@@YAHXZ", "std::test_function()"}, + {"\001?test_function@foo@std@@YAHXZ", "std::foo::test_function()"}, + {"\001?test_function@std@@YAHUtest_struct@enclosing_class@1@@Z", "std::test_function(std::enclosing_class::test_struct)"}, + {"\001?test_function@test_namespace@1@YAHPEAVtest_class@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_class*)"}, + {"\001?test_function@test_namespace@1@YAHPEATtest_union@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_union*)"}, + {"\001?test_function@test_namespace@1@YAHPEAVtest_enum@1@@Z", "test_namespace::test_namespace::test_function(test_namespace::test_enum*)"}, +}; + +MangleResult all_types_by_target[kTestTargetCount] = { + {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, + {"_Z13test_functionbahstijlmfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, float, double)"}, + {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, + {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, float, double)"}, + {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, + {"_Z13test_functionbahstijxyfd", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, float, double)"}, + {"\001?test_function@@YAH_NCEFGHI_J_KMN@Z", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, + {"\001?test_function@@YAH_NCEFGHI_J_KMN@Z", "test_function(bool, signed char, unsigned char, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double)"}, +}; + +const char *many_type_subs_itanium = "_Z13test_functionPN14test_namespace2s0EPNS_2s1EPNS_2s2EPNS_2s3EPNS_2s4EPNS_2s5EPNS_2s6EPNS_2s7EPNS_2s8EPNS_2s9EPNS_3s10EPNS_3s11EPNS_3s12EPNS_3s13EPNS_3s14EPNS_3s15EPNS_3s16EPNS_3s17EPNS_3s18EPNS_3s19EPNS_3s20EPNS_3s21EPNS_3s22EPNS_3s23EPNS_3s24EPNS_3s25EPNS_3s26EPNS_3s27EPNS_3s28EPNS_3s29EPNS_3s30EPNS_3s31EPNS_3s32EPNS_3s33EPNS_3s34EPNS_3s35EPNS_3s36EPNS_3s37EPNS_3s38EPNS_3s39EPNS_3s40EPNS_3s41EPNS_3s42EPNS_3s43EPNS_3s44EPNS_3s45EPNS_3s46EPNS_3s47EPNS_3s48EPNS_3s49EPNS_3s50EPNS_3s51EPNS_3s52EPNS_3s53EPNS_3s54EPNS_3s55EPNS_3s56EPNS_3s57EPNS_3s58EPNS_3s59EPNS_3s60EPNS_3s61EPNS_3s62EPNS_3s63EPNS_3s64EPNS_3s65EPNS_3s66EPNS_3s67EPNS_3s68EPNS_3s69EPNS_3s70EPNS_3s71EPNS_3s72EPNS_3s73EPNS_3s74EPNS_3s75EPNS_3s76EPNS_3s77EPNS_3s78EPNS_3s79EPNS_3s80EPNS_3s81EPNS_3s82EPNS_3s83EPNS_3s84EPNS_3s85EPNS_3s86EPNS_3s87EPNS_3s88EPNS_3s89EPNS_3s90EPNS_3s91EPNS_3s92EPNS_3s93EPNS_3s94EPNS_3s95EPNS_3s96EPNS_3s97EPNS_3s98EPNS_3s99ES1_S3_S5_S7_S9_SB_SD_SF_SH_SJ_SL_SN_SP_SR_ST_SV_SX_SZ_S11_S13_S15_S17_S19_S1B_S1D_S1F_S1H_S1J_S1L_S1N_S1P_S1R_S1T_S1V_S1X_S1Z_S21_S23_S25_S27_S29_S2B_S2D_S2F_S2H_S2J_S2L_S2N_S2P_S2R_S2T_S2V_S2X_S2Z_S31_S33_S35_S37_S39_S3B_S3D_S3F_S3H_S3J_S3L_S3N_S3P_S3R_S3T_S3V_S3X_S3Z_S41_S43_S45_S47_S49_S4B_S4D_S4F_S4H_S4J_S4L_S4N_S4P_S4R_S4T_S4V_S4X_S4Z_S51_S53_S55_S57_S59_S5B_S5D_S5F_S5H_S5J_"; + +const char *many_type_subs_win32 = "\001?test_function@@YAHPAUs0@test_namespace@@PAUs1@2@PAUs2@2@PAUs3@2@PAUs4@2@PAUs5@2@PAUs6@2@PAUs7@2@PAUs8@2@PAUs9@2@PAUs10@2@PAUs11@2@PAUs12@2@PAUs13@2@PAUs14@2@PAUs15@2@PAUs16@2@PAUs17@2@PAUs18@2@PAUs19@2@PAUs20@2@PAUs21@2@PAUs22@2@PAUs23@2@PAUs24@2@PAUs25@2@PAUs26@2@PAUs27@2@PAUs28@2@PAUs29@2@PAUs30@2@PAUs31@2@PAUs32@2@PAUs33@2@PAUs34@2@PAUs35@2@PAUs36@2@PAUs37@2@PAUs38@2@PAUs39@2@PAUs40@2@PAUs41@2@PAUs42@2@PAUs43@2@PAUs44@2@PAUs45@2@PAUs46@2@PAUs47@2@PAUs48@2@PAUs49@2@PAUs50@2@PAUs51@2@PAUs52@2@PAUs53@2@PAUs54@2@PAUs55@2@PAUs56@2@PAUs57@2@PAUs58@2@PAUs59@2@PAUs60@2@PAUs61@2@PAUs62@2@PAUs63@2@PAUs64@2@PAUs65@2@PAUs66@2@PAUs67@2@PAUs68@2@PAUs69@2@PAUs70@2@PAUs71@2@PAUs72@2@PAUs73@2@PAUs74@2@PAUs75@2@PAUs76@2@PAUs77@2@PAUs78@2@PAUs79@2@PAUs80@2@PAUs81@2@PAUs82@2@PAUs83@2@PAUs84@2@PAUs85@2@PAUs86@2@PAUs87@2@PAUs88@2@PAUs89@2@PAUs90@2@PAUs91@2@PAUs92@2@PAUs93@2@PAUs94@2@PAUs95@2@PAUs96@2@PAUs97@2@PAUs98@2@PAUs99@2@0123456789PAUs10@2@PAUs11@2@PAUs12@2@PAUs13@2@PAUs14@2@PAUs15@2@PAUs16@2@PAUs17@2@PAUs18@2@PAUs19@2@PAUs20@2@PAUs21@2@PAUs22@2@PAUs23@2@PAUs24@2@PAUs25@2@PAUs26@2@PAUs27@2@PAUs28@2@PAUs29@2@PAUs30@2@PAUs31@2@PAUs32@2@PAUs33@2@PAUs34@2@PAUs35@2@PAUs36@2@PAUs37@2@PAUs38@2@PAUs39@2@PAUs40@2@PAUs41@2@PAUs42@2@PAUs43@2@PAUs44@2@PAUs45@2@PAUs46@2@PAUs47@2@PAUs48@2@PAUs49@2@PAUs50@2@PAUs51@2@PAUs52@2@PAUs53@2@PAUs54@2@PAUs55@2@PAUs56@2@PAUs57@2@PAUs58@2@PAUs59@2@PAUs60@2@PAUs61@2@PAUs62@2@PAUs63@2@PAUs64@2@PAUs65@2@PAUs66@2@PAUs67@2@PAUs68@2@PAUs69@2@PAUs70@2@PAUs71@2@PAUs72@2@PAUs73@2@PAUs74@2@PAUs75@2@PAUs76@2@PAUs77@2@PAUs78@2@PAUs79@2@PAUs80@2@PAUs81@2@PAUs82@2@PAUs83@2@PAUs84@2@PAUs85@2@PAUs86@2@PAUs87@2@PAUs88@2@PAUs89@2@PAUs90@2@PAUs91@2@PAUs92@2@PAUs93@2@PAUs94@2@PAUs95@2@PAUs96@2@PAUs97@2@PAUs98@2@PAUs99@2@@Z"; + +const char *many_type_subs_win64 = "\001?test_function@@YAHPEAUs0@test_namespace@@PEAUs1@2@PEAUs2@2@PEAUs3@2@PEAUs4@2@PEAUs5@2@PEAUs6@2@PEAUs7@2@PEAUs8@2@PEAUs9@2@PEAUs10@2@PEAUs11@2@PEAUs12@2@PEAUs13@2@PEAUs14@2@PEAUs15@2@PEAUs16@2@PEAUs17@2@PEAUs18@2@PEAUs19@2@PEAUs20@2@PEAUs21@2@PEAUs22@2@PEAUs23@2@PEAUs24@2@PEAUs25@2@PEAUs26@2@PEAUs27@2@PEAUs28@2@PEAUs29@2@PEAUs30@2@PEAUs31@2@PEAUs32@2@PEAUs33@2@PEAUs34@2@PEAUs35@2@PEAUs36@2@PEAUs37@2@PEAUs38@2@PEAUs39@2@PEAUs40@2@PEAUs41@2@PEAUs42@2@PEAUs43@2@PEAUs44@2@PEAUs45@2@PEAUs46@2@PEAUs47@2@PEAUs48@2@PEAUs49@2@PEAUs50@2@PEAUs51@2@PEAUs52@2@PEAUs53@2@PEAUs54@2@PEAUs55@2@PEAUs56@2@PEAUs57@2@PEAUs58@2@PEAUs59@2@PEAUs60@2@PEAUs61@2@PEAUs62@2@PEAUs63@2@PEAUs64@2@PEAUs65@2@PEAUs66@2@PEAUs67@2@PEAUs68@2@PEAUs69@2@PEAUs70@2@PEAUs71@2@PEAUs72@2@PEAUs73@2@PEAUs74@2@PEAUs75@2@PEAUs76@2@PEAUs77@2@PEAUs78@2@PEAUs79@2@PEAUs80@2@PEAUs81@2@PEAUs82@2@PEAUs83@2@PEAUs84@2@PEAUs85@2@PEAUs86@2@PEAUs87@2@PEAUs88@2@PEAUs89@2@PEAUs90@2@PEAUs91@2@PEAUs92@2@PEAUs93@2@PEAUs94@2@PEAUs95@2@PEAUs96@2@PEAUs97@2@PEAUs98@2@PEAUs99@2@0123456789PEAUs10@2@PEAUs11@2@PEAUs12@2@PEAUs13@2@PEAUs14@2@PEAUs15@2@PEAUs16@2@PEAUs17@2@PEAUs18@2@PEAUs19@2@PEAUs20@2@PEAUs21@2@PEAUs22@2@PEAUs23@2@PEAUs24@2@PEAUs25@2@PEAUs26@2@PEAUs27@2@PEAUs28@2@PEAUs29@2@PEAUs30@2@PEAUs31@2@PEAUs32@2@PEAUs33@2@PEAUs34@2@PEAUs35@2@PEAUs36@2@PEAUs37@2@PEAUs38@2@PEAUs39@2@PEAUs40@2@PEAUs41@2@PEAUs42@2@PEAUs43@2@PEAUs44@2@PEAUs45@2@PEAUs46@2@PEAUs47@2@PEAUs48@2@PEAUs49@2@PEAUs50@2@PEAUs51@2@PEAUs52@2@PEAUs53@2@PEAUs54@2@PEAUs55@2@PEAUs56@2@PEAUs57@2@PEAUs58@2@PEAUs59@2@PEAUs60@2@PEAUs61@2@PEAUs62@2@PEAUs63@2@PEAUs64@2@PEAUs65@2@PEAUs66@2@PEAUs67@2@PEAUs68@2@PEAUs69@2@PEAUs70@2@PEAUs71@2@PEAUs72@2@PEAUs73@2@PEAUs74@2@PEAUs75@2@PEAUs76@2@PEAUs77@2@PEAUs78@2@PEAUs79@2@PEAUs80@2@PEAUs81@2@PEAUs82@2@PEAUs83@2@PEAUs84@2@PEAUs85@2@PEAUs86@2@PEAUs87@2@PEAUs88@2@PEAUs89@2@PEAUs90@2@PEAUs91@2@PEAUs92@2@PEAUs93@2@PEAUs94@2@PEAUs95@2@PEAUs96@2@PEAUs97@2@PEAUs98@2@PEAUs99@2@@Z"; + +MangleResult many_type_subs[kTestTargetCount] = { + {many_type_subs_itanium, "The expanded prototype is very long."}, + {many_type_subs_itanium, "No really, too large to put here."}, + {many_type_subs_itanium, "wc -l says 4394 characters."}, + {many_type_subs_itanium, "Feel free to run c++filt if you want to..."}, + {many_type_subs_itanium, "longity long long"}, + {many_type_subs_itanium, "It's been a long, long, long..."}, + {many_type_subs_win32, "Not gonna do it."}, + {many_type_subs_win64, "Wouldn't be prudent."}}; + +const char *many_name_subs_itanium = "_Z13test_functionPN15test_namespace01sEPN15test_namespace11sEPN15test_namespace21sEPN15test_namespace31sEPN15test_namespace41sEPN15test_namespace51sEPN15test_namespace61sEPN15test_namespace71sEPN15test_namespace81sEPN15test_namespace91sEPN16test_namespace101sEPN16test_namespace111sEPN16test_namespace121sEPN16test_namespace131sEPN16test_namespace141sEPN16test_namespace151sEPN16test_namespace161sEPN16test_namespace171sEPN16test_namespace181sEPN16test_namespace191sEPN16test_namespace201sEPN16test_namespace211sEPN16test_namespace221sEPN16test_namespace231sEPN16test_namespace241sES1_S4_S7_SA_SD_SG_SJ_SM_SP_SS_SV_SY_S11_S14_S17_S1A_S1D_S1G_S1J_S1M_S1P_S1S_S1V_S1Y_S21_"; + +const char *many_name_subs_win32 = "\001?test_function@@YAHPAUs@test_namespace0@@PAU1test_namespace1@@PAU1test_namespace2@@PAU1test_namespace3@@PAU1test_namespace4@@PAU1test_namespace5@@PAU1test_namespace6@@PAU1test_namespace7@@PAU1test_namespace8@@PAU1test_namespace9@@PAU1test_namespace10@@PAU1test_namespace11@@PAU1test_namespace12@@PAU1test_namespace13@@PAU1test_namespace14@@PAU1test_namespace15@@PAU1test_namespace16@@PAU1test_namespace17@@PAU1test_namespace18@@PAU1test_namespace19@@PAU1test_namespace20@@PAU1test_namespace21@@PAU1test_namespace22@@PAU1test_namespace23@@PAU1test_namespace24@@0123456789PAU1test_namespace10@@PAU1test_namespace11@@PAU1test_namespace12@@PAU1test_namespace13@@PAU1test_namespace14@@PAU1test_namespace15@@PAU1test_namespace16@@PAU1test_namespace17@@PAU1test_namespace18@@PAU1test_namespace19@@PAU1test_namespace20@@PAU1test_namespace21@@PAU1test_namespace22@@PAU1test_namespace23@@PAU1test_namespace24@@@Z"; + +const char *many_name_subs_win64 = "\001?test_function@@YAHPEAUs@test_namespace0@@PEAU1test_namespace1@@PEAU1test_namespace2@@PEAU1test_namespace3@@PEAU1test_namespace4@@PEAU1test_namespace5@@PEAU1test_namespace6@@PEAU1test_namespace7@@PEAU1test_namespace8@@PEAU1test_namespace9@@PEAU1test_namespace10@@PEAU1test_namespace11@@PEAU1test_namespace12@@PEAU1test_namespace13@@PEAU1test_namespace14@@PEAU1test_namespace15@@PEAU1test_namespace16@@PEAU1test_namespace17@@PEAU1test_namespace18@@PEAU1test_namespace19@@PEAU1test_namespace20@@PEAU1test_namespace21@@PEAU1test_namespace22@@PEAU1test_namespace23@@PEAU1test_namespace24@@0123456789PEAU1test_namespace10@@PEAU1test_namespace11@@PEAU1test_namespace12@@PEAU1test_namespace13@@PEAU1test_namespace14@@PEAU1test_namespace15@@PEAU1test_namespace16@@PEAU1test_namespace17@@PEAU1test_namespace18@@PEAU1test_namespace19@@PEAU1test_namespace20@@PEAU1test_namespace21@@PEAU1test_namespace22@@PEAU1test_namespace23@@PEAU1test_namespace24@@@Z"; + +const char *many_name_subs_proto = "test_function(test_namespace0::s*, test_namespace1::s*, test_namespace2::s*, test_namespace3::s*, test_namespace4::s*, test_namespace5::s*, test_namespace6::s*, test_namespace7::s*, test_namespace8::s*, test_namespace9::s*, test_namespace10::s*, test_namespace11::s*, test_namespace12::s*, test_namespace13::s*, test_namespace14::s*, test_namespace15::s*, test_namespace16::s*, test_namespace17::s*, test_namespace18::s*, test_namespace19::s*, test_namespace20::s*, test_namespace21::s*, test_namespace22::s*, test_namespace23::s*, test_namespace24::s*, test_namespace0::s*, test_namespace1::s*, test_namespace2::s*, test_namespace3::s*, test_namespace4::s*, test_namespace5::s*, test_namespace6::s*, test_namespace7::s*, test_namespace8::s*, test_namespace9::s*, test_namespace10::s*, test_namespace11::s*, test_namespace12::s*, test_namespace13::s*, test_namespace14::s*, test_namespace15::s*, test_namespace16::s*, test_namespace17::s*, test_namespace18::s*, test_namespace19::s*, test_namespace20::s*, test_namespace21::s*, test_namespace22::s*, test_namespace23::s*, test_namespace24::s*)"; + +MangleResult many_name_subs[kTestTargetCount] = { + {many_name_subs_itanium, many_name_subs_proto}, + {many_name_subs_itanium, many_name_subs_proto}, + {many_name_subs_itanium, many_name_subs_proto}, + {many_name_subs_itanium, many_name_subs_proto}, + {many_name_subs_itanium, many_name_subs_proto}, + {many_name_subs_itanium, many_name_subs_proto}, + {many_name_subs_win32, many_name_subs_proto}, + {many_name_subs_win64, many_name_subs_proto}}; + +MangleResult stacked_indirections[kTestTargetCount] = { + {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, + {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, + {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, + {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, + {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, + {"_Z13test_functionPKiPKS0_PKS2_PKS4_PKS6_PKS8_PKSA_PKSC_", ""}, + {"\001?test_function@@YAHPBHPBQBHPBQBQBHPBQBQBQBHPBQBQBQBQBHPBQBQBQBQBQBHPBQBQBQBQBQBQBHPBQBQBQBQBQBQBQBH@Z", ""}, + {"\001?test_function@@YAHPEBHPEBQEBHPEBQEBQEBHPEBQEBQEBQEBHPEBQEBQEBQEBQEBHPEBQEBQEBQEBQEBQEBHPEBQEBQEBQEBQEBQEBQEBHPEBQEBQEBQEBQEBQEBQEBQEBH@Z", ""}}; + +MangleResult all_mods_itanium[] = { + {"_Z13test_function1sRS_OS_", "test_function(s, s&, s&&)"}, + {"_Z13test_function1sRKS_OS0_", "test_function(s, s const&, s const&&)"}, + {"_Z13test_function1sRVS_OS0_", "test_function(s, s volatile&, s volatile&&)"}, + {"_Z13test_function1sRVKS_OS0_", "test_function(s, s const volatile&, s const volatile&&)"}, + {"_Z13test_function1sRrS_OS0_", "test_function(s, s restrict&, s restrict&&)"}, + {"_Z13test_function1sRrKS_OS0_", "test_function(s, s const restrict&, s const restrict&&)"}, + {"_Z13test_function1sRrVS_OS0_", "test_function(s, s volatile restrict&, s volatile restrict&&)"}, + {"_Z13test_function1sRrVKS_OS0_", "test_function(s, s const volatile restrict&, s const volatile restrict&&)"}, + {"_Z13test_functionP1sRS0_OS0_", "test_function(s*, s*&, s*&&)"}, + {"_Z13test_functionPK1sRS1_OS1_", "test_function(s const*, s const*&, s const*&&)"}, + {"_Z13test_functionPV1sRS1_OS1_", "test_function(s volatile*, s volatile*&, s volatile*&&)"}, + {"_Z13test_functionPVK1sRS1_OS1_", "test_function(s const volatile*, s const volatile*&, s const volatile*&&)"}, + {"_Z13test_functionPr1sRS1_OS1_", "test_function(s restrict*, s restrict*&, s restrict*&&)"}, + {"_Z13test_functionPrK1sRS1_OS1_", "test_function(s const restrict*, s const restrict*&, s const restrict*&&)"}, + {"_Z13test_functionPrV1sRS1_OS1_", "test_function(s volatile restrict*, s volatile restrict*&, s volatile restrict*&&)"}, + {"_Z13test_functionPrVK1sRS1_OS1_", "test_function(s const volatile restrict*, s const volatile restrict*&, s const volatile restrict*&&)"}}; + +MangleResult all_mods_win32[] = { + {"\001?test_function@@YAHUs@@AAU1@$$QAU1@@Z", "test_function(s, s&, s&&)"}, + {"\001?test_function@@YAHUs@@ABU1@$$QBU1@@Z", "test_function(s, s const&, s const&&)"}, + {"\001?test_function@@YAHUs@@ACU1@$$QCU1@@Z", "test_function(s, s volatile&, s volatile&&)"}, + {"\001?test_function@@YAHUs@@ADU1@$$QDU1@@Z", "test_function(s, s const volatile&, s const volatile&&)"}, + {"\001?test_function@@YAHUs@@AAU1@$$QAU1@@Z", "test_function(s, s restrict&, s restrict&&)"}, + {"\001?test_function@@YAHUs@@ABU1@$$QBU1@@Z", "test_function(s, s const restrict&, s const restrict&&)"}, + {"\001?test_function@@YAHUs@@ACU1@$$QCU1@@Z", "test_function(s, s volatile restrict&, s volatile restrict&&)"}, + {"\001?test_function@@YAHUs@@ADU1@$$QDU1@@Z", "test_function(s, s const volatile restrict&, s const volatile restrict&&)"}, + {"\001?test_function@@YAHPAUs@@AAPAU1@$$QAPAU1@@Z", "test_function(s*, s*&, s*&&)"}, + {"\001?test_function@@YAHPBUs@@AAPBU1@$$QAPBU1@@Z", "test_function(s const*, s const*&, s const*&&)"}, + {"\001?test_function@@YAHPCUs@@AAPCU1@$$QAPCU1@@Z", "test_function(s volatile*, s volatile*&, s volatile*&&)"}, + {"\001?test_function@@YAHPDUs@@AAPDU1@$$QAPDU1@@Z", "test_function(s const volatile*, s const volatile*&, s const volatile*&&)"}, + {"\001?test_function@@YAHPAUs@@AAPAU1@$$QAPAU1@@Z", "test_function(s restrict*, s restrict*&, s restrict*&&)"}, + {"\001?test_function@@YAHPBUs@@AAPBU1@$$QAPBU1@@Z", "test_function(s const restrict*, s const restrict*&, s const restrict*&&)"}, + {"\001?test_function@@YAHPCUs@@AAPCU1@$$QAPCU1@@Z", "test_function(s volatile restrict*, s volatile restrict*&, s volatile restrict*&&)"}, + {"\001?test_function@@YAHPDUs@@AAPDU1@$$QAPDU1@@Z", "test_function(s const volatile restrict*, s const volatile restrict*&, s const volatile restrict*&&)"}}; + +MangleResult all_mods_win64[] = { + {"\001?test_function@@YAHUs@@AEAU1@$$QEAU1@@Z", "test_function(s, s&, s&&)"}, + {"\001?test_function@@YAHUs@@AEBU1@$$QEBU1@@Z", "test_function(s, s const&, s const&&)"}, + {"\001?test_function@@YAHUs@@AECU1@$$QECU1@@Z", "test_function(s, s volatile&, s volatile&&)"}, + {"\001?test_function@@YAHUs@@AEDU1@$$QEDU1@@Z", "test_function(s, s const volatile&, s const volatile&&)"}, + {"\001?test_function@@YAHUs@@AEAU1@$$QEAU1@@Z", "test_function(s, s restrict&, s restrict&&)"}, + {"\001?test_function@@YAHUs@@AEBU1@$$QEBU1@@Z", "test_function(s, s const restrict&, s const restrict&&)"}, + {"\001?test_function@@YAHUs@@AECU1@$$QECU1@@Z", "test_function(s, s volatile restrict&, s volatile restrict&&)"}, + {"\001?test_function@@YAHUs@@AEDU1@$$QEDU1@@Z", "test_function(s, s const volatile restrict&, s const volatile restrict&&)"}, + {"\001?test_function@@YAHPEAUs@@AEAPEAU1@$$QEAPEAU1@@Z", "test_function(s*, s*&, s*&&)"}, + {"\001?test_function@@YAHPEBUs@@AEAPEBU1@$$QEAPEBU1@@Z", "test_function(s const*, s const*&, s const*&&)"}, + {"\001?test_function@@YAHPECUs@@AEAPECU1@$$QEAPECU1@@Z", "test_function(s volatile*, s volatile*&, s volatile*&&)"}, + {"\001?test_function@@YAHPEDUs@@AEAPEDU1@$$QEAPEDU1@@Z", "test_function(s const volatile*, s const volatile*&, s const volatile*&&)"}, + {"\001?test_function@@YAHPEAUs@@AEAPEAU1@$$QEAPEAU1@@Z", "test_function(s restrict*, s restrict*&, s restrict*&&)"}, + {"\001?test_function@@YAHPEBUs@@AEAPEBU1@$$QEAPEBU1@@Z", "test_function(s const restrict*, s const restrict*&, s const restrict*&&)"}, + {"\001?test_function@@YAHPECUs@@AEAPECU1@$$QEAPECU1@@Z", "test_function(s volatile restrict*, s volatile restrict*&, s volatile restrict*&&)"}, + {"\001?test_function@@YAHPEDUs@@AEAPEDU1@$$QEAPEDU1@@Z", "test_function(s const volatile restrict*, s const volatile restrict*&, s const volatile restrict*&&)"}, +}; + +MangleResult two_void_stars_itanium[] = { + {"_Z13test_functionPvS_", "test_function(void *, void *)"}, +}; + +MangleResult two_void_stars_win64[] = { + {"\001?test_function@@YAHPEAX0@Z", "test_function(void *, void *)"}, +}; + +MangleResult two_void_stars_win32[] = { + {"\001?test_function@@YAHPAX0@Z", "test_function(void *, void *)"}, +}; + +void check_result(const MangleResult *expecteds, size_t &expected_index, + const Target &target, const std::string &mangled_name) { + internal_assert(mangled_name == expecteds[expected_index].expected) << "Mangling for " << expecteds[expected_index].label << " expected\n " << expecteds[expected_index].expected << " got\n " << mangled_name << "\nfor target " << target.to_string(); + expected_index++; +} + +void main_tests(const MangleResult *expecteds, const Target &target) { + size_t expecteds_index = 0; + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {}, Int(32), {}, target)); + + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"foo"}, Int(32), {}, target)); + + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"foo", "bar"}, Int(32), {}, target)); + + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"foo", "bar"}, Int(32), + {ExternFuncArgument(42)}, target)); + + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"foo", "bar"}, Int(32), + {ExternFuncArgument(42), ExternFuncArgument(Buffer<>())}, target)); + + halide_handle_cplusplus_type enclosed_type_info(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "test_struct"), + {"test_namespace", "test_namespace"}, + {halide_cplusplus_type_name(halide_cplusplus_type_name::Class, + "enclosing_class")})); + Type test_type(Handle(&enclosed_type_info)); + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), + {ExternFuncArgument(make_zero(test_type))}, target)); + + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"foo", "bar"}, Int(32), + {ExternFuncArgument(42), ExternFuncArgument(Buffer<>()), + ExternFuncArgument(Buffer<>())}, + target)); + + halide_handle_cplusplus_type qual1(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "test_struct"), + { + "test_namespace", + }, + {}, {halide_handle_cplusplus_type::Pointer})); + Type qual1_type(Handle(&qual1)); + halide_handle_cplusplus_type qual2(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "test_struct"), + { + "test_namespace", + }, + {}, {halide_handle_cplusplus_type::Pointer | halide_handle_cplusplus_type::Const})); + Type qual2_type(Handle(&qual2)); + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), + {ExternFuncArgument(make_zero(qual1_type)), + ExternFuncArgument(make_zero(qual2_type))}, + target)); + + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), + {ExternFuncArgument(make_zero(test_type)), + ExternFuncArgument(make_zero(test_type))}, + target)); + + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"std"}, Int(32), {}, target)); + + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"std", "foo"}, Int(32), {}, target)); + + halide_handle_cplusplus_type std_enclosed_type_info(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "test_struct"), {"std"}, + {halide_cplusplus_type_name(halide_cplusplus_type_name::Class, "enclosing_class")})); + Type std_test_type(Handle(&std_enclosed_type_info)); + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"std"}, Int(32), + {ExternFuncArgument(make_zero(std_test_type))}, target)); + + halide_handle_cplusplus_type class_type_info(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Class, "test_class"), + { + "test_namespace", + }, + {}, {halide_handle_cplusplus_type::Pointer})); + Type class_type(Handle(&class_type_info)); + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), + { + ExternFuncArgument(make_zero(class_type)), + }, + target)); + + halide_handle_cplusplus_type union_type_info(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Union, "test_union"), + { + "test_namespace", + }, + {}, {halide_handle_cplusplus_type::Pointer})); + Type union_type(Handle(&union_type_info)); + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), + { + ExternFuncArgument(make_zero(union_type)), + }, + target)); + + halide_handle_cplusplus_type enum_type_info(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Class, "test_enum"), + { + "test_namespace", + }, + {}, {halide_handle_cplusplus_type::Pointer})); + Type enum_type(Handle(&enum_type_info)); + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {"test_namespace", "test_namespace"}, Int(32), + { + ExternFuncArgument(make_zero(enum_type)), + }, + target)); +} + +} // namespace + +int main() { + Target targets[kTestTargetCount]{ + Target(Target::Linux, Target::X86, 32), + Target(Target::Linux, Target::X86, 64), + Target(Target::OSX, Target::X86, 32), + Target(Target::OSX, Target::X86, 64), + Target(Target::IOS, Target::ARM, 32), + Target(Target::IOS, Target::ARM, 64), + Target(Target::Windows, Target::X86, 32), + Target(Target::Windows, Target::X86, 64)}; + MangleResult *expecteds[kTestTargetCount]{ + ItaniumABIMangling_main, ItaniumABIMangling_main, + ItaniumABIMangling_main, ItaniumABIMangling_main, + ItaniumABIMangling_main, ItaniumABIMangling_main, + win32_expecteds, win64_expecteds}; + + size_t i = 0; + for (const auto &target : targets) { + main_tests(expecteds[i++], target); + } + + { + // Test all primitive types. + std::vector args; + args.emplace_back(make_zero(Bool())); + args.emplace_back(make_zero(Int(8))); + args.emplace_back(make_zero(UInt(8))); + args.emplace_back(make_zero(Int(16))); + args.emplace_back(make_zero(UInt(16))); + args.emplace_back(make_zero(Int(32))); + args.emplace_back(make_zero(UInt(32))); + args.emplace_back(make_zero(Int(64))); + args.emplace_back(make_zero(UInt(64))); + args.emplace_back(make_zero(Float(32))); + args.emplace_back(make_zero(Float(64))); + + size_t expecteds_index = 0; + for (const auto &target : targets) { + check_result(all_types_by_target, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); + } + } + + { + // Test a whole ton of substitutions on type. + std::vector type_info; + for (int i = 0; i < 100; i++) { + std::stringstream oss; + oss << i; + halide_handle_cplusplus_type t(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s" + oss.str()), + { + "test_namespace", + }, + {}, {halide_handle_cplusplus_type::Pointer})); + type_info.push_back(t); + } + std::vector args; + args.reserve(200); + for (int i = 0; i < 200; i++) { + args.emplace_back(make_zero(Handle(&type_info[i % 100]))); + } + + size_t expecteds_index = 0; + for (const auto &target : targets) { + check_result(many_type_subs, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); + } + } + + { + // Test a whole ton of substitutions on names. + std::vector type_info; + for (int i = 0; i < 25; i++) { + std::stringstream oss; + oss << i; + halide_handle_cplusplus_type t(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s"), + { + "test_namespace" + oss.str(), + }, + {}, {halide_handle_cplusplus_type::Pointer})); + type_info.push_back(t); + } + std::vector args; + args.reserve(50); + for (int i = 0; i < 50; i++) { + args.emplace_back(make_zero(Handle(&type_info[i % 25]))); + } + + size_t expecteds_index = 0; + for (const auto &target : targets) { + check_result(many_name_subs, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); + } + } + + { + // Stack up a bunch of pointers and qualifiers. + // int test_function(int * const, int *const*const, int *const*const*const*, ...); + std::vector type_info; + for (size_t i = 1; i <= 8; i++) { + std::vector mods; + mods.reserve(i); + for (size_t j = 0; j < i; j++) { + mods.push_back(halide_handle_cplusplus_type::Pointer | halide_handle_cplusplus_type::Const); + } + halide_handle_cplusplus_type t(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Simple, "int32_t"), + {}, {}, mods)); + type_info.push_back(t); + } + std::vector args; + args.reserve(type_info.size()); + for (const auto &ti : type_info) { + args.emplace_back(make_zero(Handle(&ti))); + } + size_t expecteds_index = 0; + for (const auto &target : targets) { + check_result(stacked_indirections, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); + } + } + + { + // Test all qualifiers and all ref arguments + for (const auto &target : targets) { + size_t expecteds_index = 0; + for (uint8_t mods = 0; mods < 16; mods++) { + halide_handle_cplusplus_type t1(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s"), {}, {}, {mods})); + halide_handle_cplusplus_type t2(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s"), {}, {}, {mods}, halide_handle_cplusplus_type::LValueReference)); + halide_handle_cplusplus_type t3(halide_handle_cplusplus_type( + halide_cplusplus_type_name(halide_cplusplus_type_name::Struct, "s"), {}, {}, {mods}, halide_handle_cplusplus_type::RValueReference)); + std::vector args; + args.emplace_back(make_zero(Handle(&t1))); + args.emplace_back(make_zero(Handle(&t2))); + args.emplace_back(make_zero(Handle(&t3))); + + MangleResult *expecteds = (target.os == Target::Windows) ? (target.bits == 64 ? all_mods_win64 : all_mods_win32) : all_mods_itanium; + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); + } + } + } + + { + // Test two void * arguments to ensure substititon handles void * correctly. + // (This is a special case as "void *" is represented using nullptr for the type info.) + for (const auto &target : targets) { + size_t expecteds_index = 0; + std::vector args; + args.emplace_back(make_zero(Handle(nullptr))); + args.emplace_back(make_zero(Handle(nullptr))); + + MangleResult *expecteds = (target.os == Target::Windows) ? (target.bits == 64 ? two_void_stars_win64 : two_void_stars_win32) : two_void_stars_itanium; + check_result(expecteds, expecteds_index, target, + cplusplus_function_mangled_name("test_function", {}, Int(32), args, target)); + } + } + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/cse.cpp b/test/correctness/cse.cpp new file mode 100644 index 000000000000..fd5fb0322595 --- /dev/null +++ b/test/correctness/cse.cpp @@ -0,0 +1,179 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +namespace { + +// Normalize all names in an expr so that expr compares can be done +// without worrying about mere name differences. +class NormalizeVarNames : public IRMutator { + int counter = 0; + + std::map new_names; + + using IRMutator::visit; + + Expr visit(const Variable *var) override { + std::map::iterator iter = new_names.find(var->name); + if (iter == new_names.end()) { + return var; + } else { + return Variable::make(var->type, iter->second); + } + } + + Expr visit(const Let *let) override { + std::string new_name = "t" + std::to_string(counter++); + new_names[let->name] = new_name; + Expr value = mutate(let->value); + Expr body = mutate(let->body); + return Let::make(new_name, value, body); + } + +public: + NormalizeVarNames() = default; +}; + +void check(const Expr &in, const Expr &correct) { + Expr result = common_subexpression_elimination(in); + result = NormalizeVarNames()(result); + internal_assert(equal(result, correct)) + << "Incorrect CSE:\n" + << in + << "\nbecame:\n" + << result + << "\ninstead of:\n" + << correct << "\n"; +} + +// Construct a nested block of lets. Variables of the form "tn" refer +// to expr n in the vector. +Expr ssa_block(std::vector exprs) { + Expr e = exprs.back(); + for (size_t i = exprs.size() - 1; i > 0; i--) { + std::string name = "t" + std::to_string(i - 1); + e = Let::make(name, exprs[i - 1], e); + } + return e; +} + +} // namespace + +int main(int argc, char **argv) { + Expr x = Variable::make(Int(32), "x"); + Expr y = Variable::make(Int(32), "y"); + + Expr t[32], tf[32]; + for (int i = 0; i < 32; i++) { + t[i] = Variable::make(Int(32), "t" + std::to_string(i)); + tf[i] = Variable::make(Float(32), "t" + std::to_string(i)); + } + Expr e, correct; + + // This is fine as-is. + e = ssa_block({sin(x), tf[0] * tf[0]}); + check(e, e); + + // Test a simple case. + e = ((x * x + x) * (x * x + x)) + x * x; + e += e; + correct = ssa_block({x * x, // x*x + t[0] + x, // x*x + x + t[1] * t[1] + t[0], // (x*x + x)*(x*x + x) + x*x + t[2] + t[2]}); + check(e, correct); + + // Check for idempotence (also checks a case with lets) + check(correct, correct); + + // Check a case with redundant lets + e = ssa_block({x * x, + x * x, + t[0] / t[1], + t[1] / t[1], + t[2] % t[3], + (t[4] + x * x) + x * x}); + correct = ssa_block({x * x, + t[0] / t[0], + (t[1] % t[1] + t[0]) + t[0]}); + check(e, correct); + + // Check a case with nested lets with shared subexpressions + // between the lets, and repeated names. + Expr e1 = ssa_block({x * x, // a = x*x + t[0] + x, // b = a + x + t[1] * t[1] * t[0]}); // c = b * b * a + Expr e2 = ssa_block({x * x, // a again + t[0] - x, // d = a - x + t[1] * t[1] * t[0]}); // e = d * d * a + e = ssa_block({e1 + x * x, // f = c + a + e1 + e2, // g = c + e + t[0] + t[0] * t[1]}); // h = f + f * g + + correct = ssa_block({x * x, // t0 = a = x*x + t[0] + x, // t1 = b = a + x = t0 + x + t[1] * t[1] * t[0], // t2 = c = b * b * a = t1 * t1 * t0 + t[2] + t[0], // t3 = f = c + a = t2 + t0 + t[0] - x, // t4 = d = a - x = t0 - x + t[3] + t[3] * (t[2] + t[4] * t[4] * t[0])}); // h (with g substituted in) + check(e, correct); + + // Test it scales OK. + e = x; + for (int i = 0; i < 100; i++) { + e = e * e + e + i; + e = e * e - e * i; + } + Expr result = common_subexpression_elimination(e); + + { + Expr pred = x * x + y * y > 0; + Expr index = select(x * x + y * y > 0, x * x + y * y + 2, x * x + y * y + 10); + Expr load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), const_true(), ModulusRemainder()); + Expr pred_load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), pred, ModulusRemainder()); + e = select(x * y > 10, x * y + 2, x * y + 3 + load) + pred_load; + + Expr t2 = Variable::make(Bool(), "t2"); + Expr cse_load = Load::make(Int(32), "buf", t[3], Buffer<>(), Parameter(), const_true(), ModulusRemainder()); + Expr cse_pred_load = Load::make(Int(32), "buf", t[3], Buffer<>(), Parameter(), t2, ModulusRemainder()); + correct = ssa_block({x * y, + x * x + y * y, + t[1] > 0, + select(t2, t[1] + 2, t[1] + 10), + select(t[0] > 10, t[0] + 2, t[0] + 3 + cse_load) + cse_pred_load}); + + check(e, correct); + } + + { + Expr pred = x * x + y * y > 0; + Expr index = select(x * x + y * y > 0, x * x + y * y + 2, x * x + y * y + 10); + Expr load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), const_true(), ModulusRemainder()); + Expr pred_load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), pred, ModulusRemainder()); + e = select(x * y > 10, x * y + 2, x * y + 3 + pred_load) + pred_load; + + Expr t2 = Variable::make(Bool(), "t2"); + Expr cse_load = Load::make(Int(32), "buf", select(t2, t[1] + 2, t[1] + 10), Buffer<>(), Parameter(), const_true(), ModulusRemainder()); + Expr cse_pred_load = Load::make(Int(32), "buf", select(t2, t[1] + 2, t[1] + 10), Buffer<>(), Parameter(), t2, ModulusRemainder()); + correct = ssa_block({x * y, + x * x + y * y, + t[1] > 0, + cse_pred_load, + select(t[0] > 10, t[0] + 2, t[0] + 3 + t[3]) + t[3]}); + + check(e, correct); + } + + { + Expr halide_func = Call::make(Int(32), "dummy", {0}, Call::Halide); + e = halide_func * halide_func; + Expr t0 = Variable::make(halide_func.type(), "t0"); + // It's okay to CSE Halide call within an expr + correct = Let::make("t0", halide_func, t0 * t0); + check(e, correct); + } + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/deinterleave_vector.cpp b/test/correctness/deinterleave_vector.cpp new file mode 100644 index 000000000000..25106cd46c8f --- /dev/null +++ b/test/correctness/deinterleave_vector.cpp @@ -0,0 +1,45 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +namespace { +void check(Expr a, const Expr &even, const Expr &odd) { + a = simplify(a); + Expr correct_even = extract_even_lanes(a); + Expr correct_odd = extract_odd_lanes(a); + if (!equal(correct_even, even)) { + internal_error << correct_even << " != " << even << "\n"; + } + if (!equal(correct_odd, odd)) { + internal_error << correct_odd << " != " << odd << "\n"; + } +} +} // namespace + +int main(int argc, char **argv) { + std::pair result; + Expr x = Variable::make(Int(32), "x"); + Expr ramp = Ramp::make(x + 4, 3, 8); + Expr ramp_a = Ramp::make(x + 4, 6, 4); + Expr ramp_b = Ramp::make(x + 7, 6, 4); + Expr broadcast = Broadcast::make(x + 4, 16); + Expr broadcast_a = Broadcast::make(x + 4, 8); + const Expr &broadcast_b = broadcast_a; + + check(ramp, ramp_a, ramp_b); + check(broadcast, broadcast_a, broadcast_b); + + check(Load::make(ramp.type(), "buf", ramp, Buffer<>(), Parameter(), const_true(ramp.type().lanes()), ModulusRemainder()), + Load::make(ramp_a.type(), "buf", ramp_a, Buffer<>(), Parameter(), const_true(ramp_a.type().lanes()), ModulusRemainder()), + Load::make(ramp_b.type(), "buf", ramp_b, Buffer<>(), Parameter(), const_true(ramp_b.type().lanes()), ModulusRemainder())); + + Expr vec_x = Variable::make(Int(32, 4), "vec_x"); + Expr vec_y = Variable::make(Int(32, 4), "vec_y"); + check(Shuffle::make({vec_x, vec_y}, {0, 4, 2, 6, 4, 2, 3, 7, 1, 2, 3, 4}), + Shuffle::make({vec_x, vec_y}, {0, 2, 4, 3, 1, 3}), + Shuffle::make({vec_x, vec_y}, {4, 6, 2, 7, 2, 4})); + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/expr_match.cpp b/test/correctness/expr_match.cpp new file mode 100644 index 000000000000..0d12fb9eb2ab --- /dev/null +++ b/test/correctness/expr_match.cpp @@ -0,0 +1,41 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +int main(int argc, char **argv) { + std::vector matches; + Expr w = Variable::make(Int(32), "*"); + Expr fw = Variable::make(Float(32), "*"); + Expr x = Variable::make(Int(32), "x"); + Expr y = Variable::make(Int(32), "y"); + Expr fx = Variable::make(Float(32), "fx"); + Expr fy = Variable::make(Float(32), "fy"); + + Expr vec_wild = Variable::make(Int(32, 4), "*"); + + internal_assert(expr_match(w, 3, matches) && + equal(matches[0], 3)); + + internal_assert(expr_match(w + 3, (y * 2) + 3, matches) && + equal(matches[0], y * 2)); + + internal_assert(expr_match(fw * 17 + cast(w + cast(fw)), + (81.0f * fy) * 17 + cast(x / 2 + cast(x + 4.5f)), matches) && + matches.size() == 3 && + equal(matches[0], 81.0f * fy) && + equal(matches[1], x / 2) && + equal(matches[2], x + 4.5f)); + + internal_assert(!expr_match(fw + 17, fx + 18, matches) && + matches.empty()); + internal_assert(!expr_match((w * 2) + 17, fx + 17, matches) && + matches.empty()); + internal_assert(!expr_match(w * 3, 3 * x, matches) && + matches.empty()); + + internal_assert(expr_match(vec_wild * 3, Ramp::make(x, y, 4) * 3, matches)); + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/generator_internal.cpp b/test/correctness/generator_internal.cpp new file mode 100644 index 000000000000..2f95a5593396 --- /dev/null +++ b/test/correctness/generator_internal.cpp @@ -0,0 +1,217 @@ +#include "Halide.h" + +#include + +using namespace Halide; + +namespace Halide { +namespace Internal { + +void generator_test() { + GeneratorContext context(get_host_target().without_feature(Target::Profile)); + + // Verify that the Generator's internal phase actually prevents unsupported + // order of operations. + { + class Tester : public Generator { + public: + GeneratorParam gp0{"gp0", 0}; + GeneratorParam gp1{"gp1", 1.f}; + GeneratorParam gp2{"gp2", 2}; + + Input input{"input"}; + Output output{"output", Int(32), 1}; + + void generate() { + internal_assert(gp0 == 1); + internal_assert(gp1 == 2.f); + internal_assert(gp2 == (uint64_t)2); // unchanged + Var x; + output(x) = input + gp0; + } + void schedule() { + // empty + } + }; + + Tester tester; + tester.init_from_context(context); + internal_assert(tester.phase == GeneratorBase::Created); + + // Verify that calling GeneratorParam::set() works. + tester.gp0.set(1); + + tester.set_inputs_vector({{StubInput(42)}}); + internal_assert(tester.phase == GeneratorBase::InputsSet); + + // tester.set_inputs_vector({{StubInput(43)}}); // This will assert-fail. + + // Also ok to call in this phase. + tester.gp1.set(2.f); + + tester.call_generate(); + internal_assert(tester.phase == GeneratorBase::GenerateCalled); + + // tester.set_inputs_vector({{StubInput(44)}}); // This will assert-fail. + // tester.gp2.set(2); // This will assert-fail. + + tester.call_schedule(); + internal_assert(tester.phase == GeneratorBase::ScheduleCalled); + + // tester.set_inputs_vector({{StubInput(45)}}); // This will assert-fail. + // tester.gp2.set(2); // This will assert-fail. + // tester.sp2.set(202); // This will assert-fail. + } + + // Verify that set_inputs() works properly, even if the specific subtype of Generator is not known. + { + class Tester : public Generator { + public: + Input input_int{"input_int"}; + Input input_float{"input_float"}; + Input input_byte{"input_byte"}; + Input input_scalar_array{"input_scalar_array"}; + Input input_func_typed{"input_func_typed", Int(16), 1}; + Input input_func_untyped{"input_func_untyped", 1}; + Input input_func_array{"input_func_array", 1}; + Input> input_buffer_typed{"input_buffer_typed"}; + Input> input_buffer_untyped{"input_buffer_untyped"}; + Output output{"output", Float(32), 1}; + + void generate() { + Var x; + output(x) = input_int + + input_float + + input_byte + + input_scalar_array[3] + + input_func_untyped(x) + + input_func_typed(x) + + input_func_array[0](x) + + input_buffer_typed(x, 0, 0) + + input_buffer_untyped(x, Halide::_); + } + void schedule() { + // nothing + } + }; + + Tester tester_instance; + tester_instance.init_from_context(context); + // Use a base-typed reference to verify the code below doesn't know about subtype + GeneratorBase &tester = tester_instance; + + const int i = 1234; + const float f = 2.25f; + const uint8_t b = 0x42; + const std::vector a = {1, 2, 3, 4}; + Var x; + Func fn_typed, fn_untyped; + fn_typed(x) = make_const(Int(16), 38); + fn_untyped(x) = 32.f; + const std::vector fn_array = {fn_untyped, fn_untyped}; + + Buffer buf_typed(1, 1, 1); + Buffer buf_untyped(1); + + buf_typed.fill(33); + buf_untyped.fill(34); + + // set_inputs() requires inputs in Input<>-decl-order, + // and all inputs match type exactly. + tester.set_inputs(i, f, b, a, fn_typed, fn_untyped, fn_array, buf_typed, buf_untyped); + tester.call_generate(); + tester.call_schedule(); + + Buffer im = tester_instance.realize({1}); + internal_assert(im.dimensions() == 1); + internal_assert(im.dim(0).extent() == 1); + internal_assert(im(0) == 1475.25f) << "Expected 1475.25 but saw " << im(0); + } + + // Verify that array inputs and outputs are typed correctly. + { + class Tester : public Generator { + public: + Input expr_array_input{"expr_array_input"}; + Input func_array_input{"input_func_array"}; + Input[]> buffer_array_input{"buffer_array_input"}; + + Input expr_array_output{"expr_array_output"}; + Output func_array_output{"func_array_output"}; + Output[]> buffer_array_output{"buffer_array_output"}; + + void generate() { + } + }; + + Tester tester_instance; + + static_assert(std::is_same_v, "type mismatch"); + static_assert(std::is_same_v, "type mismatch"); + + static_assert(std::is_same_v, "type mismatch"); + static_assert(std::is_same_v, "type mismatch"); + + static_assert(std::is_same_v, "type mismatch"); + static_assert(std::is_same_v, "type mismatch"); + } + + class GPTester : public Generator { + public: + GeneratorParam gp{"gp", 0}; + Output output{"output", Int(32), 0}; + + void generate() { + internal_assert(get_target().has_feature(Target::Profile)); + output() = 0; + } + void schedule() { + } + + // Test that we can override init_from_context() to modify the target + // we use. (Generally speaking, your code probably should ever need to + // do this; this code only does it for testing purposes. See comments + // in Generator.h.) + void init_from_context(const GeneratorContext &context) override { + auto t = context.target().with_feature(Target::Profile); + Generator::init_from_context(context.with_target(t)); + } + }; + GPTester gp_tester; + gp_tester.init_from_context(context); + // Accessing the GeneratorParam will assert-fail if we + // don't do some minimal setup here. + gp_tester.set_inputs_vector({}); + gp_tester.call_generate(); + gp_tester.call_schedule(); + auto &gp = gp_tester.gp; + + // Verify that RDom parameter-pack variants can convert GeneratorParam to Expr + RDom rdom(0, gp, 0, gp); + + // Verify that Func parameter-pack variants can convert GeneratorParam to Expr + Var x, y; + Func f, g; + f(x, y) = x + y; + g(x, y) = f(gp, gp); // check Func::operator() overloads + g(rdom.x, rdom.y) += f(rdom.x, rdom.y); + g.update(0).reorder(rdom.y, rdom.x); // check Func::reorder() overloads for RDom::operator RVar() + + // Verify that print() parameter-pack variants can convert GeneratorParam to Expr + print(f(0, 0), g(1, 1), gp); + print_when(true, f(0, 0), g(1, 1), gp); + + // Verify that Tuple parameter-pack variants can convert GeneratorParam to Expr + Tuple t(gp, gp, gp); + + std::cout << "Generator test passed\n"; +} + +} // namespace Internal +} // namespace Halide + +int main(int argc, char **argv) { + Halide::Internal::generator_test(); + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/ir_equality.cpp b/test/correctness/ir_equality.cpp new file mode 100644 index 000000000000..cdc0aff40fce --- /dev/null +++ b/test/correctness/ir_equality.cpp @@ -0,0 +1,52 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +namespace { + +void check_equal(const Expr &a, const Expr &b) { + internal_assert(graph_equal(a, b)) + << "Error in ir_equality test: expected equal, but not equal, when comparing:\n" + << a + << "\nand\n" + << b << "\n"; +} + +void check_not_equal(const Expr &a, const Expr &b) { + bool eq = graph_equal(a, b); + bool lt_ab = graph_less_than(a, b); + bool lt_ba = graph_less_than(b, a); + internal_assert(!eq && (lt_ab != lt_ba)) + << "Error in ir_equality test: expected not equal with a consistent " + "(antisymmetric) ordering, when comparing:\n" + << a + << "\nand\n" + << b << "\n"; +} + +} // namespace + +int main() { + Expr x = Variable::make(Int(32), "x"); + check_equal(Ramp::make(x, 4, 3), Ramp::make(x, 4, 3)); + check_not_equal(Ramp::make(x, 2, 3), Ramp::make(x, 4, 3)); + + check_equal(x, Variable::make(Int(32), "x")); + check_not_equal(x, Variable::make(Int(32), "y")); + + // Something that will hang if IREquality has poor computational + // complexity. + Expr e1 = x, e2 = x; + for (int i = 0; i < 100; i++) { + e1 = e1 * e1 + e1; + e2 = e2 * e2 + e2; + } + check_equal(e1, e2); + // These are only discovered to be not equal way down the tree: + e2 = e2 * e2 + e2; + check_not_equal(e1, e2); + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/ir_printer.cpp b/test/correctness/ir_printer.cpp new file mode 100644 index 000000000000..21191992f2d5 --- /dev/null +++ b/test/correctness/ir_printer.cpp @@ -0,0 +1,59 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +int main(int argc, char **argv) { + Type i32 = Int(32); + Type f32 = Float(32); + Expr x = Variable::make(Int(32), "x"); + Expr y = Variable::make(Int(32), "y"); + std::ostringstream expr_source; + expr_source << (x + 3) * (y / 2 + 17); + internal_assert(expr_source.str() == "((x + 3)*((y/2) + 17))"); + + Stmt store = Store::make("buf", (x * 17) / (x - 3), y - 1, Parameter(), const_true(), ModulusRemainder()); + Stmt for_loop = For::make("x", -2, y + 2, ForType::Parallel, Partition::Auto, DeviceAPI::Host, store); + std::vector args(1); + args[0] = x % 3; + Expr call = Call::make(i32, "buf", args, Call::Extern); + Stmt store2 = Store::make("out", call + 1, x, Parameter(), const_true(), ModulusRemainder(3, 5)); + Stmt for_loop2 = For::make("x", 0, y, ForType::Vectorized, Partition::Auto, DeviceAPI::Host, store2); + + Stmt producer = ProducerConsumer::make_produce("buf", for_loop); + Stmt consumer = ProducerConsumer::make_consume("buf", for_loop2); + Stmt pipeline = Block::make(producer, consumer); + + Stmt assertion = AssertStmt::make(y >= 3, Call::make(Int(32), "halide_error_param_too_small_i64", + {std::string("y"), y, 3}, Call::Extern)); + Stmt block = Block::make(assertion, pipeline); + Stmt let_stmt = LetStmt::make("y", 17, block); + Stmt allocate = Allocate::make("buf", f32, MemoryType::Stack, {1023}, const_true(), let_stmt); + + std::ostringstream source; + source << allocate; + std::string correct_source = + "allocate buf[float32 * 1023] in Stack\n" + "let y = 17\n" + "assert(y >= 3, halide_error_param_too_small_i64(\"y\", y, 3))\n" + "produce buf {\n" + " parallel (x, -2, y + 2) {\n" + " buf[y - 1] = (x*17)/(x - 3)\n" + " }\n" + "}\n" + "consume buf {\n" + " vectorized (x, 0, y) {\n" + " out[x] = buf(x % 3) + 1\n" + " }\n" + "}\n"; + + if (source.str() != correct_source) { + internal_error << "Correct output:\n" + << correct_source + << "Actual output:\n" + << source.str(); + } + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/is_monotonic.cpp b/test/correctness/is_monotonic.cpp new file mode 100644 index 000000000000..51385e656b72 --- /dev/null +++ b/test/correctness/is_monotonic.cpp @@ -0,0 +1,101 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +namespace { + +void check_increasing(const Expr &e) { + internal_assert(is_monotonic(e, "x") == Monotonic::Increasing) + << "Was supposed to be increasing: " << e << "\n"; +} + +void check_decreasing(const Expr &e) { + internal_assert(is_monotonic(e, "x") == Monotonic::Decreasing) + << "Was supposed to be decreasing: " << e << "\n"; +} + +void check_constant(const Expr &e) { + internal_assert(is_monotonic(e, "x") == Monotonic::Constant) + << "Was supposed to be constant: " << e << "\n"; +} + +void check_unknown(const Expr &e) { + internal_assert(is_monotonic(e, "x") == Monotonic::Unknown) + << "Was supposed to be unknown: " << e << "\n"; +} + +} // namespace + +int main() { + Expr x = Variable::make(Int(32), "x"); + Expr y = Variable::make(Int(32), "y"); + Expr z = Variable::make(Int(32), "z"); + + check_increasing(x); + check_increasing(x + 4); + check_increasing(x + y); + check_increasing(x * 4); + check_increasing(x / 4); + check_increasing(min(x + 4, y + 4)); + check_increasing(max(x + y, x - y)); + check_increasing(x >= y); + check_increasing(x > y); + + check_decreasing(-x); + check_decreasing(x * -4); + check_decreasing(x / -4); + check_decreasing(y - x); + check_decreasing(x < y); + check_decreasing(x <= y); + + check_unknown(x == y); + check_unknown(x != y); + check_increasing(y <= x); + check_increasing(y < x); + check_decreasing(x <= y); + check_decreasing(x < y); + check_unknown(x * y); + + // Not constant despite having constant args, because there's a side-effect. + check_unknown(Call::make(Int(32), "foo", {Expr(3)}, Call::Extern)); + + check_increasing(select(y == 2, x, x + 4)); + check_decreasing(select(y == 2, -x, x * -4)); + + check_unknown(select(x > 2, x - 2, x)); + check_unknown(select(x < 2, x, x - 2)); + check_unknown(select(x > 2, -x + 2, -x)); + check_unknown(select(x < 2, -x, -x + 2)); + check_increasing(select(x > 2, x - 1, x)); + check_increasing(select(x < 2, x, x - 1)); + check_decreasing(select(x > 2, -x + 1, -x)); + check_decreasing(select(x < 2, -x, -x + 1)); + + check_unknown(select(x < 2, x, x - 5)); + check_unknown(select(x > 2, x - 5, x)); + + check_unknown(select(x > 0, y, z)); + + check_increasing(select(0 < x, promise_clamped(x - 1, x - 1, z) + 1, promise_clamped(x, x, z))); + + check_constant(y); + + check_increasing(select(x < 17, y, y + 1)); + check_increasing(select(x > 17, y, y - 1)); + check_decreasing(select(x < 17, y, y - 1)); + check_decreasing(select(x > 17, y, y + 1)); + + check_increasing(select(x % 2 == 0, x + 3, x + 3)); + + check_constant(select(y > 3, y + 23, y - 65)); + + check_decreasing(select(2 <= x, 0, 1)); + check_increasing(select(2 <= x, 0, 1) + x); + check_decreasing(-min(x, 16)); + + check_unknown(select(0 < x, max(min(x, 4), 3), 4)); + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/modulus_remainder.cpp b/test/correctness/modulus_remainder.cpp new file mode 100644 index 000000000000..a09c141f5299 --- /dev/null +++ b/test/correctness/modulus_remainder.cpp @@ -0,0 +1,39 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +namespace { + +void check(const Expr &e, int64_t m, int64_t r) { + ModulusRemainder result = modulus_remainder(e); + if (result.modulus != m || result.remainder != r) { + std::cerr << "Test failed for modulus_remainder:\n"; + std::cerr << "Expression: " << e << "\n"; + std::cerr << "Correct modulus, remainder = " << m << ", " << r << "\n"; + std::cerr << "Computed modulus, remainder = " + << result.modulus << ", " + << result.remainder << "\n"; + exit(1); + } +} + +} // namespace + +int main(int argc, char **argv) { + Expr x = Variable::make(Int(32), "x"); + Expr y = Variable::make(Int(32), "y"); + + check((30 * x + 3) + (40 * y + 2), 10, 5); + check((6 * x + 3) * (4 * y + 1), 2, 1); + check(max(30 * x - 24, 40 * y + 31), 5, 1); + check(10 * x - 33 * y, 1, 0); + check(10 * x - 35 * y, 5, 0); + check(123, 0, 123); + check(Let::make("y", x * 3 + 4, y * 3 + 4), 9, 7); + // Check overflow + check((5045320 * x + 4) * (405713 * y + 3) * (8000123 * x + 4354), 1, 0); + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/propagate_estimate.cpp b/test/correctness/propagate_estimate.cpp new file mode 100644 index 000000000000..997b5819927a --- /dev/null +++ b/test/correctness/propagate_estimate.cpp @@ -0,0 +1,37 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +namespace { + +void check(const Expr &input, Expr expected) { + Expr result = simplify(substitute_var_estimates(input)); + expected = simplify(expected); + if (!equal(result, expected)) { + internal_error + << "\nsubstitute_var_estimates() failure:\n" + << "Input: " << input << "\n" + << "Result: " << result << "\n" + << "Expected result: " << expected << "\n"; + } +} + +} // namespace + +int main(int argc, char **argv) { + Param p; + p.set_estimate(10); + + ImageParam img(Int(32), 2); + img.dim(0).set_estimate(-3, 33); + img.dim(1).set_estimate(5, 55); + + Var x("x"), y("y"); + check(p + x + y, x + y + 10); + check(img.dim(0).min() + img.dim(1).min() + x, x + 2); + check(img.dim(0).extent() + img.dim(1).min() + img.dim(1).extent() * x, 55 * x + 38); + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/spirv_ir.cpp b/test/correctness/spirv_ir.cpp new file mode 100644 index 000000000000..ca51dec28103 --- /dev/null +++ b/test/correctness/spirv_ir.cpp @@ -0,0 +1,59 @@ +#include "Halide.h" + +// SpirvIR.h is an internal-only header (not part of the public Halide.h +// umbrella) used to construct SPIR-V modules for the Vulkan backend. +#include "SpirvIR.h" + +using namespace Halide; +using namespace Halide::Internal; + +int main(int argc, char **argv) { + SpvBinary binary; + SpvInstruction label_inst = SpvFactory::label(777); + internal_assert(label_inst.result_id() == 777); + internal_assert(label_inst.op_code() == SpvOpLabel); + label_inst.encode(binary); + internal_assert(binary.size() == 2); // encodes to 2x 32-bit words [Length|OpCode, ResultId] + + SpvBuilder builder; + SpvId void_type_id = builder.reserve_id(SpvVoidTypeId); + SpvInstruction void_inst = SpvFactory::void_type(void_type_id); + builder.current_module().add_type(void_inst); + + SpvId int_type_id = builder.declare_type(Int(32)); + SpvId uint_type_id = builder.declare_type(UInt(32)); + SpvId float_type_id = builder.declare_type(Float(32)); + + SpvBuilder::ParamTypes param_types = {int_type_id, uint_type_id, float_type_id}; + SpvId kernel_func_id = builder.add_function("kernel_func", void_type_id, param_types); + SpvFunction kernel_func = builder.lookup_function(kernel_func_id); + + builder.enter_function(kernel_func); + SpvId intrinsic_type_id = builder.declare_type(Type(Type::UInt, 32, 3)); + SpvId intrinsic_id = builder.declare_global_variable("InputVar", intrinsic_type_id, SpvStorageClassInput); + + SpvId output_type_id = builder.declare_type(Type(Type::UInt, 32, 1)); + SpvId output_id = builder.declare_global_variable("OutputVar", output_type_id, SpvStorageClassOutput); + + SpvBuilder::Variables entry_point_variables = {intrinsic_id, output_id}; + builder.add_entry_point(kernel_func_id, SpvExecutionModelKernel, entry_point_variables); + + SpvBuilder::Literals annotation_literals = {SpvBuiltInWorkgroupId}; + builder.add_annotation(intrinsic_id, SpvDecorationBuiltIn, annotation_literals); + + SpvId intrinsic_loaded_id = builder.reserve_id(); + builder.append(SpvFactory::load(intrinsic_type_id, intrinsic_loaded_id, intrinsic_id)); + + float float_value = 32.0f; + SpvId float_src_id = builder.add_constant(Float(32), &float_value); + SpvId converted_value_id = builder.reserve_id(SpvResultId); + builder.append(SpvFactory::convert(SpvOpConvertFToU, uint_type_id, converted_value_id, float_src_id)); + builder.append(SpvFactory::store(output_id, converted_value_id)); + builder.leave_function(); + + binary.clear(); + builder.encode(binary); + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/split_predicate.cpp b/test/correctness/split_predicate.cpp new file mode 100644 index 000000000000..97332267981a --- /dev/null +++ b/test/correctness/split_predicate.cpp @@ -0,0 +1,87 @@ +#include "Halide.h" + +#include +#include + +using namespace Halide; +using namespace Halide::Internal; + +namespace { + +void check(const Expr &pred, std::vector &expected) { + std::vector result; + split_into_ands(pred, result); + bool is_equal = true; + + if (result.size() != expected.size()) { + is_equal = false; + } else { + for (size_t i = 0; i < expected.size(); ++i) { + if (!equal(simplify(result[i]), simplify(expected[i]))) { + is_equal = false; + break; + } + } + } + + if (!is_equal) { + std::cout << "Expect predicate " << pred << " to be split into:\n"; + for (const auto &e : expected) { + std::cout << " " << e << "\n"; + } + std::cout << "Got:\n"; + for (const auto &e : result) { + std::cout << " " << e << "\n"; + } + internal_error << "\n"; + } +} + +} // namespace + +int main() { + Expr x = Var("x"), y = Var("y"), z = Var("z"), w = Var("w"); + + { + std::vector expected; + expected.push_back(z < 10); + check(z < 10, expected); + } + + { + std::vector expected; + expected.push_back((x < y) || (x == 10)); + check((x < y) || (x == 10), expected); + } + + { + std::vector expected; + expected.push_back(x < y); + expected.push_back(x == 10); + check((x < y) && (x == 10), expected); + } + + { + std::vector expected; + expected.push_back(x < y); + expected.push_back(x == 10); + expected.push_back(y == z); + check((x < y) && (x == 10) && (y == z), expected); + } + + { + std::vector expected; + expected.push_back((w == 1) || ((x == 10) && (y == z))); + check((w == 1) || ((x == 10) && (y == z)), expected); + } + + { + std::vector expected; + expected.push_back(x < y); + expected.push_back((w == 1) || ((x == 10) && (y == z))); + check((x < y) && ((w == 1) || ((x == 10) && (y == z))), expected); + } + + printf("Success!\n"); + return 0; +} diff --git a/test/correctness/uniquify_variable_names.cpp b/test/correctness/uniquify_variable_names.cpp new file mode 100644 index 000000000000..879b6dd86044 --- /dev/null +++ b/test/correctness/uniquify_variable_names.cpp @@ -0,0 +1,85 @@ +#include "Halide.h" + +using namespace Halide; +using namespace Halide::Internal; + +namespace { +void check(std::vector> in, + std::vector> out) { + Stmt in_stmt = Evaluate::make(0), out_stmt = Evaluate::make(0); + for (const auto &[var, value] : reverse_view(in)) { + in_stmt = LetStmt::make(var.name(), value, in_stmt); + } + for (const auto &[var, value] : reverse_view(out)) { + out_stmt = LetStmt::make(var.name(), value, out_stmt); + } + + Stmt s = uniquify_variable_names(in_stmt); + + internal_assert(equal(s, out_stmt)) + << "Failure in uniquify_variable_names\n" + << "Input:\n" + << in_stmt << "\n" + << "Produced:\n" + << s << "\n" + << "Correct output:\n" + << out_stmt << "\n"; +} +} // namespace + +int main(int argc, char **argv) { + Var x("x"), x_1("x_1"), x_2("x_2"), x_3{"x_3"}; + Var y("y"), y_1("y_1"), y_2("y_2"), y_3{"y_3"}; + + // Stmts with all names already unique should be unchanged + check({{x, 3}, + {y, x}}, + {{x, 3}, + {y, x}}); + + // Shadowed definitions of Vars should be given unique names + check({{x, 3}, + {y, x}, + {x, x + y}, + {y, x + y}, + {x, x + y}, + {y, x + y}}, + {{x, 3}, + {y, x}, + {x_1, x + y}, + {y_1, x_1 + y}, + {x_2, x_1 + y_1}, + {y_2, x_2 + y_1}}); + + // Check a case with a free var after then end of the scope of a let of the same name + check({{x, Let::make(y.name(), 3, y)}, // y is bound + {x, y}}, // This is not the same y. It's free and can't be renamed. + {{x, Let::make(y_1.name(), 3, y_1)}, // We rename the bound one + {x_1, y}}); + + // An existing in-scope use of one of the names that would be + // autogenerated should be skipped over + check({{x_1, 8}, + {x, 3}, + {y, x}, + {x, x + y}, + {y, x + y}, + {x, x + y}, + {y, x + y}}, + {{x_1, 8}, + {x, 3}, + {y, x}, + {x_2, x + y}, + {y_1, x_2 + y}, + {x_3, x_2 + y_1}, + {y_2, x_3 + y_1}}); + + // Check parallel bindings. The scope doesn't overlap so they can keep their name + check({{x, Let::make(y.name(), 3, y)}, + {x, Let::make(y.name(), 4, y)}}, + {{x, Let::make(y.name(), 3, y)}, + {x_1, Let::make(y.name(), 4, y)}}); + + printf("Success!\n"); + return 0; +} diff --git a/test/internal.cpp b/test/internal.cpp deleted file mode 100644 index ddef5f95fa74..000000000000 --- a/test/internal.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "Associativity.h" -#include "AutoScheduleUtils.h" -#include "Bounds.h" -#include "CPlusPlusMangle.h" -#include "CSE.h" -#include "CodeGen_C.h" -#include "Deinterleave.h" -#include "Func.h" -#include "Generator.h" -#include "IR.h" -#include "IREquality.h" -#include "IRMatch.h" -#include "IRPrinter.h" -#include "Interval.h" -#include "ModulusRemainder.h" -#include "Monotonic.h" -#include "Reduction.h" -#include "Solve.h" -#include "SpirvIR.h" -#include "UniquifyVariableNames.h" - -using namespace Halide; -using namespace Halide::Internal; - -int main(int argc, const char **argv) { - IRPrinter::test(); - CodeGen_C::test(); - ir_equality_test(); - bounds_test(); - expr_match_test(); - deinterleave_vector_test(); - modulus_remainder_test(); - cse_test(); - cplusplus_mangle_test(); - is_monotonic_test(); - split_predicate_test(); - associativity_test(); - generator_test(); - propagate_estimate_test(); - uniquify_variable_names_test(); - spirv_ir_test(); - - printf("Success!\n"); - return 0; -} diff --git a/test/pch_helper.cpp b/test/pch_helper.cpp new file mode 100644 index 000000000000..f45ef339daf9 --- /dev/null +++ b/test/pch_helper.cpp @@ -0,0 +1,8 @@ +// This translation unit exists only so that other test executables can +// reuse its precompiled header via target_precompile_headers's +// REUSE_FROM option (see test/CMakeLists.txt and cmake/HalideTestHelpers.cmake). +#include "Halide.h" + +int main() { + return 0; +} From 5da585ea653572054b509ccd1bafeec5252623e1 Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Tue, 4 Aug 2026 16:59:51 -0400 Subject: [PATCH 2/3] Apply set_halide_compiler_warnings to _test_internal PCH donor _test_internal no longer runs through add_halide_test(), so it stopped getting the same warning flags as the test targets that reuse its precompiled header via REUSE_FROM. MSVC treats a warning-level mismatch between a PCH and its consumer as an error under /WX (C4652). Co-Authored-By: Claude Sonnet 5 --- test/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ac271532b281..99f7169d6328 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,6 +8,12 @@ include(CheckCXXCompilerFlag) add_executable(_test_internal EXCLUDE_FROM_ALL pch_helper.cpp) target_link_libraries(_test_internal PRIVATE Halide::Test Halide::TerminateHandler) target_precompile_headers(_test_internal PRIVATE ) +if (NOT Halide_TARGET MATCHES "wasm") + # The PCH must be compiled with the same warning flags as the targets that + # reuse it (via REUSE_FROM) -- MSVC in particular treats a mismatched + # warning level as an error under /WX. + set_halide_compiler_warnings(_test_internal) +endif () if (Halide_CCACHE_BUILD) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") target_compile_options( From f00a9c729e65d8da38f6775f6f12d56b224cb33c Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Wed, 5 Aug 2026 14:00:32 -0400 Subject: [PATCH 3/3] Update ported internal tests for Load/Store::make short forms PR #9247 (merged into main) removed the default is_streaming argument from Load::make/Store::make and added short-form overloads instead. The tests recently ported out of src/*.cpp into test/correctness still used the old call forms, which no longer compiled after merging main. --- test/correctness/bounds_internal.cpp | 12 ++++++------ test/correctness/codegen_c.cpp | 2 +- test/correctness/cse.cpp | 16 ++++++++-------- test/correctness/deinterleave_vector.cpp | 6 +++--- test/correctness/ir_printer.cpp | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/test/correctness/bounds_internal.cpp b/test/correctness/bounds_internal.cpp index 81b0667ae66c..622922bed7a3 100644 --- a/test/correctness/bounds_internal.cpp +++ b/test/correctness/bounds_internal.cpp @@ -134,7 +134,7 @@ void constant_bound_test() { check_constant_bound(i16(absd(cr, cl)), Expr((int16_t)0), Expr((int16_t)509)); } - check_constant_bound(Load::make(Int(32), "buf", 0, Buffer<>(), Parameter(), const_true(), ModulusRemainder()) * 20, + check_constant_bound(Load::make(Int(32), "buf", 0) * 20, Interval::neg_inf(), Interval::pos_inf()); { @@ -231,7 +231,7 @@ int main() { check(scope, x * y, min(y, 0) * 10, max(y, 0) * 10); check(scope, x / (x + y), -10, 10); check(scope, 11 / (x + 1), 1, 11); - check(scope, Load::make(Int(8), "buf", x, Buffer<>(), Parameter(), const_true(), ModulusRemainder()), + check(scope, Load::make(Int(8), "buf", x), i8(-128), i8(127)); check(scope, y + (Let::make("y", x + 3, y - x + 10)), y + 3, y + 23); // Once again, we don't know that y is correlated with x check(scope, clamp(1000 / (x - 2), x - 10, x + 10), -10, 20); @@ -393,8 +393,8 @@ int main() { cast(clamp(cast(x ^ y), make_zero(UInt(16)), make_const(UInt(16), 128))), u8(0), u8(128)); - Expr u8_1 = cast(Load::make(Int(8), "buf", x, Buffer<>(), Parameter(), const_true(), ModulusRemainder())); - Expr u8_2 = cast(Load::make(Int(8), "buf", x + 17, Buffer<>(), Parameter(), const_true(), ModulusRemainder())); + Expr u8_1 = cast(Load::make(Int(8), "buf", x)); + Expr u8_2 = cast(Load::make(Int(8), "buf", x + 17)); check(scope, cast(u8_1) + cast(u8_2), u16(0), u16(255 * 2)); @@ -516,7 +516,7 @@ int main() { // Test case from https://github.com/halide/Halide/pull/7377 { Var x; - Expr e = Load::make(Int(32), "buf", max(x, -x), Buffer<>{}, Parameter{}, const_true(), ModulusRemainder{}); + Expr e = Load::make(Int(32), "buf", max(x, -x), Buffer<>{}, Parameter{}, const_true(), ModulusRemainder{}, false); e = Let::make(x.name(), 37, e); Scope scope; scope.push("y", {0, 100}); @@ -527,7 +527,7 @@ int main() { // Test case from https://github.com/halide/Halide/pull/7379 { Var x; - Expr e = Load::make(Int(32), "buf", -x / x, Buffer<>{}, Parameter{}, const_true(), ModulusRemainder{}); + Expr e = Load::make(Int(32), "buf", -x / x, Buffer<>{}, Parameter{}, const_true(), ModulusRemainder{}, false); e = Let::make(x.name(), 37, e); Scope scope; scope.push("y", {0, 100}); diff --git a/test/correctness/codegen_c.cpp b/test/correctness/codegen_c.cpp index 481f4ea6b9fc..724fe6049f65 100644 --- a/test/correctness/codegen_c.cpp +++ b/test/correctness/codegen_c.cpp @@ -53,7 +53,7 @@ int main(int argc, char **argv) { Param alpha("alpha"); Param beta("beta"); Expr e = Select::make(alpha > 4.0f, print_when(x < 1, 3), 2); - Stmt s = Store::make("buf", e, x, Parameter(), const_true(), ModulusRemainder()); + Stmt s = Store::make("buf", e, x); s = LetStmt::make("x", beta + 1, s); s = Block::make(s, Free::make("tmp.stack")); s = Allocate::make("tmp.stack", Int(32), MemoryType::Stack, {127}, const_true(), s); diff --git a/test/correctness/cse.cpp b/test/correctness/cse.cpp index fd5fb0322595..edc724529ca0 100644 --- a/test/correctness/cse.cpp +++ b/test/correctness/cse.cpp @@ -130,13 +130,13 @@ int main(int argc, char **argv) { { Expr pred = x * x + y * y > 0; Expr index = select(x * x + y * y > 0, x * x + y * y + 2, x * x + y * y + 10); - Expr load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), const_true(), ModulusRemainder()); - Expr pred_load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), pred, ModulusRemainder()); + Expr load = Load::make(Int(32), "buf", index); + Expr pred_load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), pred, ModulusRemainder(), false); e = select(x * y > 10, x * y + 2, x * y + 3 + load) + pred_load; Expr t2 = Variable::make(Bool(), "t2"); - Expr cse_load = Load::make(Int(32), "buf", t[3], Buffer<>(), Parameter(), const_true(), ModulusRemainder()); - Expr cse_pred_load = Load::make(Int(32), "buf", t[3], Buffer<>(), Parameter(), t2, ModulusRemainder()); + Expr cse_load = Load::make(Int(32), "buf", t[3]); + Expr cse_pred_load = Load::make(Int(32), "buf", t[3], Buffer<>(), Parameter(), t2, ModulusRemainder(), false); correct = ssa_block({x * y, x * x + y * y, t[1] > 0, @@ -149,13 +149,13 @@ int main(int argc, char **argv) { { Expr pred = x * x + y * y > 0; Expr index = select(x * x + y * y > 0, x * x + y * y + 2, x * x + y * y + 10); - Expr load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), const_true(), ModulusRemainder()); - Expr pred_load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), pred, ModulusRemainder()); + Expr load = Load::make(Int(32), "buf", index); + Expr pred_load = Load::make(Int(32), "buf", index, Buffer<>(), Parameter(), pred, ModulusRemainder(), false); e = select(x * y > 10, x * y + 2, x * y + 3 + pred_load) + pred_load; Expr t2 = Variable::make(Bool(), "t2"); - Expr cse_load = Load::make(Int(32), "buf", select(t2, t[1] + 2, t[1] + 10), Buffer<>(), Parameter(), const_true(), ModulusRemainder()); - Expr cse_pred_load = Load::make(Int(32), "buf", select(t2, t[1] + 2, t[1] + 10), Buffer<>(), Parameter(), t2, ModulusRemainder()); + Expr cse_load = Load::make(Int(32), "buf", select(t2, t[1] + 2, t[1] + 10)); + Expr cse_pred_load = Load::make(Int(32), "buf", select(t2, t[1] + 2, t[1] + 10), Buffer<>(), Parameter(), t2, ModulusRemainder(), false); correct = ssa_block({x * y, x * x + y * y, t[1] > 0, diff --git a/test/correctness/deinterleave_vector.cpp b/test/correctness/deinterleave_vector.cpp index 25106cd46c8f..58535d84acd5 100644 --- a/test/correctness/deinterleave_vector.cpp +++ b/test/correctness/deinterleave_vector.cpp @@ -30,9 +30,9 @@ int main(int argc, char **argv) { check(ramp, ramp_a, ramp_b); check(broadcast, broadcast_a, broadcast_b); - check(Load::make(ramp.type(), "buf", ramp, Buffer<>(), Parameter(), const_true(ramp.type().lanes()), ModulusRemainder()), - Load::make(ramp_a.type(), "buf", ramp_a, Buffer<>(), Parameter(), const_true(ramp_a.type().lanes()), ModulusRemainder()), - Load::make(ramp_b.type(), "buf", ramp_b, Buffer<>(), Parameter(), const_true(ramp_b.type().lanes()), ModulusRemainder())); + check(Load::make(ramp.type(), "buf", ramp), + Load::make(ramp_a.type(), "buf", ramp_a), + Load::make(ramp_b.type(), "buf", ramp_b)); Expr vec_x = Variable::make(Int(32, 4), "vec_x"); Expr vec_y = Variable::make(Int(32, 4), "vec_y"); diff --git a/test/correctness/ir_printer.cpp b/test/correctness/ir_printer.cpp index 21191992f2d5..c205d25098f2 100644 --- a/test/correctness/ir_printer.cpp +++ b/test/correctness/ir_printer.cpp @@ -12,12 +12,12 @@ int main(int argc, char **argv) { expr_source << (x + 3) * (y / 2 + 17); internal_assert(expr_source.str() == "((x + 3)*((y/2) + 17))"); - Stmt store = Store::make("buf", (x * 17) / (x - 3), y - 1, Parameter(), const_true(), ModulusRemainder()); + Stmt store = Store::make("buf", (x * 17) / (x - 3), y - 1); Stmt for_loop = For::make("x", -2, y + 2, ForType::Parallel, Partition::Auto, DeviceAPI::Host, store); std::vector args(1); args[0] = x % 3; Expr call = Call::make(i32, "buf", args, Call::Extern); - Stmt store2 = Store::make("out", call + 1, x, Parameter(), const_true(), ModulusRemainder(3, 5)); + Stmt store2 = Store::make("out", call + 1, x, Parameter(), const_true(), ModulusRemainder(3, 5), false); Stmt for_loop2 = For::make("x", 0, y, ForType::Vectorized, Partition::Auto, DeviceAPI::Host, store2); Stmt producer = ProducerConsumer::make_produce("buf", for_loop);