This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Fuse checks for LHS (~(A | B) & C) | ... NFC.
ClosedPublic

Authored by rampitec on Nov 3 2021, 11:26 AM.

Diff Detail

Event Timeline

rampitec created this revision.Nov 3 2021, 11:26 AM
rampitec requested review of this revision.Nov 3 2021, 11:26 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 3 2021, 11:26 AM
spatel added a comment.Nov 5 2021, 9:26 AM

I think we need to step back before we add a bunch of other matches.
For an expression with and/or/not, there should always be a Demorgan sibling where the and/or are swapped, right?
https://alive2.llvm.org/ce/z/ziSbZa

But we're transforming that into something that ends in 'xor', so it suggests we're missing some other 'xor' fold (and possibly another 'or' fold?).

Can we generalize the existing transforms or at least group them so we don't have a mess of incomplete logic folds?

I think we need to step back before we add a bunch of other matches.
For an expression with and/or/not, there should always be a Demorgan sibling where the and/or are swapped, right?
https://alive2.llvm.org/ce/z/ziSbZa

But we're transforming that into something that ends in 'xor', so it suggests we're missing some other 'xor' fold (and possibly another 'or' fold?).

Can we generalize the existing transforms or at least group them so we don't have a mess of incomplete logic folds?

What I realized we have couple deficiencies: we do not always reassociate as needed, in particular if a subexpression has multiple uses, and we do not demorgan to increase a number of operations even when that will lead to a further combining. I am not sure if that can be generalized without exploding compile time.

At the same time here I am trying at least to organize folds with a same LHS into a single block (and a single LHS match) which I have not done initially. For the demorganing I also have a subsequent patch (not yet ready, I am still working on tests) to add one more LHS. This LHS is ~(A | B) & C and the demorganed form I am adding is ~A & ~B & C. matchDeMorgansLaws does not convert latter to the former because we have a second use of the inner 'and'. I'd say in general it should not do it for multiply used values, but given the overall cost of the expressions here the benefit outweights it.

https://alive2.llvm.org/ce/z/ziSbZa

I see what you mean, yes it looks like yet another missing fold.

I think we need to step back before we add a bunch of other matches.
For an expression with and/or/not, there should always be a Demorgan sibling where the and/or are swapped, right?
https://alive2.llvm.org/ce/z/ziSbZa

But we're transforming that into something that ends in 'xor', so it suggests we're missing some other 'xor' fold (and possibly another 'or' fold?).

Can we generalize the existing transforms or at least group them so we don't have a mess of incomplete logic folds?

As far as I understand the difference between visitOr and visintAnd in this case: both are calling InstCombinerImpl::SimplifyUsingDistributiveLaws. The problem is:

For 'and' it creates (~(a & c) | b) & (~(a & b)) -> (a & b) ^ (~(a & c) | b). This is beneficial as it is one instruction less.
For 'or' it would create (~(a | c) & b) | (~(a | b)) -> ~((a | b) ^ (~(a | c) & b)) using the same rules, same amount of operations, not beneficial.

The case (a & b) ^ (~(a & c) | b) -> ~(a & (b | c)) can use a separate match in the visitXor but given that extra 'not' operation which is created after swapping 'and' and 'or' operations in the source pattern I do not see how this can be generalized for a case where target is just one operation less and the same complexity if has to be inverted. In turn the pattern (~(a | c) & b) | (~(a | b)) -> ~(a | (b & c)) saves a lot specifically due to its source complexity. I wish we have infrastructure though to express a pattern with swapped 'and' and 'or' in general.

Quite similarly we are not handling inverted case from D112276: https://alive2.llvm.org/ce/z/ywuzdS
This is done: (c & ~(a | b)) | (b & ~(a | c)) --> ~a & (b ^ c), and this is not: (c | ~(a & b)) & (b | ~(a & c)) --> ~(a & (b ^ c)).

With all that said it looks like these patterns have to be extracted into a separate function and called for both 'or' and 'and'. But I don't think this shall preclude this NFC and the next D113141 to wait for it, these are mostly orthogonal to me.

spatel accepted this revision.Nov 9 2021, 9:29 AM

With all that said it looks like these patterns have to be extracted into a separate function and called for both 'or' and 'and'. But I don't think this shall preclude this NFC and the next D113141 to wait for it, these are mostly orthogonal to me.

I agree (just wanted to raise the idea of generalizing if possible) - this one looks like good cleanup / NFC. LGTM.

This revision is now accepted and ready to land.Nov 9 2021, 9:29 AM

With all that said it looks like these patterns have to be extracted into a separate function and called for both 'or' and 'and'. But I don't think this shall preclude this NFC and the next D113141 to wait for it, these are mostly orthogonal to me.

I agree (just wanted to raise the idea of generalizing if possible) - this one looks like good cleanup / NFC. LGTM.

This is both grouping and generalizing: D113526.