Skip to content

Commit 982527e

Browse files
[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.
1 parent 8be1d12 commit 982527e

File tree

2 files changed

+27
-9
lines changed

2 files changed

+27
-9
lines changed

Diff for: flang/lib/Optimizer/CodeGen/CodeGen.cpp

+14-4
Original file line numberDiff line numberDiff line change
@@ -835,10 +835,20 @@ struct ConvertOpConversion : public fir::FIROpConversion<fir::ConvertOp> {
835835
return mlir::success();
836836
}
837837
if (mlir::isa<mlir::IntegerType>(toTy)) {
838-
if (toTy.isUnsignedInteger())
839-
rewriter.replaceOpWithNewOp<mlir::LLVM::FPToUIOp>(convert, toTy, op0);
840-
else
841-
rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
838+
// NOTE: We are checking the fir type here because toTy is an LLVM type
839+
// which is signless, and we need to use the intrinsic that matches the
840+
// sign of the output in fir.
841+
if (toFirTy.isUnsignedInteger()) {
842+
auto intrinsicName =
843+
mlir::StringAttr::get(convert.getContext(), "llvm.fptoui.sat");
844+
rewriter.replaceOpWithNewOp<mlir::LLVM::CallIntrinsicOp>(
845+
convert, toTy, intrinsicName, op0);
846+
} else {
847+
auto intrinsicName =
848+
mlir::StringAttr::get(convert.getContext(), "llvm.fptosi.sat");
849+
rewriter.replaceOpWithNewOp<mlir::LLVM::CallIntrinsicOp>(
850+
convert, toTy, intrinsicName, op0);
851+
}
842852
return mlir::success();
843853
}
844854
} else if (mlir::isa<mlir::IntegerType>(fromTy)) {

Diff for: flang/test/Fir/convert-to-llvm.fir

+13-5
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,10 @@ func.func @convert_from_float(%arg0 : f32) {
701701
%7 = fir.convert %arg0 : (f32) -> i16
702702
%8 = fir.convert %arg0 : (f32) -> i32
703703
%9 = fir.convert %arg0 : (f32) -> i64
704+
%10 = fir.convert %arg0 : (f32) -> ui8
705+
%11 = fir.convert %arg0 : (f32) -> ui16
706+
%12 = fir.convert %arg0 : (f32) -> ui32
707+
%13 = fir.convert %arg0 : (f32) -> ui64
704708
return
705709
}
706710

@@ -711,11 +715,15 @@ func.func @convert_from_float(%arg0 : f32) {
711715
// CHECK: %{{.*}} = llvm.fpext %[[ARG0]] : f32 to f64
712716
// CHECK: %{{.*}} = llvm.fpext %[[ARG0]] : f32 to f80
713717
// CHECK: %{{.*}} = llvm.fpext %[[ARG0]] : f32 to f128
714-
// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i1
715-
// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i8
716-
// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i16
717-
// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i32
718-
// CHECK: %{{.*}} = llvm.fptosi %[[ARG0]] : f32 to i64
718+
// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i1
719+
// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i8
720+
// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i16
721+
// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i32
722+
// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptosi.sat"(%[[ARG0]]) : (f32) -> i64
723+
// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptoui.sat"(%[[ARG0]]) : (f32) -> i8
724+
// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptoui.sat"(%[[ARG0]]) : (f32) -> i16
725+
// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptoui.sat"(%[[ARG0]]) : (f32) -> i32
726+
// CHECK: %{{.*}} = llvm.call_intrinsic "llvm.fptoui.sat"(%[[ARG0]]) : (f32) -> i64
719727

720728
// -----
721729

0 commit comments

Comments
 (0)