[llvm] [IndVarSimplify] Ensure fp values can be represented as consecutive integers (PR #166649)
Antonio Frighetto via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 5 13:56:25 PST 2025
https://github.com/antoniofrighetto created https://github.com/llvm/llvm-project/pull/166649
When transforming floating-point induction variables into integer ones, make sure we stay within the bounds of fp values that can be represented as integers without gaps, i.e., 2^24 and 2^53 for IEEE-754 single and double precision respectively (both on negative and positive side).
Fixes: https://github.com/llvm/llvm-project/issues/166496.
>From 8a2ef9135a0015ebc90955b362914e9d7a6395cd Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Wed, 5 Nov 2025 20:20:17 +0100
Subject: [PATCH 1/2] [IndVarSimplify] Precommit tests (NFC)
---
.../IndVarSimplify/floating-point-iv.ll | 84 +++++++++++++++++++
1 file changed, 84 insertions(+)
diff --git a/llvm/test/Transforms/IndVarSimplify/floating-point-iv.ll b/llvm/test/Transforms/IndVarSimplify/floating-point-iv.ll
index b1ef50382c070..353ad7f7c0b53 100644
--- a/llvm/test/Transforms/IndVarSimplify/floating-point-iv.ll
+++ b/llvm/test/Transforms/IndVarSimplify/floating-point-iv.ll
@@ -417,3 +417,87 @@ loop:
exit:
ret void
}
+
+; FIXME: These are miscompilation issues.
+define void @test_fp_to_int_irrealizable_initval() {
+; CHECK-LABEL: @test_fp_to_int_irrealizable_initval(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[IV_INT:%.*]] = phi i32 [ 100000000, [[ENTRY:%.*]] ], [ [[IV_NEXT_INT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: call void @opaque()
+; CHECK-NEXT: [[IV_NEXT_INT]] = add nsw i32 [[IV_INT]], -17
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ult i32 [[IV_NEXT_INT]], 25
+; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi float [ 1.000000e+08, %entry ], [ %iv.next, %loop ]
+ call void @opaque()
+ %iv.next = fadd float %iv, -1.700000e+01
+ %cmp = fcmp ult float %iv.next, 2.500000e+01
+ br i1 %cmp, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+define void @test_fp_to_int_irrealizable_exitval() {
+; CHECK-LABEL: @test_fp_to_int_irrealizable_exitval(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[IV_INT:%.*]] = phi i32 [ 25, [[ENTRY:%.*]] ], [ [[IV_NEXT_INT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: call void @opaque()
+; CHECK-NEXT: [[IV_NEXT_INT]] = add nuw nsw i32 [[IV_INT]], 17
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i32 [[IV_NEXT_INT]], 100000000
+; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi float [ 2.500000e+01, %entry ], [ %iv.next, %loop ]
+ call void @opaque()
+ %iv.next = fadd float %iv, 1.700000e+01
+ %cmp = fcmp ugt float %iv.next, 1.000000e+08
+ br i1 %cmp, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+define void @test_fp_to_int_irrealizable_negative_exitval() {
+; CHECK-LABEL: @test_fp_to_int_irrealizable_negative_exitval(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[IV_INT:%.*]] = phi i32 [ -25, [[ENTRY:%.*]] ], [ [[IV_NEXT_INT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: call void @opaque()
+; CHECK-NEXT: [[IV_NEXT_INT]] = add nsw i32 [[IV_INT]], -17
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV_NEXT_INT]], -100000000
+; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi float [ -2.500000e+01, %entry ], [ %iv.next, %loop ]
+ call void @opaque()
+ %iv.next = fadd float %iv, -1.700000e+01
+ %cmp = fcmp ult float %iv.next, -1.000000e+08
+ br i1 %cmp, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+declare void @opaque()
>From 1261c5aa77850d5e55d2443defe1b5469f1c005c Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Wed, 5 Nov 2025 22:34:53 +0100
Subject: [PATCH 2/2] [IndVarSimplify] Ensure fp values can be represented as
consecutive integers
When transforming floating-point induction variables into integer ones,
make sure we stay within the bounds of fp values that can be represented
as integers without gaps, i.e., 2^24 and 2^53 for IEEE-754 single and
double precision respectively (both on negative and positive side).
Fixes: https://github.com/llvm/llvm-project/issues/166496.
---
llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 27 +++++++++++++++++++
.../IndVarSimplify/floating-point-iv.ll | 19 +++++++------
2 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index 4ba4ba3850e58..0df8670942212 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -228,6 +228,21 @@ bool IndVarSimplify::handleFloatingPointIV(Loop *L, PHINode *PN) {
!ConvertToSInt(IncValueVal->getValueAPF(), IncValue))
return false;
+ // Ensure we stay within the bounds of fp values that can be represented
+ // as integers without gaps, i.e., 2^24 and 2^53 for IEEE-754 single and
+ // double precision respectively (both on negative and positive side).
+ const auto &SVFltSema = InitValueVal->getValueAPF().getSemantics();
+ if (!APFloat::isIEEELikeFP(SVFltSema))
+ return false;
+
+ uint64_t StartValPrecision = APFloat::semanticsPrecision(SVFltSema);
+ if (StartValPrecision >= 64)
+ return false;
+
+ uint64_t StartValIntegerLimit = 1LL << StartValPrecision;
+ if (uint64_t(std::abs(InitValue)) > StartValIntegerLimit)
+ return false;
+
// Check Incr uses. One user is PN and the other user is an exit condition
// used by the conditional terminator.
Value::user_iterator IncrUse = Incr->user_begin();
@@ -265,6 +280,18 @@ bool IndVarSimplify::handleFloatingPointIV(Loop *L, PHINode *PN) {
!ConvertToSInt(ExitValueVal->getValueAPF(), ExitValue))
return false;
+ const auto &EVFltSema = ExitValueVal->getValueAPF().getSemantics();
+ if (!APFloat::isIEEELikeFP(EVFltSema))
+ return false;
+
+ uint64_t ExitValPrecision = APFloat::semanticsPrecision(EVFltSema);
+ if (ExitValPrecision >= 64)
+ return false;
+
+ uint64_t ExitValIntegerLimit = 1LL << ExitValPrecision;
+ if (uint64_t(std::abs(ExitValue)) > ExitValIntegerLimit)
+ return false;
+
// Find new predicate for integer comparison.
CmpInst::Predicate NewPred = CmpInst::BAD_ICMP_PREDICATE;
switch (Compare->getPredicate()) {
diff --git a/llvm/test/Transforms/IndVarSimplify/floating-point-iv.ll b/llvm/test/Transforms/IndVarSimplify/floating-point-iv.ll
index 353ad7f7c0b53..e3e6f5e1d2a6f 100644
--- a/llvm/test/Transforms/IndVarSimplify/floating-point-iv.ll
+++ b/llvm/test/Transforms/IndVarSimplify/floating-point-iv.ll
@@ -418,16 +418,15 @@ exit:
ret void
}
-; FIXME: These are miscompilation issues.
define void @test_fp_to_int_irrealizable_initval() {
; CHECK-LABEL: @test_fp_to_int_irrealizable_initval(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV_INT:%.*]] = phi i32 [ 100000000, [[ENTRY:%.*]] ], [ [[IV_NEXT_INT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi float [ 1.000000e+08, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: call void @opaque()
-; CHECK-NEXT: [[IV_NEXT_INT]] = add nsw i32 [[IV_INT]], -17
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ult i32 [[IV_NEXT_INT]], 25
+; CHECK-NEXT: [[IV_NEXT]] = fadd float [[IV]], -1.700000e+01
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ult float [[IV_NEXT]], 2.500000e+01
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
@@ -451,10 +450,10 @@ define void @test_fp_to_int_irrealizable_exitval() {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV_INT:%.*]] = phi i32 [ 25, [[ENTRY:%.*]] ], [ [[IV_NEXT_INT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi float [ 2.500000e+01, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: call void @opaque()
-; CHECK-NEXT: [[IV_NEXT_INT]] = add nuw nsw i32 [[IV_INT]], 17
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i32 [[IV_NEXT_INT]], 100000000
+; CHECK-NEXT: [[IV_NEXT]] = fadd float [[IV]], 1.700000e+01
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ugt float [[IV_NEXT]], 1.000000e+08
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
@@ -478,10 +477,10 @@ define void @test_fp_to_int_irrealizable_negative_exitval() {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV_INT:%.*]] = phi i32 [ -25, [[ENTRY:%.*]] ], [ [[IV_NEXT_INT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi float [ -2.500000e+01, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: call void @opaque()
-; CHECK-NEXT: [[IV_NEXT_INT]] = add nsw i32 [[IV_INT]], -17
-; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV_NEXT_INT]], -100000000
+; CHECK-NEXT: [[IV_NEXT]] = fadd float [[IV]], -1.700000e+01
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ult float [[IV_NEXT]], -1.000000e+08
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
More information about the llvm-commits
mailing list