From 89ee30a14472901edac9d89dcc69d565c30b74f3 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 30 Mar 2024 19:06:39 -0700 Subject: [PATCH 1/3] Tighten bounds of abs() --- dependencies/llvm/CMakeLists.txt | 2 +- src/Bounds.cpp | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dependencies/llvm/CMakeLists.txt b/dependencies/llvm/CMakeLists.txt index a4aef94b08de..d070caf53b19 100644 --- a/dependencies/llvm/CMakeLists.txt +++ b/dependencies/llvm/CMakeLists.txt @@ -21,7 +21,7 @@ message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") message(STATUS "Using ClangConfig.cmake in: ${Clang_DIR}") if (LLVM_PACKAGE_VERSION VERSION_LESS 16.0) - message(FATAL_ERROR "LLVM version must be 15.0 or newer") + message(FATAL_ERROR "LLVM version must be 16.0 or newer") endif () if (LLVM_PACKAGE_VERSION VERSION_GREATER 19.0) diff --git a/src/Bounds.cpp b/src/Bounds.cpp index 16fd69f3e8fb..423c0bacd6fa 100644 --- a/src/Bounds.cpp +++ b/src/Bounds.cpp @@ -1237,12 +1237,19 @@ class Bounds : public IRVisitor { if (op->is_intrinsic(Call::abs)) { Interval a = arg_bounds.get(0); - interval.min = make_zero(t); + + if (a.has_lower_bound()) { + interval.min = cast(t, Max::make(make_zero(a.min.type()), a.min)); + } else { + interval.min = make_zero(t); + } + if (a.is_bounded()) { if (equal(a.min, a.max)) { interval = Interval::single_point(Call::make(t, Call::abs, {a.max}, Call::PureIntrinsic)); } else if (op->args[0].type().is_int() && op->args[0].type().bits() >= 32) { - interval.max = Max::make(Cast::make(t, -a.min), Cast::make(t, a.max)); + interval.min = Cast::make(t, Max::make(make_zero(a.min.type()), Max::make(a.min, -a.max))); + interval.max = Cast::make(t, Max::make(-a.min, a.max)); } else { a.min = Call::make(t, Call::abs, {a.min}, Call::PureIntrinsic); a.max = Call::make(t, Call::abs, {a.max}, Call::PureIntrinsic); @@ -3651,6 +3658,11 @@ void bounds_test() { 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) - 5), 0.f, 5.f); // Check some vectors check(scope, Ramp::make(x * 2, 5, 5), 0, 40); From a2827466e77ada4492b678e3f9409ce8cf264133 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 30 Mar 2024 19:31:27 -0700 Subject: [PATCH 2/3] make abs bounds tight for non-int32 too --- src/Bounds.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Bounds.cpp b/src/Bounds.cpp index 423c0bacd6fa..ff5762dde6b2 100644 --- a/src/Bounds.cpp +++ b/src/Bounds.cpp @@ -1238,12 +1238,6 @@ class Bounds : public IRVisitor { if (op->is_intrinsic(Call::abs)) { Interval a = arg_bounds.get(0); - if (a.has_lower_bound()) { - interval.min = cast(t, Max::make(make_zero(a.min.type()), a.min)); - } else { - interval.min = make_zero(t); - } - if (a.is_bounded()) { if (equal(a.min, a.max)) { interval = Interval::single_point(Call::make(t, Call::abs, {a.max}, Call::PureIntrinsic)); @@ -1251,11 +1245,21 @@ class Bounds : public IRVisitor { interval.min = Cast::make(t, Max::make(make_zero(a.min.type()), Max::make(a.min, -a.max))); interval.max = Cast::make(t, Max::make(-a.min, a.max)); } else { + interval.min = Cast::make(t, Max::make(a.min, -Min::make(make_zero(a.min.type()), a.max))); a.min = Call::make(t, Call::abs, {a.min}, Call::PureIntrinsic); a.max = Call::make(t, Call::abs, {a.max}, Call::PureIntrinsic); interval.max = Max::make(a.min, a.max); } } else { + if (a.has_lower_bound()) { + // If a is strictly positive, then abs(a) is strictly positive. + interval.min = Cast::make(t, Max::make(make_zero(a.min.type()), a.min)); + } else if (a.has_upper_bound()) { + // If a is strictly negative, then abs(a) is strictly positive. + interval.min = Cast::make(t, -Min::make(make_zero(a.max.type()), a.max)); + } else { + interval.min = make_zero(t); + } // If the argument is unbounded on one side, then the max is unbounded. interval.max = Interval::pos_inf(); } @@ -3662,7 +3666,17 @@ void bounds_test() { 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); From f74dc302af0a8bf1151a5617a4b192d7204f8b57 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 30 Mar 2024 19:34:34 -0700 Subject: [PATCH 3/3] make int32 min expression match non-int32 min expression --- src/Bounds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bounds.cpp b/src/Bounds.cpp index ff5762dde6b2..d7d337dacfdf 100644 --- a/src/Bounds.cpp +++ b/src/Bounds.cpp @@ -1242,7 +1242,7 @@ class Bounds : public IRVisitor { if (equal(a.min, a.max)) { interval = Interval::single_point(Call::make(t, Call::abs, {a.max}, Call::PureIntrinsic)); } else if (op->args[0].type().is_int() && op->args[0].type().bits() >= 32) { - interval.min = Cast::make(t, Max::make(make_zero(a.min.type()), Max::make(a.min, -a.max))); + interval.min = Cast::make(t, Max::make(a.min, -Min::make(make_zero(a.min.type()), a.max))); interval.max = Cast::make(t, Max::make(-a.min, a.max)); } else { interval.min = Cast::make(t, Max::make(a.min, -Min::make(make_zero(a.min.type()), a.max)));