Skip to content

Commit 8d406d8

Browse files
authored
[flang] IEEE_REAL (#113948)
IEEE_REAL converts an integer or real argument to a real of a given kind.
1 parent 7f498a8 commit 8d406d8

File tree

3 files changed

+451
-2
lines changed

3 files changed

+451
-2
lines changed

flang/include/flang/Optimizer/Builder/IntrinsicCall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ struct IntrinsicLibrary {
289289
template <mlir::arith::CmpFPredicate pred>
290290
mlir::Value genIeeeQuietCompare(mlir::Type resultType,
291291
llvm::ArrayRef<mlir::Value>);
292+
mlir::Value genIeeeReal(mlir::Type, llvm::ArrayRef<mlir::Value>);
292293
mlir::Value genIeeeRint(mlir::Type, llvm::ArrayRef<mlir::Value>);
293294
template <bool isFlag>
294295
void genIeeeSetFlagOrHaltingMode(llvm::ArrayRef<fir::ExtendedValue>);

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 233 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ static bool isStaticallyPresent(const fir::ExtendedValue &exv) {
9797

9898
/// IEEE module procedure names not yet implemented for genModuleProcTODO.
9999
static constexpr char ieee_get_underflow_mode[] = "ieee_get_underflow_mode";
100-
static constexpr char ieee_real[] = "ieee_real";
101100
static constexpr char ieee_rem[] = "ieee_rem";
102101
static constexpr char ieee_set_underflow_mode[] = "ieee_set_underflow_mode";
103102

@@ -362,7 +361,7 @@ static constexpr IntrinsicHandler handlers[]{
362361
{"ieee_quiet_le", &I::genIeeeQuietCompare<mlir::arith::CmpFPredicate::OLE>},
363362
{"ieee_quiet_lt", &I::genIeeeQuietCompare<mlir::arith::CmpFPredicate::OLT>},
364363
{"ieee_quiet_ne", &I::genIeeeQuietCompare<mlir::arith::CmpFPredicate::UNE>},
365-
{"ieee_real", &I::genModuleProcTODO<ieee_real>},
364+
{"ieee_real", &I::genIeeeReal},
366365
{"ieee_rem", &I::genModuleProcTODO<ieee_rem>},
367366
{"ieee_rint", &I::genIeeeRint},
368367
{"ieee_round_eq", &I::genIeeeTypeCompare<mlir::arith::CmpIPredicate::eq>},
@@ -4799,6 +4798,238 @@ IntrinsicLibrary::genIeeeQuietCompare(mlir::Type resultType,
47994798
return builder.create<fir::ConvertOp>(loc, resultType, res);
48004799
}
48014800

4801+
// IEEE_REAL
4802+
mlir::Value IntrinsicLibrary::genIeeeReal(mlir::Type resultType,
4803+
llvm::ArrayRef<mlir::Value> args) {
4804+
// Convert integer or real argument A to a real of a specified kind.
4805+
// Round according to the current rounding mode.
4806+
// Signal IEEE_INVALID if A is an sNaN, and return a qNaN.
4807+
// Signal IEEE_UNDERFLOW for an inexact subnormal or zero result.
4808+
// Signal IEEE_OVERFLOW if A is finite and the result is infinite.
4809+
// Signal IEEE_INEXACT for an inexact result.
4810+
//
4811+
// if (type(a) == resultType) {
4812+
// // Conversion to the same type is a nop except for sNaN processing.
4813+
// result = a
4814+
// } else {
4815+
// result = r = real(a, kind(result))
4816+
// // Conversion to a larger type is exact.
4817+
// if (c_sizeof(a) >= c_sizeof(r)) {
4818+
// b = (a is integer) ? int(r, kind(a)) : real(r, kind(a))
4819+
// if (a == b || isNaN(a)) {
4820+
// // a is {-0, +0, -inf, +inf, NaN} or exact; result is r
4821+
// } else {
4822+
// // odd(r) is true if the low bit of significand(r) is 1
4823+
// // rounding mode ieee_other is an alias for mode ieee_nearest
4824+
// if (a < b) {
4825+
// if (mode == ieee_nearest && odd(r)) result = ieee_next_down(r)
4826+
// if (mode == ieee_other && odd(r)) result = ieee_next_down(r)
4827+
// if (mode == ieee_to_zero && a > 0) result = ieee_next_down(r)
4828+
// if (mode == ieee_away && a < 0) result = ieee_next_down(r)
4829+
// if (mode == ieee_down) result = ieee_next_down(r)
4830+
// } else { // a > b
4831+
// if (mode == ieee_nearest && odd(r)) result = ieee_next_up(r)
4832+
// if (mode == ieee_other && odd(r)) result = ieee_next_up(r)
4833+
// if (mode == ieee_to_zero && a < 0) result = ieee_next_up(r)
4834+
// if (mode == ieee_away && a > 0) result = ieee_next_up(r)
4835+
// if (mode == ieee_up) result = ieee_next_up(r)
4836+
// }
4837+
// }
4838+
// }
4839+
// }
4840+
4841+
assert(args.size() == 2);
4842+
mlir::Type i1Ty = builder.getI1Type();
4843+
mlir::Type f32Ty = mlir::FloatType::getF32(builder.getContext());
4844+
mlir::Value a = args[0];
4845+
mlir::Type aType = a.getType();
4846+
4847+
// If the argument is an sNaN, raise an invalid exception and return a qNaN.
4848+
// Otherwise return the argument.
4849+
auto processSnan = [&](mlir::Value x) {
4850+
fir::IfOp ifOp = builder.create<fir::IfOp>(loc, resultType,
4851+
genIsFPClass(i1Ty, x, snanTest),
4852+
/*withElseRegion=*/true);
4853+
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
4854+
genRaiseExcept(_FORTRAN_RUNTIME_IEEE_INVALID);
4855+
builder.create<fir::ResultOp>(loc, genQNan(resultType));
4856+
builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
4857+
builder.create<fir::ResultOp>(loc, x);
4858+
builder.setInsertionPointAfter(ifOp);
4859+
return ifOp.getResult(0);
4860+
};
4861+
4862+
// Conversion is a nop, except that A may be an sNaN.
4863+
if (resultType == aType)
4864+
return processSnan(a);
4865+
4866+
// Can't directly convert between kind=2 and kind=3.
4867+
mlir::Value r, r1;
4868+
if ((aType.isBF16() && resultType.isF16()) ||
4869+
(aType.isF16() && resultType.isBF16())) {
4870+
a = builder.createConvert(loc, f32Ty, a);
4871+
aType = f32Ty;
4872+
}
4873+
r = builder.create<fir::ConvertOp>(loc, resultType, a);
4874+
4875+
mlir::IntegerType aIntType = mlir::dyn_cast<mlir::IntegerType>(aType);
4876+
mlir::FloatType aFloatType = mlir::dyn_cast<mlir::FloatType>(aType);
4877+
mlir::FloatType resultFloatType = mlir::dyn_cast<mlir::FloatType>(resultType);
4878+
4879+
// Conversion from a smaller type to a larger type is exact.
4880+
if ((aIntType ? aIntType.getWidth() : aFloatType.getWidth()) <
4881+
resultFloatType.getWidth())
4882+
return aIntType ? r : processSnan(r);
4883+
4884+
// A possibly inexact conversion result may need to be rounded up or down.
4885+
mlir::Value b = builder.create<fir::ConvertOp>(loc, aType, r);
4886+
mlir::Value aEqB;
4887+
if (aIntType)
4888+
aEqB = builder.create<mlir::arith::CmpIOp>(
4889+
loc, mlir::arith::CmpIPredicate::eq, a, b);
4890+
else
4891+
aEqB = builder.create<mlir::arith::CmpFOp>(
4892+
loc, mlir::arith::CmpFPredicate::UEQ, a, b);
4893+
4894+
// [a == b] a is a NaN or r is exact (a may be -0, +0, -inf, +inf) -- return r
4895+
fir::IfOp ifOp1 = builder.create<fir::IfOp>(loc, resultType, aEqB,
4896+
/*withElseRegion=*/true);
4897+
builder.setInsertionPointToStart(&ifOp1.getThenRegion().front());
4898+
builder.create<fir::ResultOp>(loc, aIntType ? r : processSnan(r));
4899+
4900+
// Code common to (a < b) and (a > b) branches.
4901+
builder.setInsertionPointToStart(&ifOp1.getElseRegion().front());
4902+
mlir::func::FuncOp getRound = fir::factory::getLlvmGetRounding(builder);
4903+
mlir::Value mode = builder.create<fir::CallOp>(loc, getRound).getResult(0);
4904+
mlir::Value aIsNegative, aIsPositive;
4905+
if (aIntType) {
4906+
mlir::Value zero = builder.createIntegerConstant(loc, aIntType, 0);
4907+
aIsNegative = builder.create<mlir::arith::CmpIOp>(
4908+
loc, mlir::arith::CmpIPredicate::slt, a, zero);
4909+
aIsPositive = builder.create<mlir::arith::CmpIOp>(
4910+
loc, mlir::arith::CmpIPredicate::sgt, a, zero);
4911+
} else {
4912+
mlir::Value zero = builder.createRealZeroConstant(loc, aFloatType);
4913+
aIsNegative = builder.create<mlir::arith::CmpFOp>(
4914+
loc, mlir::arith::CmpFPredicate::OLT, a, zero);
4915+
aIsPositive = builder.create<mlir::arith::CmpFOp>(
4916+
loc, mlir::arith::CmpFPredicate::OGT, a, zero);
4917+
}
4918+
mlir::Type resultIntType = builder.getIntegerType(resultFloatType.getWidth());
4919+
mlir::Value resultCast =
4920+
builder.create<mlir::arith::BitcastOp>(loc, resultIntType, r);
4921+
mlir::Value one = builder.createIntegerConstant(loc, resultIntType, 1);
4922+
mlir::Value rIsOdd = builder.create<fir::ConvertOp>(
4923+
loc, i1Ty, builder.create<mlir::arith::AndIOp>(loc, resultCast, one));
4924+
// Check for a rounding mode match.
4925+
auto match = [&](int m) {
4926+
return builder.create<mlir::arith::CmpIOp>(
4927+
loc, mlir::arith::CmpIPredicate::eq, mode,
4928+
builder.createIntegerConstant(loc, mode.getType(), m));
4929+
};
4930+
mlir::Value roundToNearestBit = builder.create<mlir::arith::OrIOp>(
4931+
loc,
4932+
// IEEE_OTHER is an alias for IEEE_NEAREST.
4933+
match(_FORTRAN_RUNTIME_IEEE_NEAREST), match(_FORTRAN_RUNTIME_IEEE_OTHER));
4934+
mlir::Value roundToNearest =
4935+
builder.create<mlir::arith::AndIOp>(loc, roundToNearestBit, rIsOdd);
4936+
mlir::Value roundToZeroBit = match(_FORTRAN_RUNTIME_IEEE_TO_ZERO);
4937+
mlir::Value roundAwayBit = match(_FORTRAN_RUNTIME_IEEE_AWAY);
4938+
mlir::Value roundToZero, roundAway, mustAdjust;
4939+
fir::IfOp adjustIfOp;
4940+
mlir::Value aLtB;
4941+
if (aIntType)
4942+
aLtB = builder.create<mlir::arith::CmpIOp>(
4943+
loc, mlir::arith::CmpIPredicate::slt, a, b);
4944+
else
4945+
aLtB = builder.create<mlir::arith::CmpFOp>(
4946+
loc, mlir::arith::CmpFPredicate::OLT, a, b);
4947+
mlir::Value upResult =
4948+
builder.create<mlir::arith::AddIOp>(loc, resultCast, one);
4949+
mlir::Value downResult =
4950+
builder.create<mlir::arith::SubIOp>(loc, resultCast, one);
4951+
4952+
// (a < b): r is inexact -- return r or ieee_next_down(r)
4953+
fir::IfOp ifOp2 = builder.create<fir::IfOp>(loc, resultType, aLtB,
4954+
/*withElseRegion=*/true);
4955+
builder.setInsertionPointToStart(&ifOp2.getThenRegion().front());
4956+
roundToZero =
4957+
builder.create<mlir::arith::AndIOp>(loc, roundToZeroBit, aIsPositive);
4958+
roundAway =
4959+
builder.create<mlir::arith::AndIOp>(loc, roundAwayBit, aIsNegative);
4960+
mlir::Value roundDown = match(_FORTRAN_RUNTIME_IEEE_DOWN);
4961+
mustAdjust =
4962+
builder.create<mlir::arith::OrIOp>(loc, roundToNearest, roundToZero);
4963+
mustAdjust = builder.create<mlir::arith::OrIOp>(loc, mustAdjust, roundAway);
4964+
mustAdjust = builder.create<mlir::arith::OrIOp>(loc, mustAdjust, roundDown);
4965+
adjustIfOp = builder.create<fir::IfOp>(loc, resultType, mustAdjust,
4966+
/*withElseRegion=*/true);
4967+
builder.setInsertionPointToStart(&adjustIfOp.getThenRegion().front());
4968+
if (resultType.isF80())
4969+
r1 = fir::runtime::genNearest(builder, loc, r,
4970+
builder.createBool(loc, false));
4971+
else
4972+
r1 = builder.create<mlir::arith::BitcastOp>(
4973+
loc, resultType,
4974+
builder.create<mlir::arith::SelectOp>(loc, aIsNegative, upResult,
4975+
downResult));
4976+
builder.create<fir::ResultOp>(loc, r1);
4977+
builder.setInsertionPointToStart(&adjustIfOp.getElseRegion().front());
4978+
builder.create<fir::ResultOp>(loc, r);
4979+
builder.setInsertionPointAfter(adjustIfOp);
4980+
builder.create<fir::ResultOp>(loc, adjustIfOp.getResult(0));
4981+
4982+
// (a > b): r is inexact -- return r or ieee_next_up(r)
4983+
builder.setInsertionPointToStart(&ifOp2.getElseRegion().front());
4984+
roundToZero =
4985+
builder.create<mlir::arith::AndIOp>(loc, roundToZeroBit, aIsNegative);
4986+
roundAway =
4987+
builder.create<mlir::arith::AndIOp>(loc, roundAwayBit, aIsPositive);
4988+
mlir::Value roundUp = match(_FORTRAN_RUNTIME_IEEE_UP);
4989+
mustAdjust =
4990+
builder.create<mlir::arith::OrIOp>(loc, roundToNearest, roundToZero);
4991+
mustAdjust = builder.create<mlir::arith::OrIOp>(loc, mustAdjust, roundAway);
4992+
mustAdjust = builder.create<mlir::arith::OrIOp>(loc, mustAdjust, roundUp);
4993+
adjustIfOp = builder.create<fir::IfOp>(loc, resultType, mustAdjust,
4994+
/*withElseRegion=*/true);
4995+
builder.setInsertionPointToStart(&adjustIfOp.getThenRegion().front());
4996+
if (resultType.isF80())
4997+
r1 = fir::runtime::genNearest(builder, loc, r,
4998+
builder.createBool(loc, true));
4999+
else
5000+
r1 = builder.create<mlir::arith::BitcastOp>(
5001+
loc, resultType,
5002+
builder.create<mlir::arith::SelectOp>(loc, aIsPositive, upResult,
5003+
downResult));
5004+
builder.create<fir::ResultOp>(loc, r1);
5005+
builder.setInsertionPointToStart(&adjustIfOp.getElseRegion().front());
5006+
builder.create<fir::ResultOp>(loc, r);
5007+
builder.setInsertionPointAfter(adjustIfOp);
5008+
builder.create<fir::ResultOp>(loc, adjustIfOp.getResult(0));
5009+
5010+
// Generate exceptions for (a < b) and (a > b) branches.
5011+
builder.setInsertionPointAfter(ifOp2);
5012+
r = ifOp2.getResult(0);
5013+
fir::IfOp exceptIfOp1 = builder.create<fir::IfOp>(
5014+
loc, genIsFPClass(i1Ty, r, infiniteTest), /*withElseRegion=*/true);
5015+
builder.setInsertionPointToStart(&exceptIfOp1.getThenRegion().front());
5016+
genRaiseExcept(_FORTRAN_RUNTIME_IEEE_OVERFLOW |
5017+
_FORTRAN_RUNTIME_IEEE_INEXACT);
5018+
builder.setInsertionPointToStart(&exceptIfOp1.getElseRegion().front());
5019+
fir::IfOp exceptIfOp2 = builder.create<fir::IfOp>(
5020+
loc, genIsFPClass(i1Ty, r, subnormalTest | zeroTest),
5021+
/*withElseRegion=*/true);
5022+
builder.setInsertionPointToStart(&exceptIfOp2.getThenRegion().front());
5023+
genRaiseExcept(_FORTRAN_RUNTIME_IEEE_UNDERFLOW |
5024+
_FORTRAN_RUNTIME_IEEE_INEXACT);
5025+
builder.setInsertionPointToStart(&exceptIfOp2.getElseRegion().front());
5026+
genRaiseExcept(_FORTRAN_RUNTIME_IEEE_INEXACT);
5027+
builder.setInsertionPointAfter(exceptIfOp1);
5028+
builder.create<fir::ResultOp>(loc, ifOp2.getResult(0));
5029+
builder.setInsertionPointAfter(ifOp1);
5030+
return ifOp1.getResult(0);
5031+
}
5032+
48025033
// IEEE_RINT
48035034
mlir::Value IntrinsicLibrary::genIeeeRint(mlir::Type resultType,
48045035
llvm::ArrayRef<mlir::Value> args) {

0 commit comments

Comments
 (0)