From de9830fe40bf0bcca05dccd9fb9bcd983eaea3bf Mon Sep 17 00:00:00 2001 From: lijinpei-amd Date: Thu, 23 Apr 2026 17:26:30 +0800 Subject: [PATCH] [InstCombine] Treat sdiv as udiv in foldICmpDivConstant when both operands are non-negative (#188731) When foldICmpDivConstant encounters a signedness mismatch between the sdiv and the icmp (e.g. icmp ugt (sdiv X, 64), 2), it previously bailed out. This patch allows it to proceed by checking whether the sdiv's dividend is known non-negative (via assumes/known bits) and the divisor is a positive constant. In that case, sdiv is equivalent to udiv, so we set DivIsSigned=false and let the existing udiv folding handle the rest. --------- Co-authored-by: Claude Opus 4.6 --- .../InstCombine/InstCombineCompares.cpp | 11 +- .../InstCombine/icmp-sdiv-assume.ll | 209 ++++++++++++++++++ 2 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 6f1ab31fe03b..a53f825f43a8 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -2814,8 +2814,15 @@ Instruction *InstCombinerImpl::foldICmpDivConstant(ICmpInst &Cmp, // (x /u C2) isStrictlyPositive() || + !isKnownNonNegative(X, SQ.getWithInstruction(&Cmp))) + return nullptr; + DivIsSigned = false; + } // The ProdOV computation fails on divide by 0 and divide by -1. Cases with // INT_MIN will also fail if the divisor is 1. Although folds of all these diff --git a/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll b/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll new file mode 100644 index 000000000000..27c0ff485c5e --- /dev/null +++ b/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll @@ -0,0 +1,209 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +; Test that foldICmpDivConstant can treat sdiv as udiv when the dividend +; is known non-negative (e.g. via a dominating condition), allowing the +; fold to proceed despite a signed div / unsigned icmp mismatch. +; Non-power-of-two divisors are used to prevent sdiv->lshr conversion. +; The sdiv is placed in the entry block where x's sign is unknown, and +; the icmp is in a block dominated by a condition that proves x >= 0. + +declare void @use(i32) + +; Positive test: x > 255 dominates the icmp, proving x non-negative. +; sdiv(x, 65) = udiv(x, 65) when x >= 0, and udiv(x, 65) > 2 when x >= 195. +; Since x > 255 >= 195, the result folds to true. +define i1 @icmp_sdiv_domcond(i32 %x) { +; CHECK-LABEL: @icmp_sdiv_domcond( +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65 +; CHECK-NEXT: call void @use(i32 [[DIV]]) +; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X]], 255 +; CHECK-NEXT: br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; CHECK: if.true: +; CHECK-NEXT: ret i1 true +; CHECK: if.false: +; CHECK-NEXT: ret i1 false +; + %div = sdiv i32 %x, 65 + call void @use(i32 %div) + %cond = icmp sgt i32 %x, 255 + br i1 %cond, label %if.true, label %if.false + +if.true: + %cmp = icmp samesign ugt i32 %div, 2 + ret i1 %cmp + +if.false: + ret i1 false +} + +; Positive test: add+sdiv pattern (mirrors real-world kernel case). +; x+63 > 255 dominates the icmp, proving x+63 non-negative. +define i1 @icmp_add_sdiv_domcond(i32 %x) { +; CHECK-LABEL: @icmp_add_sdiv_domcond( +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], 63 +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[ADD]], 65 +; CHECK-NEXT: call void @use(i32 [[DIV]]) +; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[ADD]], 255 +; CHECK-NEXT: br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; CHECK: if.true: +; CHECK-NEXT: ret i1 true +; CHECK: if.false: +; CHECK-NEXT: ret i1 false +; + %add = add i32 %x, 63 + %div = sdiv i32 %add, 65 + call void @use(i32 %div) + %cond = icmp sgt i32 %add, 255 + br i1 %cond, label %if.true, label %if.false + +if.true: + %cmp = icmp samesign ugt i32 %div, 2 + ret i1 %cmp + +if.false: + ret i1 false +} + +; Positive test: udiv case (no signedness mismatch, baseline behavior). +define i1 @icmp_udiv_domcond(i32 %x) { +; CHECK-LABEL: @icmp_udiv_domcond( +; CHECK-NEXT: [[DIV:%.*]] = udiv i32 [[X:%.*]], 65 +; CHECK-NEXT: call void @use(i32 [[DIV]]) +; CHECK-NEXT: [[COND:%.*]] = icmp ugt i32 [[X]], 255 +; CHECK-NEXT: br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; CHECK: if.true: +; CHECK-NEXT: ret i1 true +; CHECK: if.false: +; CHECK-NEXT: ret i1 false +; + %div = udiv i32 %x, 65 + call void @use(i32 %div) + %cond = icmp ugt i32 %x, 255 + br i1 %cond, label %if.true, label %if.false + +if.true: + %cmp = icmp ugt i32 %div, 2 + ret i1 %cmp + +if.false: + ret i1 false +} + +; Negative test: dominating condition is too weak. +; x > 127 does NOT prove sdiv(x, 65) > 2 (e.g. x=128, sdiv(128,65)=1). +define i1 @icmp_sdiv_domcond_too_weak(i32 %x) { +; CHECK-LABEL: @icmp_sdiv_domcond_too_weak( +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65 +; CHECK-NEXT: call void @use(i32 [[DIV]]) +; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X]], 127 +; CHECK-NEXT: br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; CHECK: if.true: +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X]], 194 +; CHECK-NEXT: ret i1 [[CMP]] +; CHECK: if.false: +; CHECK-NEXT: ret i1 false +; + %div = sdiv i32 %x, 65 + call void @use(i32 %div) + %cond = icmp sgt i32 %x, 127 + br i1 %cond, label %if.true, label %if.false + +if.true: + %cmp = icmp samesign ugt i32 %div, 2 + ret i1 %cmp + +if.false: + ret i1 false +} + +; Negative test: no dominating condition, x's sign is unknown. +define i1 @icmp_sdiv_no_domcond(i32 %x) { +; CHECK-LABEL: @icmp_sdiv_no_domcond( +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65 +; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i32 [[DIV]], 2 +; CHECK-NEXT: ret i1 [[CMP]] +; + %div = sdiv i32 %x, 65 + %cmp = icmp samesign ugt i32 %div, 2 + ret i1 %cmp +} + +; Negative test: comparison is on the boundary. +; x > 194 means x >= 195, sdiv(195, 65) = 3, but 3 > 3 is false. +define i1 @icmp_sdiv_domcond_boundary(i32 %x) { +; CHECK-LABEL: @icmp_sdiv_domcond_boundary( +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65 +; CHECK-NEXT: call void @use(i32 [[DIV]]) +; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X]], 194 +; CHECK-NEXT: br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; CHECK: if.true: +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X]], 259 +; CHECK-NEXT: ret i1 [[CMP]] +; CHECK: if.false: +; CHECK-NEXT: ret i1 false +; + %div = sdiv i32 %x, 65 + call void @use(i32 %div) + %cond = icmp sgt i32 %x, 194 + br i1 %cond, label %if.true, label %if.false + +if.true: + %cmp = icmp samesign ugt i32 %div, 3 + ret i1 %cmp + +if.false: + ret i1 false +} + +; Negative test: dividend is known negative, sdiv != udiv. +define i1 @icmp_sdiv_negative_dividend(i32 %x) { +; CHECK-LABEL: @icmp_sdiv_negative_dividend( +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65 +; CHECK-NEXT: call void @use(i32 [[DIV]]) +; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X]], -100 +; CHECK-NEXT: br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; CHECK: if.true: +; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i32 [[DIV]], 2 +; CHECK-NEXT: ret i1 [[CMP]] +; CHECK: if.false: +; CHECK-NEXT: ret i1 false +; + %div = sdiv i32 %x, 65 + call void @use(i32 %div) + %cond = icmp slt i32 %x, -100 + br i1 %cond, label %if.true, label %if.false + +if.true: + %cmp = icmp samesign ugt i32 %div, 2 + ret i1 %cmp + +if.false: + ret i1 false +} + +; Negative test: divisor is negative, sdiv(x, -65) != udiv(x, -65). +define i1 @icmp_sdiv_negative_divisor(i32 %x) { +; CHECK-LABEL: @icmp_sdiv_negative_divisor( +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[X:%.*]], -65 +; CHECK-NEXT: call void @use(i32 [[DIV]]) +; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X]], 255 +; CHECK-NEXT: br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; CHECK: if.true: +; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i32 [[DIV]], 2 +; CHECK-NEXT: ret i1 [[CMP]] +; CHECK: if.false: +; CHECK-NEXT: ret i1 false +; + %div = sdiv i32 %x, -65 + call void @use(i32 %div) + %cond = icmp sgt i32 %x, 255 + br i1 %cond, label %if.true, label %if.false + +if.true: + %cmp = icmp samesign ugt i32 %div, 2 + ret i1 %cmp + +if.false: + ret i1 false +}