This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] simplify (~A | B) ^ A --> ~( A & B)
ClosedPublic

Authored by MehrHeidar on Nov 21 2021, 1:37 PM.

Details

Summary

This bug is reported at : https://bugs.llvm.org/show_bug.cgi?id=52518

According to the Alive2, https://alive2.llvm.org/ce/z/gLrYPk, the transformation seems to be correct and -O2/-O3 does not do it.

Diff Detail

Event Timeline

MehrHeidar created this revision.Nov 21 2021, 1:37 PM
MehrHeidar requested review of this revision.Nov 21 2021, 1:37 PM
Herald added a project: Restricted Project. · View Herald TranscriptNov 21 2021, 1:37 PM
foad added inline comments.Nov 22 2021, 1:14 AM
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
3627

This probably needs some m_OneUses (but I'm not sure how you decide exactly where to put them).

  1. Please commit the baseline tests, so we see diffs here.
  2. I expect that will show that the commuted variations are not testing what they claim to be testing (you'll need to add instructions to prevent commuting).
  3. Please add tests with extra uses - since we're creating 2 instructions, the fold should be good as long as any one of the intermediate values can be eliminated (has one use).

Side note: @rampitec recently added a matcher that takes a binary opcode as an input. It probably doesn't make sense here, but it's something to think about if we're trying to make logic folds more regular/predictable ('DeMorganize' folds so we handle patterns where 'and' and 'or' operations are swapped).

MehrHeidar marked an inline comment as done.Nov 23 2021, 2:25 PM
  1. Please commit the baseline tests, so we see diffs here.
  2. I expect that will show that the commuted variations are not testing what they claim to be testing (you'll need to add instructions to prevent commuting).
  3. Please add tests with extra uses - since we're creating 2 instructions, the fold should be good as long as any one of the intermediate values can be eliminated (has one use).

Side note: @rampitec recently added a matcher that takes a binary opcode as an input. It probably doesn't make sense here, but it's something to think about if we're trying to make logic folds more regular/predictable ('DeMorganize' folds so we handle patterns where 'and' and 'or' operations are swapped).

Thanks for you suggestion.
I have modified the tests and committed them in D114436;
I did change the code as well;
Though it's still conservative in terms that both should have only use. Since, we were removing an instruction from a combination of 3, I thought if any of them have an extra use then the number of instructions after fold will remains 3.
Also, regarding your last suggestion, I am agree that it's a good idea to make these logic folds more predictable. I will start in this direction.

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
3627

Thank you!
I added the m_OneUse as per you suggestion in new commit.

MehrHeidar marked an inline comment as done.Nov 23 2021, 2:25 PM
spatel added inline comments.Nov 24 2021, 7:46 AM
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
3627

I'm not sure how the matching logic or one-use logic works here. It seems dangerous to overwrite the Op0/Op1 locals.

If you want a conservative use check, just put that on the 'or' instruction - as long as we're eliminating that instruction, this transform is a win:

// (~A | B) ^ A --> ~(A & B)
if (match(Op0, m_OneUse(m_c_Or(m_Not(m_Specific(Op1)), m_Value(B)))))
  return BinaryOperator::CreateNot(Builder.CreateAnd(Op1, B));

// A ^ (~A | B) --> ~(A & B)
if (match(Op1, m_OneUse(m_c_Or(m_Not(m_Specific(Op0)), m_Value(B)))))
  return BinaryOperator::CreateNot(Builder.CreateAnd(Op0, B));

But I think we're missing a test where the 'not A' has another use. Please add and make sure that looks right.

Addressing review comments.

MehrHeidar marked an inline comment as done.Nov 24 2021, 8:48 AM
MehrHeidar added inline comments.
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
3627

Thank you for your suggestions, I was not aware that overwriting the operands might be dangerous. I changed the code.
About the missing test case for an extra use of NotA, I had the test but because previous code was also checking for NotA to have only a single use, we were not actually folding this pattern.
However now, by putting m_OneUse on the or instruction, folding is done and the changes can be shown against the base result.

spatel added inline comments.Nov 24 2021, 11:50 AM
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
3632–3633

Is this logically different than the m_Specific(Op0) code pattern that I suggested?
Unless I'm missing something, you don't need to use m_Deferred either way. In this code, the clause before && must run and capture 'A' before it is matched as Op0.

MehrHeidar marked 2 inline comments as done.

Addressing review comments.

MehrHeidar added inline comments.Nov 27 2021, 9:43 AM
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
3632–3633

Sorry for delay on following this issue.

Is this logically different than the m_Specific(Op0) code pattern that I suggested?
Unless I'm missing something, you don't need to use m_Deferred either way. In this code, the clause before && must run and capture 'A' before it is matched as Op0.

Oh, I see. I think I misunderstood your previous comment then. I tried to avoid any use of Op0/Op1 in any of the m_Specific/m_Value/m_Deferred, so I wrote the code in that way in the previous commit. I did replace the code as your suggestion.

Thanks.

3632–3633

Is this logically different than the m_Specific(Op0) code pattern that I suggested?
Unless I'm missing something, you don't need to use m_Deferred either way. In this code, the clause before && must run and capture 'A' before it is matched as Op0.

spatel accepted this revision.Nov 29 2021, 5:17 AM

LGTM

This revision is now accepted and ready to land.Nov 29 2021, 5:17 AM
This revision was automatically updated to reflect the committed changes.