[InstCombine] If a in (CST, CST + 1), then (type)(a == CST) into a-CST
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
[GCC bug](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126085)
[Godbolt](https://godbolt.org/z/5j1T5xaWf)
Edit:
This should be done in inverse as explained in [GCC link](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126085#c2)
This is far easier to do than the original `If a in (-CST, 1-CST), then a + CST to a == (CST + 1)`:
In fact, this is the general version of:
```c
/* (type)([0,1]@a != 0) -> (type)a
(type)([0,1]@a == 1) -> (type)a
(type)([0,1]@a == 0) -> a ^ 1
(type)([0,1]@a != 1) -> a ^ 1. */
```
All four functions should optimize to return 1;
src_zext_ne_lower_cst () and src_zext_eq_upper_cst () should be better transformations since it removes zext, meaning one instruction.
src_zext_eq_lower_cst () and src_zext_ne_upper_cst () removes zext but still uses two instructions, add then xor.
It should reduce register usage IIRC.
At least on x86, it removes sete.
```c
#define CST 10
/* (type)([CST,CST + 1]@a != CST) -> (type)(a - CST)
(type)([CST - 1,CST]@a == CST) -> (type)(a - (CST - 1))
(type)([CST,CST + 1]@a == CST) -> (a - CST) ^ 1
(type)([CST - 1,CST]@a != CST) -> (a - (CST - 1)) ^ 1. */
int
src_zext_ne_lower_cst (int a)
{
if (a != (CST) && a != (CST + 1))
__builtin_unreachable ();
return (a != CST) == (a - CST);
}
int
src_zext_eq_upper_cst (int a)
{
if (a != (CST - 1) && a != (CST))
__builtin_unreachable ();
return (a == CST) == (a - (CST - 1));
}
int
src_zext_eq_lower_cst (int a)
{
if (a != (CST) && a != (CST + 1))
__builtin_unreachable ();
return (a == CST) == ((a - CST) ^ 1);
}
int
src_zext_ne_upper_cst (int a)
{
if (a != (CST - 1) && a != (CST))
__builtin_unreachable ();
return (a != CST) == ((a - (CST - 1)) ^ 1);
}
```
```llvm
define dso_local range(i32 0, 2) i32 @src_zext_ne_lower_cst(i32 noundef %a) local_unnamed_addr {
entry:
%cmp = icmp ne i32 %a, 10
%0 = and i32 %a, -2
%or.cond = icmp eq i32 %0, 10
tail call void @llvm.assume(i1 %or.cond)
%conv = zext i1 %cmp to i32
%sub = add nsw i32 %a, -10
%cmp3 = icmp eq i32 %sub, %conv
%conv4 = zext i1 %cmp3 to i32
ret i32 %conv4
}
define dso_local range(i32 0, 2) i32 @src_zext_eq_upper_cst(i32 noundef %a) local_unnamed_addr {
entry:
%0 = add nsw i32 %a, -9
%cmp2 = icmp eq i32 %a, 10
%conv = zext i1 %cmp2 to i32
%cmp3 = icmp eq i32 %0, %conv
%conv4 = zext i1 %cmp3 to i32
ret i32 %conv4
}
define dso_local range(i32 0, 2) i32 @src_zext_eq_lower_cst(i32 noundef %a) local_unnamed_addr {
entry:
%0 = and i32 %a, -2
%or.cond = icmp eq i32 %0, 10
tail call void @llvm.assume(i1 %or.cond)
%cmp2 = icmp eq i32 %a, 10
%conv = zext i1 %cmp2 to i32
%1 = xor i32 %a, 1
%xor = add nsw i32 %1, -10
%cmp3 = icmp eq i32 %xor, %conv
%conv4 = zext i1 %cmp3 to i32
ret i32 %conv4
}
define dso_local range(i32 0, 2) i32 @src_zext_ne_upper_cst(i32 noundef %a) local_unnamed_addr {
entry:
%cmp1 = icmp ne i32 %a, 10
%0 = add nsw i32 %a, -9
%conv = zext i1 %cmp1 to i32
%1 = xor i32 %0, %conv
%cmp3 = icmp eq i32 %1, 1
%conv4 = zext i1 %cmp3 to i32
ret i32 %conv4
}
declare void @llvm.assume(i1 noundef) #2
```
Contributor guide
Assessment
This issue has not been assessed yet.