Skip to content

Commit ebb2984

Browse files
authored
[ROO-70] [mlir] [arith] emulate wide int (#3)
Well, emulation pass for `arith.fptoui` and `arith.fptosi`. The basic algorithm looks like this: ```c const double TWO_POW_BW = (uint_64_t(1) << bitwidth); // 2^BW // f is a floating-point value representing the 64-bit number. uint32_t hi = (uint32_t)(f / TWO_POW_BW); // Truncates the division result. uint32_t lo = (uint32_t)(f - hi * TWO_POW_BW); // Subtracts to get the lower bits. ``` `f - hi * TWO_POW_BW` is emitted via `arith.remf`. `arith.fptosi` emits `fptoui` with the absolute value of the input fp. It also does a bounds check and emits `MAX_SIGNED_INT` or `MIN_SIGNED_INT` w.r.t. the sign of the input. I added the runner tests, but here are some remarks: According to LLVM LangRef https://llvm.org/docs/LangRef.html#fptoui-to-instruction "If the value cannot fit in target int type, the result is a poison value.". So basically, the `+-inf` and overflow values - but they result in the same poison value in the case of `fptoui` when we run it with and without emulation (see the integration test). Also, `NaN` values result in different results with `fptosi` and `fptoui`, namely `-2^63(INT64_MIN)` and `-1` respectively. I'm not entirely sure if this is UB/poison or not. Lastly, numbers that are representable with unsigned integers but not with signed ones (`>=2^63` in the case of `int64`) also result in poison-looking numbers: `fptosi(2^63)` emits `-2^63` with the `mlir-cpu-runner` without the emulation, which seems vague to be honest. https://llvm.org/docs/LangRef.html#behavior-of-floating-point-nan-values also does not say much about the behavior of NaN bitcast/conversion ops. https://llvm.org/docs/LangRef.html#llvm-fptoui-sat-intrinsic gives saturating semantics but default lowering does not result in these. I guess not doing anything and leaving it to the specific target/lowering might emit these? Not sure... So if I actually have to handle the cases of `abs(fp) >= MAX_SINT` in `arith.fptosi` and overflows in `arith.fptosi` are unclear. I guess the best course of action here would be that I post the PR upstream and ask for reviews over there, after your rounds of review.
1 parent 8301a96 commit ebb2984

File tree

4 files changed

+392
-1
lines changed

4 files changed

+392
-1
lines changed

mlir/lib/Dialect/Arith/Transforms/EmulateWideInt.cpp

+122-1
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,126 @@ struct ConvertUIToFP final : OpConversionPattern<arith::UIToFPOp> {
974974
}
975975
};
976976

977+
//===----------------------------------------------------------------------===//
978+
// ConvertFPToSI
979+
//===----------------------------------------------------------------------===//
980+
981+
struct ConvertFPToSI final : OpConversionPattern<arith::FPToSIOp> {
982+
using OpConversionPattern::OpConversionPattern;
983+
984+
LogicalResult
985+
matchAndRewrite(arith::FPToSIOp op, OpAdaptor adaptor,
986+
ConversionPatternRewriter &rewriter) const override {
987+
Location loc = op.getLoc();
988+
/* Get the input float type */
989+
auto inFp = adaptor.getIn();
990+
auto fpTy = inFp.getType();
991+
auto fpElemTy = getElementTypeOrSelf(fpTy);
992+
993+
Type intTy = op.getType();
994+
unsigned oldBitWidth = getElementTypeOrSelf(intTy).getIntOrFloatBitWidth();
995+
996+
auto newTy = getTypeConverter()->convertType<VectorType>(intTy);
997+
if (!newTy)
998+
return rewriter.notifyMatchFailure(
999+
loc, llvm::formatv("unsupported type: {0}", intTy));
1000+
1001+
/*
1002+
Work on the absolute value and then convert the result to signed integer.
1003+
Defer absolute value to fptoui. If minSInt < fp < maxSInt, i.e.
1004+
if the fp is representable in signed i2N, emits the correct result.
1005+
Else, the result is UB.
1006+
*/
1007+
TypedAttr zeroAttr = rewriter.getFloatAttr(fpElemTy, 0.0);
1008+
1009+
if (auto vecTy = dyn_cast<VectorType>(fpTy))
1010+
zeroAttr = SplatElementsAttr::get(vecTy, zeroAttr);
1011+
1012+
Value zeroCst = rewriter.create<arith::ConstantOp>(loc, zeroAttr);
1013+
1014+
Value oneCst = createScalarOrSplatConstant(rewriter, loc, intTy, 1);
1015+
Value allOnesCst = createScalarOrSplatConstant(
1016+
rewriter, loc, intTy, APInt::getAllOnes(oldBitWidth));
1017+
1018+
/* Get the absolute value */
1019+
Value isNeg = rewriter.create<arith::CmpFOp>(loc, arith::CmpFPredicate::OLT,
1020+
inFp, zeroCst);
1021+
Value negInFp = rewriter.create<arith::NegFOp>(loc, inFp);
1022+
1023+
Value absVal = rewriter.create<arith::SelectOp>(loc, isNeg, negInFp, inFp);
1024+
1025+
/* Defer the absolute value to fptoui */
1026+
Value res = rewriter.create<arith::FPToUIOp>(loc, intTy, absVal);
1027+
1028+
/* Negate the value if < 0 */
1029+
Value bitwiseNeg = rewriter.create<arith::XOrIOp>(loc, res, allOnesCst);
1030+
Value neg = rewriter.create<arith::AddIOp>(loc, bitwiseNeg, oneCst);
1031+
1032+
rewriter.replaceOpWithNewOp<arith::SelectOp>(op, isNeg, neg, res);
1033+
return success();
1034+
}
1035+
};
1036+
1037+
//===----------------------------------------------------------------------===//
1038+
// ConvertFPToUI
1039+
//===----------------------------------------------------------------------===//
1040+
1041+
struct ConvertFPToUI final : OpConversionPattern<arith::FPToUIOp> {
1042+
using OpConversionPattern::OpConversionPattern;
1043+
1044+
LogicalResult
1045+
matchAndRewrite(arith::FPToUIOp op, OpAdaptor adaptor,
1046+
ConversionPatternRewriter &rewriter) const override {
1047+
Location loc = op.getLoc();
1048+
/* Get the input float type */
1049+
auto inFp = adaptor.getIn();
1050+
auto fpTy = inFp.getType();
1051+
1052+
Type intTy = op.getType();
1053+
auto newTy = getTypeConverter()->convertType<VectorType>(intTy);
1054+
if (!newTy)
1055+
return rewriter.notifyMatchFailure(
1056+
loc, llvm::formatv("unsupported type: {0}", intTy));
1057+
unsigned newBitWidth = newTy.getElementTypeBitWidth();
1058+
Type newHalfType = IntegerType::get(inFp.getContext(), newBitWidth);
1059+
if (auto vecType = dyn_cast<VectorType>(fpTy))
1060+
newHalfType = VectorType::get(vecType.getShape(), newHalfType);
1061+
/*
1062+
The resulting integer has the upper part and the lower part.
1063+
This would be interpreted as 2^N * high + low, where N is the bitwidth.
1064+
Therefore, to calculate the higher part, we emit resHigh = fptoui(fp/2^N).
1065+
For the lower part, we emit fptoui(fp - resHigh * 2^N).
1066+
The special cases of overflows including +-inf, NaNs and negative numbers
1067+
are UB.
1068+
*/
1069+
double powBitwidth = (uint64_t(1) << newBitWidth);
1070+
TypedAttr powBitwidthAttr =
1071+
FloatAttr::get(getElementTypeOrSelf(fpTy), powBitwidth);
1072+
if (auto vecType = dyn_cast<VectorType>(fpTy))
1073+
powBitwidthAttr = SplatElementsAttr::get(vecType, powBitwidthAttr);
1074+
Value powBitwidthFloatCst =
1075+
rewriter.create<arith::ConstantOp>(loc, powBitwidthAttr);
1076+
1077+
Value fpDivPowBitwidth =
1078+
rewriter.create<arith::DivFOp>(loc, inFp, powBitwidthFloatCst);
1079+
Value resHigh =
1080+
rewriter.create<arith::FPToUIOp>(loc, newHalfType, fpDivPowBitwidth);
1081+
// Calculate fp - resHigh * 2^N by getting the remainder of the division
1082+
Value remainder =
1083+
rewriter.create<arith::RemFOp>(loc, inFp, powBitwidthFloatCst);
1084+
Value resLow =
1085+
rewriter.create<arith::FPToUIOp>(loc, newHalfType, remainder);
1086+
1087+
auto high = appendX1Dim(rewriter, loc, resHigh);
1088+
auto low = appendX1Dim(rewriter, loc, resLow);
1089+
1090+
auto resultVec = constructResultVector(rewriter, loc, newTy, {low, high});
1091+
1092+
rewriter.replaceOp(op, resultVec);
1093+
return success();
1094+
}
1095+
};
1096+
9771097
//===----------------------------------------------------------------------===//
9781098
// ConvertTruncI
9791099
//===----------------------------------------------------------------------===//
@@ -1150,5 +1270,6 @@ void arith::populateArithWideIntEmulationPatterns(
11501270
ConvertIndexCastIntToIndex<arith::IndexCastUIOp>,
11511271
ConvertIndexCastIndexToInt<arith::IndexCastOp, arith::ExtSIOp>,
11521272
ConvertIndexCastIndexToInt<arith::IndexCastUIOp, arith::ExtUIOp>,
1153-
ConvertSIToFP, ConvertUIToFP>(typeConverter, patterns.getContext());
1273+
ConvertSIToFP, ConvertUIToFP, ConvertFPToUI, ConvertFPToSI>(
1274+
typeConverter, patterns.getContext());
11541275
}

mlir/test/Dialect/Arith/emulate-wide-int.mlir

+124
Original file line numberDiff line numberDiff line change
@@ -1007,3 +1007,127 @@ func.func @sitofp_i64_f64_vector(%a : vector<3xi64>) -> vector<3xf64> {
10071007
%r = arith.sitofp %a : vector<3xi64> to vector<3xf64>
10081008
return %r : vector<3xf64>
10091009
}
1010+
1011+
// CHECK-LABEL: func @fptoui_i64_f64
1012+
// CHECK-SAME: ([[ARG:%.+]]: f64) -> vector<2xi32>
1013+
// CHECK-NEXT: [[POW:%.+]] = arith.constant 0x41F0000000000000 : f64
1014+
// CHECK-NEXT: [[DIV:%.+]] = arith.divf [[ARG]], [[POW]] : f64
1015+
// CHECK-NEXT: [[HIGHHALF:%.+]] = arith.fptoui [[DIV]] : f64 to i32
1016+
// CHECK-NEXT: [[REM:%.+]] = arith.remf [[ARG]], [[POW]] : f64
1017+
// CHECK-NEXT: [[LOWHALF:%.+]] = arith.fptoui [[REM]] : f64 to i32
1018+
// CHECK: %{{.+}} = vector.insert [[LOWHALF]], %{{.+}} [0]
1019+
// CHECK-NEXT: [[RESVEC:%.+]] = vector.insert [[HIGHHALF]], %{{.+}} [1]
1020+
// CHECK: return [[RESVEC]] : vector<2xi32>
1021+
func.func @fptoui_i64_f64(%a : f64) -> i64 {
1022+
%r = arith.fptoui %a : f64 to i64
1023+
return %r : i64
1024+
}
1025+
1026+
// CHECK-LABEL: func @fptoui_i64_f64_vector
1027+
// CHECK-SAME: ([[ARG:%.+]]: vector<3xf64>) -> vector<3x2xi32>
1028+
// CHECK-NEXT: [[POW:%.+]] = arith.constant dense<0x41F0000000000000> : vector<3xf64>
1029+
// CHECK-NEXT: [[DIV:%.+]] = arith.divf [[ARG]], [[POW]] : vector<3xf64>
1030+
// CHECK-NEXT: [[HIGHHALF:%.+]] = arith.fptoui [[DIV]] : vector<3xf64> to vector<3xi32>
1031+
// CHECK-NEXT: [[REM:%.+]] = arith.remf [[ARG]], [[POW]] : vector<3xf64>
1032+
// CHECK-NEXT: [[LOWHALF:%.+]] = arith.fptoui [[REM]] : vector<3xf64> to vector<3xi32>
1033+
// CHECK-DAG: [[HIGHHALFX1:%.+]] = vector.shape_cast [[HIGHHALF]] : vector<3xi32> to vector<3x1xi32>
1034+
// CHECK-DAG: [[LOWHALFX1:%.+]] = vector.shape_cast [[LOWHALF]] : vector<3xi32> to vector<3x1xi32>
1035+
// CHECK: %{{.+}} = vector.insert_strided_slice [[LOWHALFX1]], %{{.+}} {offsets = [0, 0], strides = [1, 1]}
1036+
// CHECK-NEXT: [[RESVEC:%.+]] = vector.insert_strided_slice [[HIGHHALFX1]], %{{.+}} {offsets = [0, 1], strides = [1, 1]}
1037+
// CHECK: return [[RESVEC]] : vector<3x2xi32>
1038+
func.func @fptoui_i64_f64_vector(%a : vector<3xf64>) -> vector<3xi64> {
1039+
%r = arith.fptoui %a : vector<3xf64> to vector<3xi64>
1040+
return %r : vector<3xi64>
1041+
}
1042+
1043+
// This generates lines that are already verified by other patterns
1044+
// We do not re-verify these and just check for the wrapper around fptoui by following its low part
1045+
// CHECK-LABEL: func @fptosi_i64_f64
1046+
// CHECK-SAME: ([[ARG:%.+]]: f64) -> vector<2xi32>
1047+
// CHECK: [[ZEROCST:%.+]] = arith.constant 0.000000e+00 : f64
1048+
// CHECK: [[ONECST:%.+]] = arith.constant dense<[1, 0]> : vector<2xi32>
1049+
// CHECK: [[ALLONECST:%.+]] = arith.constant dense<-1> : vector<2xi32>
1050+
// CHECK-NEXT: [[ISNEGATIVE:%.+]] = arith.cmpf olt, [[ARG]], [[ZEROCST]] : f64
1051+
// CHECK-NEXT: [[NEGATED:%.+]] = arith.negf [[ARG]] : f64
1052+
// CHECK-NEXT: [[ABSVALUE:%.+]] = arith.select [[ISNEGATIVE]], [[NEGATED]], [[ARG]] : f64
1053+
// CHECK-NEXT: [[POW:%.+]] = arith.constant 0x41F0000000000000 : f64
1054+
// CHECK-NEXT: [[DIV:%.+]] = arith.divf [[ABSVALUE]], [[POW]] : f64
1055+
// CHECK-NEXT: [[HIGHHALF:%.+]] = arith.fptoui [[DIV]] : f64 to i32
1056+
// CHECK-NEXT: [[REM:%.+]] = arith.remf [[ABSVALUE]], [[POW]] : f64
1057+
// CHECK-NEXT: [[LOWHALF:%.+]] = arith.fptoui [[REM]] : f64 to i32
1058+
// CHECK: vector.insert [[LOWHALF]], %{{.+}} [0] : i32 into vector<2xi32>
1059+
// CHECK-NEXT: [[FPTOUIRESVEC:%.+]] = vector.insert [[HIGHHALF]]
1060+
// CHECK: [[ALLONECSTHALF:%.+]] = vector.extract [[ALLONECST]][0] : i32 from vector<2xi32>
1061+
// CHECK: [[XOR:%.+]] = arith.xori %{{.+}}, [[ALLONECSTHALF]] : i32
1062+
// CHECK-NEXT: arith.xori
1063+
// CHECK: vector.insert [[XOR]]
1064+
// CHECK-NEXT: [[XORVEC:%.+]] = vector.insert
1065+
// CHECK: [[XOR:%.+]] = vector.extract [[XORVEC]][0] : i32 from vector<2xi32>
1066+
// CHECK: [[ONECSTHALF:%.+]] = vector.extract [[ONECST]][0] : i32 from vector<2xi32>
1067+
// CHECK: [[SUM:%.+]], %{{.+}} = arith.addui_extended [[XOR]], [[ONECSTHALF]] : i32, i1
1068+
// CHECK-NEXT: arith.extui
1069+
// CHECK-NEXT: arith.addi
1070+
// CHECK-NEXT: arith.addi
1071+
// CHECK: vector.insert [[SUM]]
1072+
// CHECK-NEXT: [[SUMVEC:%.+]] = vector.insert
1073+
// CHECK: [[NEGATEDRES:%.+]] = vector.extract [[SUMVEC]][0] : i32 from vector<2xi32>
1074+
// CHECK: [[LOWRES:%.+]] = vector.extract [[FPTOUIRESVEC]][0] : i32 from vector<2xi32>
1075+
// CHECK: [[ABSRES:%.+]] = arith.select [[ISNEGATIVE]], [[NEGATEDRES]], [[LOWRES]] : i32
1076+
// CHECK-NEXT: arith.select [[ISNEGATIVE]]
1077+
// CHECK: vector.insert [[ABSRES]]
1078+
// CHECK-NEXT: [[ABSRESVEC:%.+]] = vector.insert
1079+
// CHECK-NEXT: return [[ABSRESVEC]] : vector<2xi32>
1080+
func.func @fptosi_i64_f64(%a : f64) -> i64 {
1081+
%r = arith.fptosi %a : f64 to i64
1082+
return %r : i64
1083+
}
1084+
1085+
// Same as the non-vector one, we don't re-verify
1086+
// CHECK-LABEL: func @fptosi_i64_f64_vector
1087+
// CHECK-SAME: ([[ARG:%.+]]: vector<3xf64>) -> vector<3x2xi32>
1088+
// CHECK-NEXT: [[ZEROCST:%.+]] = arith.constant dense<0.000000e+00> : vector<3xf64>
1089+
// CHECK-NEXT: [[ONECST:%.+]] = arith.constant
1090+
// CHECK-SAME{LITERAL} dense<[[1, 0], [1, 0], [1, 0]]> : vector<3x2xi32>
1091+
// CHECK-NEXT: [[ALLONECST:%.+]] = arith.constant dense<-1> : vector<3x2xi32>
1092+
// CHECK-NEXT: [[ISNEGATIVE:%.+]] = arith.cmpf olt, [[ARG]], [[ZEROCST]] : vector<3xf64>
1093+
// CHECK-NEXT: [[NEGATED:%.+]] = arith.negf [[ARG]] : vector<3xf64>
1094+
// CHECK-NEXT: [[ABSVALUE:%.+]] = arith.select [[ISNEGATIVE]], [[NEGATED]], [[ARG]] : vector<3xi1>, vector<3xf64>
1095+
// CHECK-NEXT: [[POW:%.+]] = arith.constant dense<0x41F0000000000000> : vector<3xf64>
1096+
// CHECK-NEXT: [[DIV:%.+]] = arith.divf [[ABSVALUE]], [[POW]] : vector<3xf64>
1097+
// CHECK-NEXT: [[HIGHHALF:%.+]] = arith.fptoui [[DIV]] : vector<3xf64> to vector<3xi32>
1098+
// CHECK-NEXT: [[REM:%.+]] = arith.remf [[ABSVALUE]], [[POW]] : vector<3xf64>
1099+
// CHECK-NEXT: [[LOWHALF:%.+]] = arith.fptoui [[REM]] : vector<3xf64> to vector<3xi32>
1100+
// CHECK-NEXT: [[HIGHHALFX1:%.+]] = vector.shape_cast [[HIGHHALF]] : vector<3xi32> to vector<3x1xi32>
1101+
// CHECK-NEXT: [[LOWHALFX1:%.+]] = vector.shape_cast [[LOWHALF]] : vector<3xi32> to vector<3x1xi32>
1102+
// CHECK: vector.insert_strided_slice [[LOWHALFX1]], %{{.+}} {offsets = [0, 0], strides = [1, 1]} : vector<3x1xi32> into vector<3x2xi32>
1103+
// CHECK-NEXT: [[FPTOUIRESVEC:%.+]] = vector.insert_strided_slice [[HIGHHALFX1]]
1104+
// CHECK: [[ALLONECSTHALF:%.+]] = vector.extract_strided_slice [[ALLONECST]]
1105+
// CHECK-SAME: {offsets = [0, 0], sizes = [3, 1], strides = [1, 1]} : vector<3x2xi32> to vector<3x1xi32>
1106+
// CHECK: [[XOR:%.+]] = arith.xori %{{.+}}, [[ALLONECSTHALF]] : vector<3x1xi32>
1107+
// CHECK-NEXT: arith.xori
1108+
// CHECK: vector.insert_strided_slice [[XOR]]
1109+
// CHECK-NEXT: [[XORVEC:%.+]] = vector.insert_strided_slice
1110+
// CHECK: [[XOR:%.+]] = vector.extract_strided_slice [[XORVEC]]
1111+
// CHECK-SAME: {offsets = [0, 0], sizes = [3, 1], strides = [1, 1]} : vector<3x2xi32> to vector<3x1xi32>
1112+
// CHECK: [[ONECSTHALF:%.+]] = vector.extract_strided_slice [[ONECST]]
1113+
// CHECK-SAME: {offsets = [0, 0], sizes = [3, 1], strides = [1, 1]} : vector<3x2xi32> to vector<3x1xi32>
1114+
// CHECK: [[SUM:%.+]], %{{.+}} = arith.addui_extended [[XOR]], [[ONECSTHALF]] : vector<3x1xi32>, vector<3x1xi1>
1115+
// CHECK-NEXT: arith.extui
1116+
// CHECK-NEXT: arith.addi
1117+
// CHECK-NEXT: arith.addi
1118+
// CHECK: vector.insert_strided_slice [[SUM]]
1119+
// CHECK-NEXT: [[SUMVEC:%.+]] = vector.insert_strided_slice
1120+
// CHECK: [[NEGATEDRES:%.+]] = vector.extract_strided_slice [[SUMVEC]]
1121+
// CHECK-SAME: {offsets = [0, 0], sizes = [3, 1], strides = [1, 1]} : vector<3x2xi32> to vector<3x1xi32>
1122+
// CHECK: [[LOWRES:%.+]] = vector.extract_strided_slice [[FPTOUIRESVEC]]
1123+
// CHECK-SAME: {offsets = [0, 0], sizes = [3, 1], strides = [1, 1]} : vector<3x2xi32> to vector<3x1xi32>
1124+
// CHECK: [[ISNEGATIVEX1:%.+]] = vector.shape_cast [[ISNEGATIVE]] : vector<3xi1> to vector<3x1xi1>
1125+
// CHECK: [[ABSRES:%.+]] = arith.select [[ISNEGATIVEX1]], [[NEGATEDRES]], [[LOWRES]] : vector<3x1xi1>, vector<3x1xi32>
1126+
// CHECK-NEXT: arith.select [[ISNEGATIVEX1]]
1127+
// CHECK: vector.insert_strided_slice [[ABSRES]]
1128+
// CHECK-NEXT: [[ABSRESVEC:%.+]] = vector.insert_strided_slice
1129+
// CHECK-NEXT: return [[ABSRESVEC]] : vector<3x2xi32>
1130+
func.func @fptosi_i64_f64_vector(%a : vector<3xf64>) -> vector<3xi64> {
1131+
%r = arith.fptosi %a : vector<3xf64> to vector<3xi64>
1132+
return %r : vector<3xi64>
1133+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Check that the wide integer `arith.fptosi` emulation produces the same result as wide
2+
// `arith.fptosi`. Emulate i64 ops with i32 ops.
3+
4+
// RUN: mlir-opt %s --convert-scf-to-cf --convert-cf-to-llvm --convert-vector-to-llvm \
5+
// RUN: --convert-func-to-llvm --convert-arith-to-llvm | \
6+
// RUN: mlir-cpu-runner -e entry -entry-point-result=void \
7+
// RUN: --shared-libs=%mlir_c_runner_utils | \
8+
// RUN: FileCheck %s --match-full-lines
9+
10+
// RUN: mlir-opt %s --test-arith-emulate-wide-int="widest-int-supported=32" \
11+
// RUN: --convert-scf-to-cf --convert-cf-to-llvm --convert-vector-to-llvm \
12+
// RUN: --convert-func-to-llvm --convert-arith-to-llvm | \
13+
// RUN: mlir-cpu-runner -e entry -entry-point-result=void \
14+
// RUN: --shared-libs=%mlir_c_runner_utils | \
15+
// RUN: FileCheck %s --match-full-lines
16+
17+
// Ops in this function *only* will be emulated using i32 types.
18+
func.func @emulate_fptosi(%arg: f64) -> i64 {
19+
%res = arith.fptosi %arg : f64 to i64
20+
return %res : i64
21+
}
22+
23+
func.func @check_fptosi(%arg : f64) -> () {
24+
%res = func.call @emulate_fptosi(%arg) : (f64) -> (i64)
25+
vector.print %res : i64
26+
return
27+
}
28+
29+
func.func @entry() {
30+
%cst0 = arith.constant 0.0 : f64
31+
%cst_nzero = arith.constant 0x8000000000000000 : f64
32+
%cst1 = arith.constant 1.0 : f64
33+
%cst_n1 = arith.constant -1.0 : f64
34+
%cst_n1_5 = arith.constant -1.5 : f64
35+
36+
%cstpow20 = arith.constant 1048576.0 : f64
37+
%cstnpow20 = arith.constant -1048576.0 : f64
38+
39+
%cst_i32_max = arith.constant 4294967295.0 : f64
40+
%cst_i32_min = arith.constant -4294967296.0 : f64
41+
%cst_i32_overflow = arith.constant 4294967296.0 : f64
42+
%cst_i32_noverflow = arith.constant -4294967297.0 : f64
43+
44+
45+
%cstpow40 = arith.constant 1099511627776.0 : f64
46+
%cstnpow40 = arith.constant -1099511627776.0 : f64
47+
%cst_pow40ppow20 = arith.constant 1099512676352.0 : f64
48+
%cst_npow40ppow20 = arith.constant -1099512676352.0 : f64
49+
50+
// CHECK: 0
51+
func.call @check_fptosi(%cst0) : (f64) -> ()
52+
// CHECK-NEXT: 0
53+
func.call @check_fptosi(%cst_nzero) : (f64) -> ()
54+
// CHECK-NEXT: 1
55+
func.call @check_fptosi(%cst1) : (f64) -> ()
56+
// CHECK-NEXT: -1
57+
func.call @check_fptosi(%cst_n1) : (f64) -> ()
58+
// CHECK-NEXT: -1
59+
func.call @check_fptosi(%cst_n1_5) : (f64) -> ()
60+
// CHECK-NEXT: 1048576
61+
func.call @check_fptosi(%cstpow20) : (f64) -> ()
62+
// CHECK-NEXT: -1048576
63+
func.call @check_fptosi(%cstnpow20) : (f64) -> ()
64+
// CHECK-NEXT: 4294967295
65+
func.call @check_fptosi(%cst_i32_max) : (f64) -> ()
66+
// CHECK-NEXT: -4294967296
67+
func.call @check_fptosi(%cst_i32_min) : (f64) -> ()
68+
// CHECK-NEXT: 4294967296
69+
func.call @check_fptosi(%cst_i32_overflow) : (f64) -> ()
70+
// CHECK-NEXT: -4294967297
71+
func.call @check_fptosi(%cst_i32_noverflow) : (f64) -> ()
72+
// CHECK-NEXT: 1099511627776
73+
func.call @check_fptosi(%cstpow40) : (f64) -> ()
74+
// CHECK-NEXT: -1099511627776
75+
func.call @check_fptosi(%cstnpow40) : (f64) -> ()
76+
// CHECK-NEXT: 1099512676352
77+
func.call @check_fptosi(%cst_pow40ppow20) : (f64) -> ()
78+
// CHECK-NEXT: -1099512676352
79+
func.call @check_fptosi(%cst_npow40ppow20) : (f64) -> ()
80+
81+
return
82+
}

0 commit comments

Comments
 (0)