Skip to content

[CVP] Infer nuw/nsw flags for TruncInst #130504

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

Merged
merged 4 commits into from
Mar 12, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang/test/CodeGen/attr-counted-by.c
Original file line number Diff line number Diff line change
@@ -333,7 +333,7 @@ size_t test3_bdos(struct annotated *p) {
// SANITIZE-WITH-ATTR-NEXT: [[COUNT50:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD44]] to i64
// SANITIZE-WITH-ATTR-NEXT: [[TMP10:%.*]] = sub nsw i64 [[COUNT50]], [[IDXPROM42]]
// SANITIZE-WITH-ATTR-NEXT: [[TMP11:%.*]] = tail call i64 @llvm.smax.i64(i64 [[TMP10]], i64 0)
// SANITIZE-WITH-ATTR-NEXT: [[DOTTR:%.*]] = trunc nuw i64 [[TMP11]] to i32
// SANITIZE-WITH-ATTR-NEXT: [[DOTTR:%.*]] = trunc nuw nsw i64 [[TMP11]] to i32
// SANITIZE-WITH-ATTR-NEXT: [[CONV54:%.*]] = shl i32 [[DOTTR]], 2
// SANITIZE-WITH-ATTR-NEXT: [[CONV55:%.*]] = and i32 [[CONV54]], 252
// SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV55]], ptr [[ARRAYIDX65]], align 4, !tbaa [[TBAA4]]
31 changes: 31 additions & 0 deletions llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
Original file line number Diff line number Diff line change
@@ -1212,6 +1212,34 @@ static bool processAnd(BinaryOperator *BinOp, LazyValueInfo *LVI) {
return true;
}

static bool processTrunc(TruncInst *TI, LazyValueInfo *LVI) {
if (TI->hasNoSignedWrap() && TI->hasNoUnsignedWrap())
return false;

ConstantRange Range =
LVI->getConstantRangeAtUse(TI->getOperandUse(0), /*UndefAllowed=*/false);
uint64_t DestWidth = TI->getDestTy()->getScalarSizeInBits();
bool Changed = false;

if (!TI->hasNoUnsignedWrap()) {
if (Range.getActiveBits() <= DestWidth) {
TI->setHasNoUnsignedWrap(true);
++NumNUW;
Changed = true;
}
}

if (!TI->hasNoSignedWrap()) {
if (Range.getMinSignedBits() <= DestWidth) {
TI->setHasNoSignedWrap(true);
++NumNSW;
Changed = true;
}
}

return Changed;
}

static bool runImpl(Function &F, LazyValueInfo *LVI, DominatorTree *DT,
const SimplifyQuery &SQ) {
bool FnChanged = false;
@@ -1275,6 +1303,9 @@ static bool runImpl(Function &F, LazyValueInfo *LVI, DominatorTree *DT,
case Instruction::And:
BBChanged |= processAnd(cast<BinaryOperator>(&II), LVI);
break;
case Instruction::Trunc:
BBChanged |= processTrunc(cast<TruncInst>(&II), LVI);
break;
}
}

2 changes: 1 addition & 1 deletion llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll
Original file line number Diff line number Diff line change
@@ -339,7 +339,7 @@ define i1 @test12(i32 %x) {
; CHECK-NEXT: [[ZEXT:%.*]] = zext i32 [[X:%.*]] to i64
; CHECK-NEXT: [[MUL:%.*]] = mul nuw nsw i64 [[ZEXT]], 7
; CHECK-NEXT: [[SHR:%.*]] = lshr i64 [[MUL]], 32
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i64 [[SHR]] to i32
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw nsw i64 [[SHR]] to i32
; CHECK-NEXT: ret i1 true
;
%zext = zext i32 %x to i64
4 changes: 2 additions & 2 deletions llvm/test/Transforms/CorrelatedValuePropagation/range.ll
Original file line number Diff line number Diff line change
@@ -937,7 +937,7 @@ entry:
define i1 @ctlz_with_range_metadata(i16 %x) {
; CHECK-LABEL: @ctlz_with_range_metadata(
; CHECK-NEXT: [[CTLZ:%.*]] = call i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false), !range [[RNG5:![0-9]+]]
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i16 [[CTLZ]] to i8
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw nsw i16 [[CTLZ]] to i8
; CHECK-NEXT: ret i1 true
;
%ctlz = call i16 @llvm.ctlz.i16(i16 %x, i1 false), !range !{i16 0, i16 8}
@@ -949,7 +949,7 @@ define i1 @ctlz_with_range_metadata(i16 %x) {
define i1 @abs_with_range_metadata(i16 %x) {
; CHECK-LABEL: @abs_with_range_metadata(
; CHECK-NEXT: [[ABS:%.*]] = call i16 @llvm.abs.i16(i16 [[X:%.*]], i1 false), !range [[RNG5]]
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i16 [[ABS]] to i8
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw nsw i16 [[ABS]] to i8
; CHECK-NEXT: ret i1 true
;
%abs = call i16 @llvm.abs.i16(i16 %x, i1 false), !range !{i16 0, i16 8}
8 changes: 4 additions & 4 deletions llvm/test/Transforms/CorrelatedValuePropagation/sext.ll
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ define void @test1(i32 %n) {
; CHECK: for.body:
; CHECK-NEXT: [[EXT_WIDE1:%.*]] = zext nneg i32 [[A]] to i64
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE1]])
; CHECK-NEXT: [[EXT]] = trunc i64 [[EXT_WIDE1]] to i32
; CHECK-NEXT: [[EXT]] = trunc nuw nsw i64 [[EXT_WIDE1]] to i32
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: ret void
@@ -55,7 +55,7 @@ define void @test2(i32 %n) {
; CHECK: for.body:
; CHECK-NEXT: [[EXT_WIDE:%.*]] = sext i32 [[A]] to i64
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
; CHECK-NEXT: [[EXT]] = trunc i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: [[EXT]] = trunc nsw i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: ret void
@@ -87,7 +87,7 @@ define void @test3(i32 %n) {
; CHECK: bb:
; CHECK-NEXT: [[EXT_WIDE1:%.*]] = zext nneg i32 [[N]] to i64
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE1]])
; CHECK-NEXT: [[EXT:%.*]] = trunc i64 [[EXT_WIDE1]] to i32
; CHECK-NEXT: [[EXT:%.*]] = trunc nuw nsw i64 [[EXT_WIDE1]] to i32
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
@@ -115,7 +115,7 @@ define void @test4(i32 %n) {
; CHECK: bb:
; CHECK-NEXT: [[EXT_WIDE:%.*]] = sext i32 [[N]] to i64
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
; CHECK-NEXT: [[EXT:%.*]] = trunc i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: [[EXT:%.*]] = trunc nsw i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
108 changes: 108 additions & 0 deletions llvm/test/Transforms/CorrelatedValuePropagation/trunc.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s


define i1 @infer_nuw(i8 range(i8 0, 2) %A, i8 range(i8 0, 2) %B) {
; CHECK-LABEL: define i1 @infer_nuw(
; CHECK-SAME: i8 range(i8 0, 2) [[A:%.*]], i8 range(i8 0, 2) [[B:%.*]]) {
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[B]], [[A]]
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i8 [[XOR]] to i1
; CHECK-NEXT: ret i1 [[TRUNC]]
;
%xor = xor i8 %B, %A
%trunc = trunc i8 %xor to i1
ret i1 %trunc
}


define i4 @infer_nsw(i8 %A) {
; CHECK-LABEL: define range(i4 -4, 4) i4 @infer_nsw(
; CHECK-SAME: i8 [[A:%.*]]) {
; CHECK-NEXT: [[ASHR:%.*]] = ashr i8 [[A]], 5
; CHECK-NEXT: [[B:%.*]] = trunc nsw i8 [[ASHR]] to i4
; CHECK-NEXT: ret i4 [[B]]
;
%ashr = ashr i8 %A, 5
%result = trunc i8 %ashr to i4
ret i4 %result
}


define i8 @infer_nuw_nsw(i16 range(i16 -5, -3) %A, i16 range(i16 -5, -3) %B) {
; CHECK-LABEL: define range(i8 0, 8) i8 @infer_nuw_nsw(
; CHECK-SAME: i16 range(i16 -5, -3) [[A:%.*]], i16 range(i16 -5, -3) [[B:%.*]]) {
; CHECK-NEXT: [[XOR:%.*]] = xor i16 [[B]], [[A]]
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw nsw i16 [[XOR]] to i8
; CHECK-NEXT: ret i8 [[TRUNC]]
;
%xor = xor i16 %B, %A
%trunc = trunc i16 %xor to i8
ret i8 %trunc
}


define i8 @infer_nsw_from_assume(i16 %x) {
; CHECK-LABEL: define i8 @infer_nsw_from_assume(
; CHECK-SAME: i16 [[X:%.*]]) {
; CHECK-NEXT: [[ADD:%.*]] = add i16 [[X]], 128
; CHECK-NEXT: [[OR_COND_I:%.*]] = icmp ult i16 [[ADD]], 256
; CHECK-NEXT: tail call void @llvm.assume(i1 [[OR_COND_I]])
; CHECK-NEXT: [[CONV1:%.*]] = trunc nsw i16 [[X]] to i8
; CHECK-NEXT: ret i8 [[CONV1]]
;
%add = add i16 %x, 128
%or.cond.i = icmp ult i16 %add, 256
tail call void @llvm.assume(i1 %or.cond.i)
%conv1 = trunc i16 %x to i8
ret i8 %conv1
}


define i1 @rust_issue_122734(i8 range(i8 0, 3) %A, i8 range(i8 0, 3) %B) {
; CHECK-LABEL: define i1 @rust_issue_122734(
; CHECK-SAME: i8 range(i8 0, 3) [[A:%.*]], i8 range(i8 0, 3) [[B:%.*]]) {
; CHECK-NEXT: [[START:.*]]:
; CHECK-NEXT: [[LHS:%.*]] = icmp eq i8 [[A]], 2
; CHECK-NEXT: [[RHS:%.*]] = icmp eq i8 [[B]], 2
; CHECK-NEXT: [[OR:%.*]] = or i1 [[LHS]], [[RHS]]
; CHECK-NEXT: [[AND:%.*]] = and i1 [[LHS]], [[RHS]]
; CHECK-NEXT: br i1 [[OR]], label %[[IFTRUE:.*]], label %[[IFFALSE:.*]]
; CHECK: [[IFTRUE]]:
; CHECK-NEXT: [[PHI:%.*]] = phi i1 [ [[XOR2:%.*]], %[[IFFALSE]] ], [ [[AND]], %[[START]] ]
; CHECK-NEXT: ret i1 [[PHI]]
; CHECK: [[IFFALSE]]:
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[A]], [[B]]
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i8 [[XOR]] to i1
; CHECK-NEXT: [[XOR2]] = xor i1 [[TRUNC]], true
; CHECK-NEXT: br label %[[IFTRUE]]
;
start:
%lhs = icmp eq i8 %A, 2
%rhs = icmp eq i8 %B, 2
%or = or i1 %lhs, %rhs
%and = and i1 %lhs, %rhs
br i1 %or, label %iftrue, label %iffalse

iftrue:
%phi = phi i1 [ %xor2, %iffalse], [ %and, %start ]
ret i1 %phi

iffalse:
%xor = xor i8 %A, %B
%trunc = trunc i8 %xor to i1
%xor2 = xor i1 %trunc, true
br label %iftrue
}


define i1 @overdefined_range_negative(i8 %A, i8 %B) {
; CHECK-LABEL: define i1 @overdefined_range_negative(
; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[A]], [[B]]
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i8 [[XOR]] to i1
; CHECK-NEXT: ret i1 [[TRUNC]]
;
%xor = xor i8 %A, %B
%trunc = trunc i8 %xor to i1
ret i1 %trunc
}
8 changes: 4 additions & 4 deletions llvm/test/Transforms/CorrelatedValuePropagation/zext.ll
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ define void @test1(i32 %n) {
; CHECK: for.body:
; CHECK-NEXT: [[EXT_WIDE:%.*]] = zext nneg i32 [[A]] to i64
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
; CHECK-NEXT: [[EXT]] = trunc i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: [[EXT]] = trunc nuw nsw i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: ret void
@@ -49,7 +49,7 @@ define void @test2(i32 %n) {
; CHECK: for.body:
; CHECK-NEXT: [[EXT_WIDE:%.*]] = zext i32 [[A]] to i64
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
; CHECK-NEXT: [[EXT]] = trunc i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: [[EXT]] = trunc nuw i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: ret void
@@ -81,7 +81,7 @@ define void @test3(i32 %n) {
; CHECK: bb:
; CHECK-NEXT: [[EXT_WIDE:%.*]] = zext nneg i32 [[N]] to i64
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
; CHECK-NEXT: [[EXT:%.*]] = trunc i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: [[EXT:%.*]] = trunc nuw nsw i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
@@ -109,7 +109,7 @@ define void @test4(i32 %n) {
; CHECK: bb:
; CHECK-NEXT: [[EXT_WIDE:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
; CHECK-NEXT: [[EXT:%.*]] = trunc i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: [[EXT:%.*]] = trunc nuw i64 [[EXT_WIDE]] to i32
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void