This is an archive of the discontinued LLVM Phabricator instance.

[ValueTracking] Add additional cases for `isKnownNonZero(mul X, Y)`
ClosedPublic

Authored by goldstein.w.n on Apr 27 2023, 11:21 PM.

Details

Summary

If either X or Y is odd and the other is non-zero, the result is
non-zero.

Alive2 Link:

https://alive2.llvm.org/ce/z/9V7-es

Diff Detail

Event Timeline

goldstein.w.n created this revision.Apr 27 2023, 11:21 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 27 2023, 11:21 PM
Herald added a subscriber: hiraditya. · View Herald Transcript
goldstein.w.n requested review of this revision.Apr 27 2023, 11:21 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 27 2023, 11:21 PM
nikic added inline comments.Apr 29 2023, 10:24 AM
llvm/lib/Analysis/ValueTracking.cpp
2875–2892

Similar to the add case, we should compute mul known bits here based on the already computed XKnown/YKnown, rather than computing them again via the fallback.

I think just using KnownBits::mul should be enough, as the NSW case handled by computeKnownBitsMul is already handled better above.

Make it so we don't need to fallback to generic case

goldstein.w.n marked an inline comment as done.Apr 29 2023, 11:45 AM
goldstein.w.n added inline comments.
llvm/lib/Analysis/ValueTracking.cpp
2875–2892

Similar to the add case, we should compute mul known bits here based on the already computed XKnown/YKnown, rather than computing them again via the fallback.

I think just using KnownBits::mul should be enough, as the NSW case handled by computeKnownBitsMul is already handled better above.

Added the actual logic b.c there is a trick for knowing the sign of the result that doesn't appear to be handled by KnownBits::mul. Also seems more future-proof incase someone has a new bright idea and improves it further.

But we don't recompute knownbits now :)

nikic added inline comments.Apr 29 2023, 12:23 PM
llvm/lib/Analysis/ValueTracking.cpp
2875–2892

But isn't all the sign bit logic behind if (NSW), which we already handle separately?

nikic added inline comments.Apr 29 2023, 12:37 PM
llvm/lib/Analysis/ValueTracking.cpp
2875–2892

I mainly find the computeKnownBitsMulDoMul() function pretty ugly and would prefer to avoid it :) KnownBits::mul should handle anything that is not based on nowrap flags, and we handle nowrap flags ourselves.

goldstein.w.n marked an inline comment as done.

use KnownBits::mul instead of refactoring and using computeKnownBitsMul

goldstein.w.n marked 2 inline comments as done.Apr 29 2023, 3:12 PM
goldstein.w.n added inline comments.
llvm/lib/Analysis/ValueTracking.cpp
2875–2892

You're right no difference. Thanks for all the reviews on this very long series :)

thinkwe are in the home stretch now

nikic added inline comments.Apr 30 2023, 12:51 AM
llvm/lib/Analysis/ValueTracking.cpp
2897

You can drop the SelfMultiply handling as well. It only determines that the low bit is zero, which is not useful if we want a non-zero result.

goldstein.w.n marked an inline comment as done.

Don't both with selfmultiply

goldstein.w.n marked an inline comment as done.Apr 30 2023, 7:27 AM
nikic accepted this revision.Apr 30 2023, 7:28 AM

LGTM

This revision is now accepted and ready to land.Apr 30 2023, 7:28 AM
This revision was landed with ongoing or failed builds.Apr 30 2023, 8:08 AM
This revision was automatically updated to reflect the committed changes.

I'm probably missing something obvious here, but what does it matter that one of the operands is odd? If we are multiplying two numbers where one is odd or even (as long as its non-zero) and the other is non-zero, the result will be non-zero, no? I can't figure out what's special about odd in this context.

I'm probably missing something obvious here, but what does it matter that one of the operands is odd? If we are multiplying two numbers where one is odd or even (as long as its non-zero) and the other is non-zero, the result will be non-zero, no? I can't figure out what's special about odd in this context.

Its because multiple by odd preserves the low-bit from the input i.e
X * Y if Y is not, the low bit in X will be set in the result: https://alive2.llvm.org/ce/z/RD-MCy
So if X != 0 then the low-bit in X is non-zero then the result X * Y is non-zero.

holland11 added a comment.EditedMay 11 2023, 4:15 PM

Ah I see now, thank you! So in general, the only way a multiply with two non-zero source operands can result in a 0 is from overflow, but if at least one of those operands is odd, any overflow will never result in overflowing to 0.

Edit: Nvm, the below is actually incorrect. I was only considering a single overflow (so 0b10000 in a 4 bit result), but it's possible to get multiple overflow (0b110000 in a 4 bit result) which could still be 0 without being a power of two.
I suppose another way to think about it is that the only way to overflow to 0 in binary is with a result that is a power of two (ex. 256 in 8bit). Power of two's are never divisible by an odd number (besides 1, but that would require 256 * 1 which wouldn't make sense for 8b arithmetic). So if our multiplication involves at least one odd number, the result will never be a power of two (unless the other operand is 1 which means we won't overflow anyways) which means it will never overflow to 0.

Made a mistake in my assumptions in the previous comment, but I do see how this patch makes sense now.

Ah I see now, thank you! So in general, the only way a multiply with two non-zero source operands can result in a 0 is from overflow, but if at least one of those operands is odd, any overflow will never result in overflowing to 0.

Edit: Nvm, the below is actually incorrect. I was only considering a single overflow (so 0b10000 in a 4 bit result), but it's possible to get multiple overflow (0b110000 in a 4 bit result) which could still be 0 without being a power of two.
I suppose another way to think about it is that the only way to overflow to 0 in binary is with a result that is a power of two (ex. 256 in 8bit). Power of two's are never divisible by an odd number (besides 1, but that would require 256 * 1 which wouldn't make sense for 8b arithmetic). So if our multiplication involves at least one odd number, the result will never be a power of two (unless the other operand is 1 which means we won't overflow anyways) which means it will never overflow to 0.

This made me think of something, we can actually strengthen this case. Odd is just a special case really the result is X * Y != 0 is the same is Lowbit(X) * Lowbit(Y) != 0. Patch incoming!