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?
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.
I see what you mean, yes it looks like yet another missing fold.
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.