-
Notifications
You must be signed in to change notification settings - Fork 15.5k
[InstCombine] Add combines for unsigned comparison of absolute value to constant #172021
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@llvm/pr-subscribers-llvm-transforms Author: Nathan Corbyn (cofibrant) ChangesThis patch implements the following two peephole optimisations:
In practice, the result of See the the following Alive2 proofs:
A similar transformation to 2 is possible even in cases when Full diff: https://github.com/llvm/llvm-project/pull/172021.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 1859dad4ec00b..1a3355cb42edb 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7602,6 +7602,51 @@ Instruction *InstCombinerImpl::foldICmpCommutative(CmpPredicate Pred,
}
}
+ // abs(X) u> K --> K >= 0 ? `X + K u> 2 * K` : `false`
+ // If abs(INT_MIN) is poison:
+ // abs(X) u< K --> K >= 1 ? `X + (K - 1) u<= 2 * (K - 1)` : K != 0
+ {
+ Value *X;
+ ConstantInt *C, *K;
+ bool Frozen = false;
+ if ((match(Op0,
+ m_Intrinsic<Intrinsic::abs>(m_Value(X), m_ConstantInt(C))) ||
+ (Frozen = match(Op0, m_Freeze(m_Intrinsic<Intrinsic::abs>(
+ m_Value(X), m_ConstantInt(C)))))) &&
+ match(Op1, m_ConstantInt(K))) {
+ const APInt KValue = K->getValue();
+
+ if (Pred == CmpInst::ICMP_UGT) {
+ if (KValue.isNegative())
+ return replaceInstUsesWith(CxtI,
+ ConstantInt::getFalse(CxtI.getType()));
+
+ Value *XPlusK = Builder.CreateAdd(X, K);
+ if (Frozen)
+ XPlusK = Builder.CreateFreeze(XPlusK);
+ return replaceInstUsesWith(
+ CxtI, Builder.CreateICmpUGT(
+ XPlusK, ConstantInt::get(K->getType(), 2 * KValue)));
+ }
+
+ if (Pred == CmpInst::ICMP_ULT && !C->getValue().isZero()) {
+ if (KValue.slt(1))
+ return replaceInstUsesWith(
+ CxtI, KValue.isZero() ? ConstantInt::getFalse(CxtI.getType())
+ : ConstantInt::getTrue(CxtI.getType()));
+
+ Value *XPlusKDec =
+ Builder.CreateAdd(X, ConstantInt::get(K->getType(), KValue - 1));
+ if (Frozen)
+ XPlusKDec = Builder.CreateFreeze(XPlusKDec);
+ return replaceInstUsesWith(
+ CxtI,
+ Builder.CreateICmpULE(
+ XPlusKDec, ConstantInt::get(K->getType(), 2 * (KValue - 1))));
+ }
+ }
+ }
+
const SimplifyQuery Q = SQ.getWithInstruction(&CxtI);
if (Value *V = foldICmpWithLowBitMaskedVal(Pred, Op0, Op1, Q, *this))
return replaceInstUsesWith(CxtI, V);
diff --git a/llvm/test/Transforms/InstCombine/abs-intrinsic.ll b/llvm/test/Transforms/InstCombine/abs-intrinsic.ll
index 763d82652dd5d..7355a7a71dada 100644
--- a/llvm/test/Transforms/InstCombine/abs-intrinsic.ll
+++ b/llvm/test/Transforms/InstCombine/abs-intrinsic.ll
@@ -997,3 +997,99 @@ define i8 @abs_diff_sle_y_x(i8 %x, i8 %y) {
%cond = select i1 %cmp, i8 %sub, i8 %sub1
ret i8 %cond
}
+
+define i1 @abs_cmp_ule_no_poison(i32 %x) {
+; CHECK-LABEL: @abs_cmp_ule_no_poison(
+; CHECK-NEXT: [[X_ABS:%.*]] = call i32 @llvm.abs.i32(i32 [[X:%.*]], i1 false)
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X_ABS]], 32
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %x.abs = call i32 @llvm.abs.i32(i32 %x, i1 0)
+ %cmp = icmp ule i32 %x.abs, 31
+ ret i1 %cmp
+}
+
+define i1 @abs_cmp_ule_poison(i32 %x) {
+; CHECK-LABEL: @abs_cmp_ule_poison(
+; CHECK-NEXT: [[X_ABS_FREEZE:%.*]] = freeze i32 [[X:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X_ABS_FREEZE]], 31
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], 63
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %x.abs = call i32 @llvm.abs.i32(i32 %x, i1 1)
+ %x.abs.freeze = freeze i32 %x.abs
+ %cmp = icmp ule i32 %x.abs.freeze, 31
+ ret i1 %cmp
+}
+
+define i1 @abs_cmp_ult_no_poison(i32 %x) {
+; CHECK-LABEL: @abs_cmp_ult_no_poison(
+; CHECK-NEXT: [[X_ABS:%.*]] = call i32 @llvm.abs.i32(i32 [[X:%.*]], i1 false)
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X_ABS]], 32
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %x.abs = call i32 @llvm.abs.i32(i32 %x, i1 0)
+ %cmp = icmp ult i32 %x.abs, 32
+ ret i1 %cmp
+}
+
+define i1 @abs_cmp_ult_poison(i32 %x) {
+; CHECK-LABEL: @abs_cmp_ult_poison(
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X_FR]], 31
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], 63
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %x.abs = call i32 @llvm.abs.i32(i32 %x, i1 1)
+ %x.abs.freeze = freeze i32 %x.abs
+ %cmp = icmp ult i32 %x.abs.freeze, 32
+ ret i1 %cmp
+}
+
+define i1 @abs_cmp_uge_no_poison(i32 %x) {
+; CHECK-LABEL: @abs_cmp_uge_no_poison(
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -31
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], -61
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %x.abs = call i32 @llvm.abs.i32(i32 %x, i1 0)
+ %cmp = icmp ugt i32 %x.abs, 30
+ ret i1 %cmp
+}
+
+define i1 @abs_cmp_uge_poison(i32 %x) {
+; CHECK-LABEL: @abs_cmp_uge_poison(
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X_FR]], -31
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], -61
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %x.abs = call i32 @llvm.abs.i32(i32 %x, i1 1)
+ %x.abs.freeze = freeze i32 %x.abs
+ %cmp = icmp ugt i32 %x.abs.freeze, 30
+ ret i1 %cmp
+}
+
+define i1 @abs_cmp_ugt_no_poison(i32 %x) {
+; CHECK-LABEL: @abs_cmp_ugt_no_poison(
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], -63
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %x.abs = call i32 @llvm.abs.i32(i32 %x, i1 0)
+ %cmp = icmp ugt i32 %x.abs, 31
+ ret i1 %cmp
+}
+
+define i1 @abs_cmp_ugt_poison(i32 %x) {
+; CHECK-LABEL: @abs_cmp_ugt_poison(
+; CHECK-NEXT: [[X:%.*]] = freeze i32 [[X1:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], -32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], -63
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %x.abs = call i32 @llvm.abs.i32(i32 %x, i1 1)
+ %x.abs.freeze = freeze i32 %x.abs
+ %cmp = icmp ugt i32 %x.abs.freeze, 31
+ ret i1 %cmp
+}
|
This patch implements the following two peephole optimisations:
abs(X) u> K --> K >= 0 ? `X + K u> 2 * K` : `false`;abs(INT_MIN)ispoison,abs(X) u< K --> K >= 1 ? `X + (K - 1) u<= 2 * (K - 1)` : K != 0.In practice, the result of
abs(X)is typically frozen, in which case, for the transformation to be correct, the results of the adds must be frozen accordingly.See the the following Alive2 proofs:
abs(X)unfrozen andabs(INT_MIN) == INT_MIN;abs(X)unfrozen andabs(INT_MIN) == poison;abs(X)frozenabs(INT_MIN) == INT_MIN;abs(X)frozenabs(INT_MIN) == poison;abs(X)unfrozen;abs(X)frozen.A similar transformation to 2 is possible even in cases when
abs(INT_MIN)isINT_MIN, but not profitable in practice (see Godbolt). We deliberately exclude this case.