[flang-commits] [flang] [flang] Use saturated intrinsic for floating point conversions (PR #130686)
via flang-commits
flang-commits at lists.llvm.org
Mon Mar 10 16:46:46 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-codegen
Author: Asher Mancinelli (ashermancinelli)
<details>
<summary>Changes</summary>
The saturated floating point conversion intrinsics more closely match the semantics in the standard.
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 other compilers).
---
Full diff: https://github.com/llvm/llvm-project/pull/130686.diff
3 Files Affected:
- (modified) flang/lib/Optimizer/CodeGen/CodeGen.cpp (+14-4)
- (modified) flang/test/Fir/convert-to-llvm.fir (+13-5)
- (added) flang/test/Integration/fp-convert.f90 (+236)
``````````diff
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index a2743edd7844a..2302c08fae508 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
// -----
diff --git a/flang/test/Integration/fp-convert.f90 b/flang/test/Integration/fp-convert.f90
new file mode 100644
index 0000000000000..a042b28827b9a
--- /dev/null
+++ b/flang/test/Integration/fp-convert.f90
@@ -0,0 +1,236 @@
+! RUN: %flang -funsigned %s -o %t && %t | FileCheck %s
+! RUN: %flang -funsigned -emit-llvm -S -o - %s | FileCheck %s --check-prefix=LLVMIR
+
+module fp_convert_m
+ implicit none
+ interface set_and_print
+ module procedure set_and_print_r16
+ module procedure set_and_print_r8
+ end interface
+contains
+ subroutine set_and_print_r16(value)
+ real(kind=16), intent(in) :: value
+ integer(kind=1) :: i8
+ integer(kind=2) :: i16
+ integer(kind=4) :: i32
+ integer(kind=8) :: i64
+ integer(kind=16) :: i128
+ unsigned(kind=1) :: u8
+ unsigned(kind=2) :: u16
+ unsigned(kind=4) :: u32
+ unsigned(kind=8) :: u64
+ unsigned(kind=16) :: u128
+ print *, "Original real(16) value:", value
+ i8 = int(value, kind=1)
+ i16 = int(value, kind=2)
+ i32 = int(value, kind=4)
+ i64 = int(value, kind=8)
+ i128 = int(value, kind=16)
+ u8 = uint(value, kind=1)
+ u16 = uint(value, kind=2)
+ u32 = uint(value, kind=4)
+ u64 = uint(value, kind=8)
+ u128 = uint(value, kind=16)
+ print *, "Converted to 8-bit integer:", i8
+ print *, "Converted to 16-bit integer:", i16
+ print *, "Converted to 32-bit integer:", i32
+ print *, "Converted to 64-bit integer:", i64
+ print *, "Converted to 128-bit integer:", i128
+ print *, "Converted to 8-bit unsigned integer:", u8
+ print *, "Converted to 16-bit unsigned integer:", u16
+ print *, "Converted to 32-bit unsigned integer:", u32
+ print *, "Converted to 64-bit unsigned integer:", u64
+ print *, "Converted to 128-bit unsigned integer:", u128
+ end subroutine
+
+ subroutine set_and_print_r8(value)
+ real(kind=8), intent(in) :: value
+ integer(kind=1) :: i8
+ integer(kind=2) :: i16
+ integer(kind=4) :: i32
+ integer(kind=8) :: i64
+ integer(kind=16) :: i128
+ unsigned(kind=1) :: u8
+ unsigned(kind=2) :: u16
+ unsigned(kind=4) :: u32
+ unsigned(kind=8) :: u64
+ unsigned(kind=16) :: u128
+ print *, "Original real(8) value:", value
+ i8 = int(value, kind=1)
+ i16 = int(value, kind=2)
+ i32 = int(value, kind=4)
+ i64 = int(value, kind=8)
+ i128 = int(value, kind=16)
+ u8 = uint(value, kind=1)
+ u16 = uint(value, kind=2)
+ u32 = uint(value, kind=4)
+ u64 = uint(value, kind=8)
+ u128 = uint(value, kind=16)
+ print *, "Converted to 8-bit integer:", i8
+ print *, "Converted to 16-bit integer:", i16
+ print *, "Converted to 32-bit integer:", i32
+ print *, "Converted to 64-bit integer:", i64
+ print *, "Converted to 128-bit integer:", i128
+ print *, "Converted to 8-bit unsigned integer:", u8
+ print *, "Converted to 16-bit unsigned integer:", u16
+ print *, "Converted to 32-bit unsigned integer:", u32
+ print *, "Converted to 64-bit unsigned integer:", u64
+ print *, "Converted to 128-bit unsigned integer:", u128
+ end subroutine
+end module fp_convert_m
+
+program fp_convert
+ use ieee_arithmetic, only: ieee_value, ieee_quiet_nan, ieee_positive_inf, ieee_negative_inf
+ use fp_convert_m, only: set_and_print
+ implicit none
+
+ real(kind=8) :: nan, inf, ninf
+ nan = ieee_value(nan, ieee_quiet_nan)
+ inf = ieee_value(inf, ieee_positive_inf)
+ ninf = ieee_value(ninf, ieee_negative_inf)
+
+ call set_and_print(huge(0.0_8))
+ call set_and_print(-huge(0.0_8))
+ call set_and_print(huge(0.0_16))
+ call set_and_print(-huge(0.0_16))
+ call set_and_print(tiny(0.0_8))
+ call set_and_print(-tiny(0.0_8))
+ call set_and_print(tiny(0.0_16))
+ call set_and_print(-tiny(0.0_16))
+ call set_and_print(nan)
+ call set_and_print(inf)
+ call set_and_print(ninf)
+
+end program fp_convert
+
+! LLVMIR: call i8 @llvm.fptosi.sat.i8.f128(fp128 %{{.+}})
+! LLVMIR: call i16 @llvm.fptosi.sat.i16.f128(fp128 %{{.+}})
+! LLVMIR: call i32 @llvm.fptosi.sat.i32.f128(fp128 %{{.+}})
+! LLVMIR: call i64 @llvm.fptosi.sat.i64.f128(fp128 %{{.+}})
+! LLVMIR: call i128 @llvm.fptosi.sat.i128.f128(fp128 %{{.+}})
+! LLVMIR: call i8 @llvm.fptoui.sat.i8.f128(fp128 %{{.+}})
+! LLVMIR: call i16 @llvm.fptoui.sat.i16.f128(fp128 %{{.+}})
+! LLVMIR: call i32 @llvm.fptoui.sat.i32.f128(fp128 %{{.+}})
+! LLVMIR: call i64 @llvm.fptoui.sat.i64.f128(fp128 %{{.+}})
+! LLVMIR: call i128 @llvm.fptoui.sat.i128.f128(fp128 %{{.+}})
+! LLVMIR: call i8 @llvm.fptosi.sat.i8.f64(double %{{.+}})
+! LLVMIR: call i16 @llvm.fptosi.sat.i16.f64(double %{{.+}})
+! LLVMIR: call i32 @llvm.fptosi.sat.i32.f64(double %{{.+}})
+! LLVMIR: call i64 @llvm.fptosi.sat.i64.f64(double %{{.+}})
+! LLVMIR: call i128 @llvm.fptosi.sat.i128.f64(double %{{.+}})
+! LLVMIR: call i8 @llvm.fptoui.sat.i8.f64(double %{{.+}})
+! LLVMIR: call i16 @llvm.fptoui.sat.i16.f64(double %{{.+}})
+! LLVMIR: call i32 @llvm.fptoui.sat.i32.f64(double %{{.+}})
+! LLVMIR: call i64 @llvm.fptoui.sat.i64.f64(double %{{.+}})
+! LLVMIR: call i128 @llvm.fptoui.sat.i128.f64(double %{{.+}})
+
+! CHECK: Converted to 8-bit integer: 127
+! CHECK: Converted to 16-bit integer: 32767
+! CHECK: Converted to 32-bit integer: 2147483647
+! CHECK: Converted to 64-bit integer: 9223372036854775807
+! CHECK: Converted to 128-bit integer: 170141183460469231731687303715884105727
+! CHECK: Converted to 8-bit unsigned integer: 255
+! CHECK: Converted to 16-bit unsigned integer: 65535
+! CHECK: Converted to 32-bit unsigned integer: 4294967295
+! CHECK: Converted to 64-bit unsigned integer: 18446744073709551615
+! CHECK: Converted to 128-bit unsigned integer: 340282366920938463463374607431768211455
+! CHECK: Converted to 8-bit integer: -128
+! CHECK: Converted to 16-bit integer: -32768
+! CHECK: Converted to 32-bit integer: -2147483648
+! CHECK: Converted to 64-bit integer: -9223372036854775808
+! CHECK: Converted to 128-bit integer: -170141183460469231731687303715884105728
+! CHECK: Converted to 8-bit unsigned integer: 0
+! CHECK: Converted to 16-bit unsigned integer: 0
+! CHECK: Converted to 32-bit unsigned integer: 0
+! CHECK: Converted to 64-bit unsigned integer: 0
+! CHECK: Converted to 128-bit unsigned integer: 0
+! CHECK: Converted to 8-bit integer: 127
+! CHECK: Converted to 16-bit integer: 32767
+! CHECK: Converted to 32-bit integer: 2147483647
+! CHECK: Converted to 64-bit integer: 9223372036854775807
+! CHECK: Converted to 128-bit integer: 170141183460469231731687303715884105727
+! CHECK: Converted to 8-bit unsigned integer: 255
+! CHECK: Converted to 16-bit unsigned integer: 65535
+! CHECK: Converted to 32-bit unsigned integer: 4294967295
+! CHECK: Converted to 64-bit unsigned integer: 18446744073709551615
+! CHECK: Converted to 128-bit unsigned integer: 340282366920938463463374607431768211455
+! CHECK: Converted to 8-bit integer: -128
+! CHECK: Converted to 16-bit integer: -32768
+! CHECK: Converted to 32-bit integer: -2147483648
+! CHECK: Converted to 64-bit integer: -9223372036854775808
+! CHECK: Converted to 128-bit integer: -170141183460469231731687303715884105728
+! CHECK: Converted to 8-bit unsigned integer: 0
+! CHECK: Converted to 16-bit unsigned integer: 0
+! CHECK: Converted to 32-bit unsigned integer: 0
+! CHECK: Converted to 64-bit unsigned integer: 0
+! CHECK: Converted to 128-bit unsigned integer: 0
+! CHECK: Converted to 8-bit integer: 0
+! CHECK: Converted to 16-bit integer: 0
+! CHECK: Converted to 32-bit integer: 0
+! CHECK: Converted to 64-bit integer: 0
+! CHECK: Converted to 128-bit integer: 0
+! CHECK: Converted to 8-bit unsigned integer: 0
+! CHECK: Converted to 16-bit unsigned integer: 0
+! CHECK: Converted to 32-bit unsigned integer: 0
+! CHECK: Converted to 64-bit unsigned integer: 0
+! CHECK: Converted to 128-bit unsigned integer: 0
+! CHECK: Converted to 8-bit integer: 0
+! CHECK: Converted to 16-bit integer: 0
+! CHECK: Converted to 32-bit integer: 0
+! CHECK: Converted to 64-bit integer: 0
+! CHECK: Converted to 128-bit integer: 0
+! CHECK: Converted to 8-bit unsigned integer: 0
+! CHECK: Converted to 16-bit unsigned integer: 0
+! CHECK: Converted to 32-bit unsigned integer: 0
+! CHECK: Converted to 64-bit unsigned integer: 0
+! CHECK: Converted to 128-bit unsigned integer: 0
+! CHECK: Converted to 8-bit integer: 0
+! CHECK: Converted to 16-bit integer: 0
+! CHECK: Converted to 32-bit integer: 0
+! CHECK: Converted to 64-bit integer: 0
+! CHECK: Converted to 128-bit integer: 0
+! CHECK: Converted to 8-bit unsigned integer: 0
+! CHECK: Converted to 16-bit unsigned integer: 0
+! CHECK: Converted to 32-bit unsigned integer: 0
+! CHECK: Converted to 64-bit unsigned integer: 0
+! CHECK: Converted to 128-bit unsigned integer: 0
+! CHECK: Converted to 8-bit integer: 0
+! CHECK: Converted to 16-bit integer: 0
+! CHECK: Converted to 32-bit integer: 0
+! CHECK: Converted to 64-bit integer: 0
+! CHECK: Converted to 128-bit integer: 0
+! CHECK: Converted to 8-bit unsigned integer: 0
+! CHECK: Converted to 16-bit unsigned integer: 0
+! CHECK: Converted to 32-bit unsigned integer: 0
+! CHECK: Converted to 64-bit unsigned integer: 0
+! CHECK: Converted to 128-bit unsigned integer: 0
+! CHECK: Converted to 8-bit integer: 0
+! CHECK: Converted to 16-bit integer: 0
+! CHECK: Converted to 32-bit integer: 0
+! CHECK: Converted to 64-bit integer: 0
+! CHECK: Converted to 128-bit integer: 0
+! CHECK: Converted to 8-bit unsigned integer: 0
+! CHECK: Converted to 16-bit unsigned integer: 0
+! CHECK: Converted to 32-bit unsigned integer: 0
+! CHECK: Converted to 64-bit unsigned integer: 0
+! CHECK: Converted to 128-bit unsigned integer: 0
+! CHECK: Converted to 8-bit integer: 127
+! CHECK: Converted to 16-bit integer: 32767
+! CHECK: Converted to 32-bit integer: 2147483647
+! CHECK: Converted to 64-bit integer: 9223372036854775807
+! CHECK: Converted to 128-bit integer: 170141183460469231731687303715884105727
+! CHECK: Converted to 8-bit unsigned integer: 255
+! CHECK: Converted to 16-bit unsigned integer: 65535
+! CHECK: Converted to 32-bit unsigned integer: 4294967295
+! CHECK: Converted to 64-bit unsigned integer: 18446744073709551615
+! CHECK: Converted to 128-bit unsigned integer: 340282366920938463463374607431768211455
+! CHECK: Converted to 8-bit integer: -128
+! CHECK: Converted to 16-bit integer: -32768
+! CHECK: Converted to 32-bit integer: -2147483648
+! CHECK: Converted to 64-bit integer: -9223372036854775808
+! CHECK: Converted to 128-bit integer: -170141183460469231731687303715884105728
+! CHECK: Converted to 8-bit unsigned integer: 0
+! CHECK: Converted to 16-bit unsigned integer: 0
+! CHECK: Converted to 32-bit unsigned integer: 0
+! CHECK: Converted to 64-bit unsigned integer: 0
+! CHECK: Converted to 128-bit unsigned integer: 0
``````````
</details>
https://github.com/llvm/llvm-project/pull/130686
More information about the flang-commits
mailing list