Skip to content

Bounds and alignment analysis through bitwise ops#8574

Merged
abadams merged 17 commits intomainfrom
abadams/bits_known
Jul 4, 2025
Merged

Bounds and alignment analysis through bitwise ops#8574
abadams merged 17 commits intomainfrom
abadams/bits_known

Conversation

@abadams
Copy link
Copy Markdown
Member

@abadams abadams commented Feb 13, 2025

I encountered a case in production where slow code was being generated because Halide didn't recognize (x + 15) & ~15 - y & ~15 as a multiple of 16, so it failed to vectorize an atomic because a GuardWithIf if statement got in the way. This was a new issue for Halide 19, because previously the GuardWithIf if statement was incorrectly being dropped by a now-fixed bug in rfactor.

This PR adds support for constant bounds analysis and alignment analysis in the simplifier for bitwise ops. It does this by converting ExprInfo to a more LLVM-style BitsKnown structure temporarily.

@abadams abadams requested a review from rootjalex February 13, 2025 18:05
Comment thread src/Simplify.cpp
result.mask |= (uint64_t)(-1) << (type.bits() - 1);
} else if (type.bits() < 64) {
// Narrow uints are zero-extended.
result.mask |= (uint64_t)(-1) << type.bits();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this true regardless of the bounds?

Comment thread src/Simplify.cpp
return is_const_one(e);
}

Simplify::ExprInfo::BitsKnown Simplify::ExprInfo::to_bits_known(const Type &type) const {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of this bitwise math is a little tricky to follow. Have you thrown this in an SMT solver? I think this should be verified

Comment thread src/Simplify.cpp
Comment thread src/Simplify_Call.cpp Outdated
if (info && (op->type.is_int() || op->type.is_uint())) {
auto bits_known = a_info.to_bits_known(op->type) & b_info.to_bits_known(op->type);
info->from_bits_known(bits_known, op->type);
if (bits_known.mask == (uint64_t)-1) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment saying something like "This is a constant". Or make ExprInfo::BitsKnown have a helper function like "std::optional<uint64_t> as_constant()`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and same below with bitwise_or

Comment thread src/Simplify_Call.cpp
Comment thread src/Simplify_Call.cpp

if (info && (op->type.is_int() || op->type.is_uint())) {
auto bits_known = a_info.to_bits_known(op->type) ^ b_info.to_bits_known(op->type);
info->from_bits_known(bits_known, op->type);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, check for a constant?

Comment thread src/Simplify_Internal.h Outdated
}

BitsKnown operator|(const BitsKnown &other) const {
uint64_t zeros = known_zeros() & other.known_zeros();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment like in the & case

Comment thread src/Simplify_Select.cpp Outdated
Some fuzzing with extra checks turned on revealed that the simplifier
would often produce constants without the ExprInfo knowing they were
constants. In a few places we were doing a janky remutation of a known
constant just to set the info, but this wasn't being consistently done.
This commit changes constant-folding in the simplifier and info handling
in boolean ops to know that it's a constant value being returned.

Also standardize the spelling of zeros
@abadams
Copy link
Copy Markdown
Member Author

abadams commented Jun 9, 2025

I delayed this until after the siggraph asia deadline, but now that it has passed, ptal

Comment thread src/Simplify.cpp Outdated
Comment thread test/correctness/bits_known.cpp Outdated
Comment on lines +29 to +30
// op. Currently just known the second-lowest-bit is 2 but nothing else
// doesn't give us an alignment or bounds.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am struggling to follow the grammar of this sentence

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me too! I'll try to reconstruct what I meant.

Copy link
Copy Markdown
Member

@rootjalex rootjalex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm with nits

@abadams
Copy link
Copy Markdown
Member Author

abadams commented Jul 4, 2025

Failures unrelated

@abadams abadams merged commit 7bb9947 into main Jul 4, 2025
17 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants