If both operands of a rem instruction are left shifts of the same value, this can be simplified to 0 or the first operand
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Here are some alive tests which support the transforms:
https://alive2.llvm.org/ce/z/9KpSpV
https://alive2.llvm.org/ce/z/JFsIPK
negative transformations:
https://alive2.llvm.org/ce/z/9HD6L_
https://alive2.llvm.org/ce/z/PDymNH
Nice improvement @MattDevereau! I just had a few minor comments ...
llvm/lib/Analysis/InstructionSimplify.cpp | ||
---|---|---|
1006 | Perhaps you can create a temp variable here to reuse the logic, i.e. bool NoWrap = (IsSigned && Q.IIQ.hasNoSignedWrap(Shift)) || (!IsSigned && Q.IIQ.hasNoUnsignedWrap(Shift)); if (S1 >= S2 && NoWrap) return Constant::getNullValue(Shift->getType()); if (NoWrap) return Op0; | |
llvm/test/Transforms/InstSimplify/rem.ll | ||
502 | Could you leave comments on the negative tests explaining why they fail? If it makes it easier you could put all the negative tests together? | |
539 | Is it worth having a negative test for srem with nuw shifts as well? We shouldn't do any simplification in that case either. |
llvm/lib/Analysis/InstructionSimplify.cpp | ||
---|---|---|
1006 | I'm not sure this suggestion is correct, they key thing here being Shift = cast<OverflowingBinaryOperator>(Op1); on line 1010 which changes Shift to be a cast of Op1 instead of Op0 on which the nsw/nuw flags are checked, which is because the larger shift value needs its flags checking. If we went ahead with this, in this example (https://alive2.llvm.org/ce/z/YiFELt) NoWrap would evaluate to true while the transform is not viable. | |
llvm/test/Transforms/InstSimplify/rem.ll | ||
502 | Sure thing, should be simple enough | |
539 | I personally think the current tests are ok since we have one test for nsw which is testing the presence of nsw, and one which tests for it's absence and other flags aren't particularly relevant. These tests are quite compact though so I suppose there's no harm in adding extra cases. |
llvm/lib/Analysis/InstructionSimplify.cpp | ||
---|---|---|
995 | Instead of a new helper maybe this should be in simplifyRem which already appears to have: // (X << Y) % X -> 0 if (Q.IIQ.UseInstrInfo && ((Opcode == Instruction::SRem && match(Op0, m_NSWShl(m_Specific(Op1), m_Value()))) || (Opcode == Instruction::URem && match(Op0, m_NUWShl(m_Specific(Op1), m_Value()))))) return Constant::getNullValue(Op0->getType()); This is really just a superset of that case, so maybe expanding the existing logic would be simpler? |
llvm/lib/Analysis/InstructionSimplify.cpp | ||
---|---|---|
997 | Maybe add a TODO for the more generalized case of (rem (mul nsw/nuw X, C1), (mul nsw/nuw X, C2) if C1 % C2 == 0 -> 0 We seem to be missing it: https://godbolt.org/z/fzxb4sxb5 |
llvm/test/Transforms/InstSimplify/rem.ll | ||
---|---|---|
539 | Sure, I was just thinking that specifically we don't want to perform the simplification for any of nsw or nuw. They both mark the instruction as not wrapping, but only the signed version applies here. It's just because your logic accepts both nsw and nuw flags as valid, but *only if* the signedness matches the instruction. I was hoping to defend against someone accidentally rewriting your code in future and losing the signedness checks. |
Inlined unnecessary helper function
Added comments to negative tests
Added more incorrect flag tests
Note that this patch will likely be dropped in favour of https://reviews.llvm.org/D143014
Instead of a new helper maybe this should be in simplifyRem which already appears to have:
This is really just a superset of that case, so maybe expanding the existing logic would be simpler?