Closed
Description
Take the following IR, extracted from part of how Rust generates ==
on enums:
define noundef i64 @get_discr(i8 noundef %x) unnamed_addr {
start:
%_4 = icmp ule i8 %x, 1
%small = select i1 %_4, i8 3, i8 %x
%_5 = sub i8 %small, 2
%_0 = zext i8 %_5 to i64
ret i64 %_0
}
define noundef zeroext i1 @discr_eq(i8 noundef %a, i8 noundef %b) unnamed_addr {
start:
%_3 = call noundef i64 @get_discr(i8 noundef %a)
%_4 = call noundef i64 @get_discr(i8 noundef %b)
%_0 = icmp eq i64 %_3, %_4
ret i1 %_0
}
Today, when optimizing get_discr
InstCombine will change the https://llvm.godbolt.org/z/6Eq87h6h7
(x <= 1 ? 3 : x) - 2
to
(x <= 1 ? 1 : x - 2)
which is reasonable on its own.
The problem, though, is that that makes things worse later in discr_eq
.
It's unable to undo that transformation after the inlining, and thus ends up as https://llvm.godbolt.org/z/31MdEhfcv
define noundef zeroext i1 @discr_eq(i8 noundef %a, i8 noundef %b) unnamed_addr #0 {
%0 = add i8 %a, -2
%_4.inv.i = icmp ugt i8 %a, 1
%_5.i = select i1 %_4.inv.i, i8 %0, i8 1
%1 = add i8 %b, -2
%_4.inv.i1 = icmp ugt i8 %b, 1
%_5.i2 = select i1 %_4.inv.i1, i8 %1, i8 1
%_0 = icmp eq i8 %_5.i, %_5.i2
ret i1 %_0
}
But there's no reason to do those add
s any more -- they could be pulled outside the select
s where they'd cancel out.
Phrased as C, it's doing
(x <= 1 ? 1 : x - 2) == (y <= 1 ? 1 : y - 2)
when instead it could be doing
(x <= 1 ? 3 : x) == (y <= 1 ? 3 : y)
So InstCombine should be able to detect this case, and rewrite it back to
define noundef zeroext i1 @discr_eq(i8 noundef %a, i8 noundef %b) unnamed_addr #0 {
%_4.inv.i = icmp ugt i8 %a, 1
%_5.i = select i1 %_4.inv.i, i8 %a, i8 3
%_4.inv.i1 = icmp ugt i8 %b, 1
%_5.i2 = select i1 %_4.inv.i1, i8 %b, i8 3
%_0 = icmp eq i8 %_5.i, %_5.i2
ret i1 %_0
}
Alive2 proof of correctness for that: https://alive2.llvm.org/ce/z/AWCmDs