Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ xcuserdata
# NeoVim + clangd
.cache

# CCLS
.ccls-cache

# Emacs
tags
TAGS
Expand Down
5 changes: 3 additions & 2 deletions python_bindings/test/correctness/basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,13 @@ def test_requirements():
delta.set(1)
p.realize([10])

with assert_throws(hl.HalideError, r"Requirement Failed: \(false\)"):
with assert_throws(hl.HalideError, r"Requirement Failed: \(\(delta != 0\)\)"):
delta.set(0)
p.realize([10])

with assert_throws(
hl.HalideError, r"Requirement Failed: \(false\) negative values are bad -1"
hl.HalideError,
r"Requirement Failed: \(\(delta > 0\)\) negative values are bad -1",
):
delta.set(-1)
p.realize([10])
Expand Down
21 changes: 18 additions & 3 deletions src/BoundsInference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1317,22 +1317,37 @@ Stmt bounds_inference(Stmt s,
fused_pairs_in_groups.push_back(pairs);
}

// Walk past a block of AssertStmts, which are the user-defined requirements.
// We want to use those to further simplify the image_checks which are injected later,
// by moving the inject-markers beyond those asserts.
std::vector<Stmt> stmts;
while (const Block *rb = s.as<Block>()) {
if (const auto *a = rb->first.as<AssertStmt>()) {
stmts.emplace_back(a);
s = rb->rest;
} else {
break;
}
}

// Add a note in the IR for where the outermost dynamic-stage skipping
// checks should go. These are injected in a later pass.
Expr marker = Call::make(Int(32), Call::skip_stages_marker, {}, Call::Intrinsic);
s = Block::make(Evaluate::make(marker), s);
stmts.push_back(Evaluate::make(marker));

if (target.has_feature(Target::Profile) || target.has_feature(Target::ProfileByTimer)) {
// Add a note in the IR for what profiling should cover, so that it doesn't
// include bounds queries as pipeline executions.
marker = Call::make(Int(32), Call::profiling_enable_instance_marker, {}, Call::Intrinsic);
s = Block::make(Evaluate::make(marker), s);
stmts.push_back(Evaluate::make(marker));
}

// Add a note in the IR for where assertions on input images
// should go. Those are handled by a later lowering pass.
marker = Call::make(Int(32), Call::add_image_checks_marker, {}, Call::Intrinsic);
s = Block::make(Evaluate::make(marker), s);
stmts.push_back(Evaluate::make(marker));
stmts.push_back(std::move(s));
s = Block::make(stmts);

// Add a synthetic outermost loop to act as 'root'.
s = For::make("<outermost>", 0, 0, ForType::Serial, Partition::Never, DeviceAPI::None, s);
Expand Down
4 changes: 3 additions & 1 deletion src/IROperator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1136,10 +1136,12 @@
return e;
}

Expr requirement_failed_error(Expr condition, const std::vector<Expr> &args) {

Check failure on line 1139 in src/IROperator.cpp

View workflow job for this annotation

GitHub Actions / Check clang-tidy

performance-unnecessary-value-param,-warnings-as-errors

the parameter 'condition' is copied for each invocation but only used as a const reference; consider making it a const reference
std::stringstream cond_str;
cond_str << condition;
return Call::make(Int(32),
"halide_error_requirement_failed",
{stringify({std::move(condition)}), combine_strings(args)},
{cond_str.str(), combine_strings(args)},
Call::Extern);
}

Expand Down
75 changes: 47 additions & 28 deletions test/correctness/vector_store_alignment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class CheckVectorStoreAlignment : public IRVisitor {
using IRVisitor::visit;

void visit(const Store *op) override {
if (op->name == "output" && op->value.type().lanes() == 32) {
if (starts_with(op->name, "output") && op->value.type().lanes() == 32) {
found_vector_store = true;
all_vector_stores_aligned &= op->alignment.modulus % 32 == 0 &&
op->alignment.remainder % 32 == 0;
Expand All @@ -25,39 +25,58 @@ class CheckVectorStoreAlignment : public IRVisitor {
} // namespace

int main(int argc, char **argv) {
Var x{"x"}, y{"y"}, xi{"xi"}, yi{"yi"};
ImageParam input{UInt(16), 2, "input"};
Target target = get_jit_target_from_environment()
//.with_feature(Target::NoAsserts)
.with_feature(Target::NoBoundsQuery)
.with_feature(Target::NoRuntime);

Func output{"output"};
output(x, y) = input(x, y);
// We'll run the test twice, but with a different way of expressing
// the dense storage constraints:
// - once with dim(1).set_stride(dim(0).extent())
// - once with add_requirement(dim(1).stride() == dim(0).extent())
for (int i = 0; i < 2; ++i) {
Var x{"x"}, y{"y"}, xi{"xi"}, yi{"yi"};
ImageParam input{UInt(16), 2, "input"};

output.align_extent(x, 32)
.tile(x, y, xi, yi, 32, 4)
.vectorize(xi);
Func output{"output"};
output(x, y) = input(x, y);

output.output_buffer().dim(0).set_min(0);
output.output_buffer().dim(1).set_min(0);
output.output_buffer().dim(1).set_stride(output.output_buffer().dim(0).extent());
output.output_buffer().set_host_alignment(64);
output.align_extent(x, 32)
.align_extent(y, 4)
.tile(x, y, xi, yi, 32, 4)
.vectorize(xi);

Target target = get_jit_target_from_environment()
.with_feature(Target::NoAsserts)
.with_feature(Target::NoBoundsQuery)
.with_feature(Target::NoRuntime);
Module module = output.compile_to_module({input}, "vector_store_alignment", target);
output.output_buffer().set_host_alignment(64);
if (i == 0) {
printf("Testing Dimension setting tricks\n");
output.output_buffer().dim(0).set_min(0);
output.output_buffer().dim(1).set_min(0);
output.output_buffer().dim(1).set_stride(output.output_buffer().dim(0).extent());
}

CheckVectorStoreAlignment checker;
for (const LoweredFunc &f : module.functions()) {
f.body.accept(&checker);
}
Pipeline p(output);
if (i == 1) {
printf("Testing add_requirements\n");
p.add_requirement(output.output_buffer().dim(0).min() == 0);
p.add_requirement(output.output_buffer().dim(1).min() == 0);
p.add_requirement(output.output_buffer().dim(1).stride() == output.output_buffer().dim(0).extent());
}

if (!checker.found_vector_store) {
printf("Did not find the vectorized output store.\n");
return 1;
}
if (!checker.all_vector_stores_aligned) {
printf("The vectorized output store was not known to be aligned by 32 elements.\n");
return 1;
Module module = p.compile_to_module({input}, "vector_store_alignment", target);

CheckVectorStoreAlignment checker;
for (const LoweredFunc &f : module.functions()) {
f.body.accept(&checker);
}

if (!checker.found_vector_store) {
printf("Did not find the vectorized output store.\n");
return 1;
}
if (!checker.all_vector_stores_aligned) {
printf("The vectorized output store was not known to be aligned by 32 elements.\n");
return 1;
}
}

printf("Success!\n");
Expand Down
26 changes: 18 additions & 8 deletions test/generator/error_codes_aottest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@

#include "error_codes.h"

bool print = false;

void my_halide_error(void *user_context, const char *msg) {
// Silently drop the error
// printf("%s\n", msg);
if (print) {
printf("%s\n", msg);
}
}

void check(int result, int correct) {
if (result != correct) {
printf("The exit status was %d instead of %d\n", result, correct);
exit(1);
} else {
if (print) {
printf("Found expected error: %d\n\n", correct);
}
}
}

Expand All @@ -23,17 +31,18 @@ int main(int argc, char **argv) {

halide_buffer_t in = {0}, out = {0};
halide_dimension_t shape[] = {{0, 64, 1},
{0, 123, 64}};
{0, 123, 64},
{0, 4, 64 * 123}};

in.host = (uint8_t *)malloc(64 * 123 * 4);
in.host = (uint8_t *)malloc(64 * 123 * sizeof(int));
in.type = halide_type_of<int>();
in.dim = shape;
in.dimensions = 2;

out.host = (uint8_t *)malloc(64 * 123 * 4);
out.host = (uint8_t *)malloc(64 * 123 * 4 * sizeof(int));
out.type = halide_type_of<int>();
out.dim = shape;
out.dimensions = 2;
out.dimensions = 3;

// First, a successful run.
int result = error_codes(&in, 64, &out);
Expand All @@ -58,7 +67,8 @@ int main(int argc, char **argv) {
// buffer extent negative, but in a way that doesn't trigger oob checks
{
halide_dimension_t bad_shape[] = {{0, 64, 1},
{0, -123, 64}};
{0, 123, 64},
{0, -4, 64 * 123}};
halide_buffer_t i = in, o = out;
i.dim = bad_shape;
o.dim = bad_shape;
Expand All @@ -69,8 +79,8 @@ int main(int argc, char **argv) {
}

// Input buffer larger than 2GB
halide_dimension_t huge[] = {{0, 10000000, 1},
{0, 10000000, 64}};
halide_dimension_t huge[] = {{0, 1 << 30, 1},
{0, 123, 4}};
in.dim = huge;
result = error_codes(&in, 64, &out);
correct = halide_error_code_buffer_extents_too_large;
Expand Down
6 changes: 3 additions & 3 deletions test/generator/error_codes_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ class ErrorCodes : public Halide::Generator<ErrorCodes> {
public:
Input<Buffer<int32_t, 2>> input{"input"};
Input<int> f_explicit_bound{"f_explicit_bound", 1, 0, 64};
Output<Buffer<int32_t, 2>> output{"output"};
Output<Buffer<int32_t, 3>> output{"output"};

void generate() {
assert(!get_target().has_feature(Target::LargeBuffers));
Var x, y;
Var x, y, z;

output(x, y) = input(x, y);
output(x, y, z) = input(x, y);
output.bound(x, 0, f_explicit_bound);

add_requirement(input.dim(1).extent() == 123);
Expand Down
Loading