This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Convert xor (ashr X, BW-1), C -> select(X >=s 0, C, ~C)
ClosedPublic

Authored by dmgreen on Sep 2 2021, 5:59 AM.

Details

Summary

The sequence of instructions xor (ashr X, BW-1), C (or with a truncation xor (trunc (ashr X, BW-1)), C) takes a value, produces all zeros or all ones and with it optionally inverts a constant depending on whether the original input was positive or negative. This is the same as checking if the value is positive, and selecting between the constant and ~constant.
https://alive2.llvm.org/ce/z/NJ85qY

This is a fairly general version of the fold as part of D108049, which really only needs the constant to be a saturate INTMAX/INTMIN pair.

Diff Detail

Event Timeline

dmgreen created this revision.Sep 2 2021, 5:59 AM
dmgreen requested review of this revision.Sep 2 2021, 5:59 AM
Herald added a project: Restricted Project. · View Herald TranscriptSep 2 2021, 5:59 AM
dmgreen edited the summary of this revision. (Show Details)Sep 2 2021, 6:11 AM
foad added a subscriber: foad.Sep 3 2021, 2:31 AM

... optionally negates a constant ...

It's not integer negation. I like "inverts" better.

dmgreen edited the summary of this revision. (Show Details)Sep 3 2021, 7:23 AM
spatel added inline comments.Sep 7 2021, 6:14 AM
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
3558

"xor X, 0" - seems like that should be an assert because it means this instruction escaped from instsimplify.

"xor X, -1" - I think we should handle this too for consistency. The transform holds for any value unless I'm missing something:
https://alive2.llvm.org/ce/z/R7qEeT
(If that's right, please add a test so we know we are not or will not conflict with some other transform.)

3559

Might as well create this as "SGT X, -1" to save a step since the "SGE" will get canonicalized to that form.

dmgreen added inline comments.Sep 8 2021, 2:58 AM
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
3558

Yeah that apparently comes up a few times in the existing tests.

From what I can tell, we would convert a select c, -1, 0 to a sext c:
https://godbolt.org/z/zKePx154f
And a (x >s -1) ? -1 : 0 -> not (ashr x, 31):
https://github.com/llvm/llvm-project/blob/c01b76e733d6e5e2d21e4277dceaa1f319794c6a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp#L1408

It makes sense to treat "not" as special to me, more canonical than the other folds here.

3559

Will do.

dmgreen updated this revision to Diff 371296.Sep 8 2021, 3:00 AM

Add an assert for zero, and change to s> -1.

lebedev.ri added inline comments.Sep 8 2021, 3:17 AM
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
3556

I'm not sure this one-use check does what you want it to do when there's trunc

3556

Should this use m_APIntAllowUndef?

dmgreen updated this revision to Diff 377634.Oct 6 2021, 12:09 PM

Sorry for the delay, other things came up. This is a rebase, plus makes used of m_APIntAllowUndef and moves the m_OneUse before the m_TruncOrSelf.

lebedev.ri edited the summary of this revision. (Show Details)Oct 6 2021, 12:15 PM

FWIW, we don't strictly need this to be xor-by-constant: https://alive2.llvm.org/ce/z/snX3_H
If everything is one-use, then we could allow xor-by-value too.
Not sure if it improves things or not though?

FWIW, we don't strictly need this to be xor-by-constant: https://alive2.llvm.org/ce/z/snX3_H

Err, i of course meant https://alive2.llvm.org/ce/z/NJ85qY
... which can also be spelled as: https://alive2.llvm.org/ce/z/FTGTu7
which brings me to the real question: isn't the main pattern here: https://alive2.llvm.org/ce/z/B8TUY6 ?

If everything is one-use, then we could allow xor-by-value too.
Not sure if it improves things or not though?

lebedev.ri edited the summary of this revision. (Show Details)Oct 6 2021, 12:26 PM
foad added a comment.Oct 7 2021, 12:23 AM

isn't the main pattern here: https://alive2.llvm.org/ce/z/B8TUY6 ?

This is (trunc (ashr i16 %input, 15) to i8) -> (select i1 (icmp sgt i16 %input, 65535), i8 0, i8 255), but I don't understand why you include the trunc in the transformation.

Don't we want to fold (ashr i16 %input, 15) -> (select i1 (icmp sgt i16 %input, 65535), i16 0, i16 65535) in any situation where the user(s) of this expression can be pushed into the select? I.e. not just a trunc, but also things like an XOR with constant from the original motivating example "xor (ashr X, BW-1), C".

which brings me to the real question: isn't the main pattern here: https://alive2.llvm.org/ce/z/B8TUY6 ?

Not sure. This is already more general than I need it to be. I really just need this awkward saturate to fold into a more canonical pattern :)

A select %c, -1, 0 is just a sext c (or in this case select c, 0, -1 -> sext(not(c)), which inverts the condition c). https://github.com/llvm/llvm-project/blob/c01b76e733d6e5e2d21e4277dceaa1f319794c6a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp#L1408 will convert that to a ashr. If you think any of that should change then let me know, but it seems like it might break a few things.

lebedev.ri accepted this revision.Oct 15 2021, 7:12 AM

Seems fine to me.

llvm/test/Transforms/InstCombine/xor-ashr.ll
66–76

We fail to fully propagate undefs. This should be
https://alive2.llvm.org/ce/z/ZGMsRq
(see Constant::mergeUndefsWith())

This revision is now accepted and ready to land.Oct 15 2021, 7:12 AM
This revision was landed with ongoing or failed builds.Oct 29 2021, 3:19 AM
This revision was automatically updated to reflect the committed changes.