[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 <noreply@anthropic.com>
This commit is contained in:
lijinpei-amd
2026-04-23 17:26:30 +08:00
committed by GitHub
parent 5502053d57
commit de9830fe40
2 changed files with 218 additions and 2 deletions

View File

@@ -2814,8 +2814,15 @@ Instruction *InstCombinerImpl::foldICmpDivConstant(ICmpInst &Cmp,
// (x /u C2) <u C. Simply casting the operands and result won't
// work. :( The if statement below tests that condition and bails
// if it finds it.
if (!Cmp.isEquality() && DivIsSigned != Cmp.isSigned())
return nullptr;
// However, when the divisor is a positive constant and the dividend is
// known non-negative, sdiv is equivalent to udiv, so we can lower
// DivIsSigned and proceed through the unsigned path.
if (!Cmp.isEquality() && DivIsSigned != Cmp.isSigned()) {
if (!DivIsSigned || !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

View File

@@ -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
}