[flang-commits] [flang] 982527e - [flang] Use saturated intrinsics for floating point to integer conversions (#130686)
via flang-commits
flang-commits at lists.llvm.org
Wed Mar 12 08:14:52 PDT 2025
Author: Asher Mancinelli
Date: 2025-03-12T08:14:46-07:00
New Revision: 982527eef0f284161dd5e09a9b4fc952b332ec92
URL: https://github.com/llvm/llvm-project/commit/982527eef0f284161dd5e09a9b4fc952b332ec92
DIFF: https://github.com/llvm/llvm-project/commit/982527eef0f284161dd5e09a9b4fc952b332ec92.diff
LOG: [flang] Use saturated intrinsics for floating point to integer conversions (#130686)
The saturated floating point conversion intrinsics match the semantics in the standard more closely than the fptosi/fptoui instructions.
Case 2 of 16.9.100 is
> INT (A [, KIND])
> If A is of type real, there are two cases: if |A| < 1, INT (A) has the
value 0; if |A| ≥ 1, INT (A) is the integer whose magnitude is the
largest integer that does not exceed the magnitude of A and whose sign
is the same as the sign of A.
Currently, converting a floating point value into an integer type too
small to hold the constant will be converted to poison in opt, leaving
us with garbage:
```
> cat t.f90
program main
real(kind=16) :: f
integer(kind=4) :: i
f=huge(f)
i=f
print *, i
end program main
# current upstream
> for i in `seq 10`; do; ./a.out; done
-862156992
-1497393344
-739096768
-1649494208
1761228608
-1959270592
-746244288
-1629194432
-231217344
382322496
```
With the saturated fptoui/fptosi intrinsics, we get the appropriate
values
```
# mine
> flang -O2 ./t.f90 && ./a.out
2147483647
> perl -e 'printf "%d\n", (2 ** 31) - 1'
2147483647
```
One notable difference: NaNs being converted to ints will become zero, unlike current flang (and some other compilers). Newer versions of GCC have this behavior.
Added:
Modified:
flang/lib/Optimizer/CodeGen/CodeGen.cpp
flang/test/Fir/convert-to-llvm.fir
Removed:
################################################################################
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 5548f5f981b12..2cb4cea58c2b0 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -835,10 +835,20 @@ struct ConvertOpConversion : public fir::FIROpConversion<fir::ConvertOp> {
return mlir::success();
}
if (mlir::isa<mlir::IntegerType>(toTy)) {
- if (toTy.isUnsignedInteger())
- rewriter.replaceOpWithNewOp<mlir::LLVM::FPToUIOp>(convert, toTy, op0);
- else
- rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
+ // NOTE: We are checking the fir type here because toTy is an LLVM type
+ // which is signless, and we need to use the intrinsic that matches the
+ // sign of the output in fir.
+ if (toFirTy.isUnsignedInteger()) {
+ auto intrinsicName =
+ mlir::StringAttr::get(convert.getContext(), "llvm.fptoui.sat");
+ rewriter.replaceOpWithNewOp<mlir::LLVM::CallIntrinsicOp>(
+ convert, toTy, intrinsicName, op0);
+ } else {
+ auto intrinsicName =
+ mlir::StringAttr::get(convert.getContext(), "llvm.fptosi.sat");
+ rewriter.replaceOpWithNewOp<mlir::LLVM::CallIntrinsicOp>(
+ convert, toTy, intrinsicName, op0);
+ }
return mlir::success();
}
} else if (mlir::isa<mlir::IntegerType>(fromTy)) {
diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir
index c7037019ee701..2960528fb6c24 100644
--- a/flang/test/Fir/convert-to-llvm.fir
+++ b/flang/test/Fir/convert-to-llvm.fir
@@ -701,6 +701,10 @@ func.func @convert_from_float(%arg0 : f32) {
%7 = fir.convert %arg0 : (f32) -> i16
%8 = fir.convert %arg0 : (f32) -> i32
%9 = fir.convert %arg0 : (f32) -> i64
+ %10 = fir.convert %arg0 : (f32) -> ui8
+ %11 = fir.convert %arg0 : (f32) -> ui16
+ %12 = fir.convert %arg0 : (f32) -> ui32
+ %13 = fir.convert %arg0 : (f32) -> ui64
return
}
@@ -711,11 +715,15 @@ func.func @convert_from_float(%arg0 : f32) {
// CHECK: %{{.*}} = llvm.fpext %[[ARG0]] : f32 to f64
// CHECK: %{{.*}} = llvm.fpext %[[ARG0]] : f32 to f80
// CHECK: %{{.*}} = llvm.fpext %[[ARG0]] : f32 to f128
-// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i1
-// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i8
-// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i16
-// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i32
-// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i64
+// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i1
+// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i8
+// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i16
+// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i32
+// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i64
+// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptoui.sat"(%[[ARG0]]) : (f32) -> i8
+// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptoui.sat"(%[[ARG0]]) : (f32) -> i16
+// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptoui.sat"(%[[ARG0]]) : (f32) -> i32
+// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptoui.sat"(%[[ARG0]]) : (f32) -> i64
// -----
More information about the flang-commits
mailing list