| Issue |
204136
|
| Summary |
[ISel] Implement known bits optimization for `@llvm.pext` and `@llvm.pdep`
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
eisenwave
|
This is a follow-up to https://github.com/llvm/llvm-project/issues/172857
#200570 already implemented known bits optimization on the initial PR, but it was yoinked to trim down the pull request size. I have a fully working implementation sitting somewhere on a backup branch.
https://github.com/llvm/llvm-project/pull/200570/changes/d07f81e181408e12d59fa5c6362647adb941822e
```cpp
KnownBits KnownBits::bext(const KnownBits &LHS, const KnownBits &RHS) {
// For each source position I where mask[I] could be set, the output index j
// lies in [M0, M1] where these track the range of possible set-bit counts
// seen so far in mask.
//
// The output bit j
// - can be 0 if any candidate LHS[I] could be zero or popcount(mask) could
// be <= j, and
// - can be 1 only if some candidate LHS[I] could be one and popcount(mask)
// is known > j.
unsigned BitWidth = LHS.getBitWidth();
KnownBits Res(BitWidth);
Res.setAllConflict();
unsigned M0 = 0, M1 = 0;
for (unsigned I = 0; I < BitWidth; ++I) {
if (!RHS.Zero[I]) {
APInt Range = APInt::getBitsSet(BitWidth, M0, M1 + 1);
if (!LHS.Zero[I])
Res.Zero &= ~Range; // some position in Range could be 1
if (!LHS.One[I])
Res.One &= ~Range; // some position in Range could be 0
}
if (RHS.One[I])
++M0, ++M1;
else if (!RHS.Zero[I])
++M1;
}
// Output positions j >= M0 may have no source (popcount(mask) <= j), in
// which case they default to zero.
Res.One &= APInt::getLowBitsSet(BitWidth, M0);
return Res;
}
KnownBits KnownBits::bdep(const KnownBits &LHS, const KnownBits &RHS) {
// For each output position I where mask[I] could be set, the source index j
// lies in [M0, M1] where these track possible counts of set mask bits < I.
//
// The output bit
// - can be 0 if mask[I] or any candidate LHS[j] could be zero, and
// - can be 1 only if both mask[I] and some candidate LHS[j] could be one.
unsigned BitWidth = LHS.getBitWidth();
KnownBits Res(BitWidth);
Res.setAllConflict();
unsigned M0 = 0, M1 = 0;
for (unsigned I = 0; I < BitWidth; ++I) {
if (!RHS.One[I])
Res.One.clearBit(I); // mask[I] could be 0 -> output[I] could be 0
if (!RHS.Zero[I]) {
APInt Range = APInt::getBitsSet(BitWidth, M0, M1 + 1);
if (!Range.isSubsetOf(LHS.One))
Res.One.clearBit(I); // some candidate could be 0
if (!Range.isSubsetOf(LHS.Zero))
Res.Zero.clearBit(I); // some candidate could be 1
}
if (RHS.One[I])
++M0, ++M1;
else if (!RHS.Zero[I])
++M1;
}
return Res;
}
```
```cpp
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes=instcombine -S < %s | FileCheck %s
; bext(x, 0b00001111) produces a 4-bit result; bit 4 and above are always zero.
define i1 @bext_high_bits_zero(i8 %x) nounwind {
; CHECK-LABEL: @bext_high_bits_zero(
; CHECK-NEXT: ret i1 true
;
%bext = call i8 @llvm.bext.i8(i8 %x, i8 15)
%and = and i8 %bext, 240
%r = icmp eq i8 %and, 0
ret i1 %r
}
; bext(x, 0b11001100) can produce up to 4 bits; bits 4..7 are zero.
define i1 @bext_sparse_mask_high_zero(i8 %x) nounwind {
; CHECK-LABEL: @bext_sparse_mask_high_zero(
; CHECK-NEXT: ret i1 true
;
%bext = call i8 @llvm.bext.i8(i8 %x, i8 204)
%and = and i8 %bext, 240
%r = icmp eq i8 %and, 0
ret i1 %r
}
; bext(x, 0) -> all bits zero: the AND with any mask is always zero.
define i1 @bext_zero_mask_known_zero(i8 %x) nounwind {
; CHECK-LABEL: @bext_zero_mask_known_zero(
; CHECK-NEXT: ret i1 true
;
%bext = call i8 @llvm.bext.i8(i8 %x, i8 0)
%r = icmp eq i8 %bext, 0
ret i1 %r
}
; Negative: bext(x, 0b11001100) can have bits 0-3 set; can't prove bit 0 is zero.
define i1 @bext_low_bits_not_known(i8 %x) nounwind {
; CHECK-LABEL: @bext_low_bits_not_known(
; CHECK-NEXT: [[BEXT:%.*]] = call i8 @llvm.bext.i8(i8 [[X:%.*]], i8 -52)
; CHECK-NEXT: [[AND:%.*]] = and i8 [[BEXT]], 1
; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[AND]], 0
; CHECK-NEXT: ret i1 [[R]]
;
%bext = call i8 @llvm.bext.i8(i8 %x, i8 204)
%and = and i8 %bext, 1
%r = icmp eq i8 %and, 0
ret i1 %r
}
; bdep(x, 0b11110000): bits 0..3 of result are always zero.
define i1 @bdep_low_bits_zero(i8 %x) nounwind {
; CHECK-LABEL: @bdep_low_bits_zero(
; CHECK-NEXT: ret i1 true
;
%bdep = call i8 @llvm.bdep.i8(i8 %x, i8 240)
%and = and i8 %bdep, 15
%r = icmp eq i8 %and, 0
ret i1 %r
}
; bdep(x, 0b00001111): bits 4..7 of result are always zero.
define i1 @bdep_high_bits_zero(i8 %x) nounwind {
; CHECK-LABEL: @bdep_high_bits_zero(
; CHECK-NEXT: ret i1 true
;
%bdep = call i8 @llvm.bdep.i8(i8 %x, i8 15)
%and = and i8 %bdep, 240
%r = icmp eq i8 %and, 0
ret i1 %r
}
; bdep(x, 0) -> all bits zero.
define i1 @bdep_zero_mask_known_zero(i8 %x) nounwind {
; CHECK-LABEL: @bdep_zero_mask_known_zero(
; CHECK-NEXT: ret i1 true
;
%bdep = call i8 @llvm.bdep.i8(i8 %x, i8 0)
%r = icmp eq i8 %bdep, 0
ret i1 %r
}
; Negative: bdep(x, 0b11001100) can have bits 2,3,6,7 set.
define i1 @bdep_mask_bits_not_known(i8 %x) nounwind {
; CHECK-LABEL: @bdep_mask_bits_not_known(
; CHECK-NEXT: [[BDEP:%.*]] = call i8 @llvm.bdep.i8(i8 [[X:%.*]], i8 -52)
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[BDEP]], -1
; CHECK-NEXT: ret i1 [[R]]
;
%bdep = call i8 @llvm.bdep.i8(i8 %x, i8 204)
%and = and i8 %bdep, 128
%r = icmp eq i8 %and, 0
ret i1 %r
}
declare i8 @llvm.bext.i8(i8, i8)
declare i8 @llvm.bdep.i8(i8, i8)
```
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs