Skip to content

Commit 54784b1

Browse files
committed
[flang] Fold OUT_OF_RANGE()
Fold the F'2018 intrinsic function OUT_OF_RANGE(), which returns .TRUE. when a conversion of an integer or real value to an integer or real type would yield an overflow or (for real->integer only) invalid operand exception. Test all type combinations, with both rounding possibilities for the real->integer cases. Differential Revision: https://reviews.llvm.org/D159038
1 parent 6a6f3c9 commit 54784b1

File tree

3 files changed

+432
-3
lines changed

3 files changed

+432
-3
lines changed

flang/lib/Evaluate/fold-logical.cpp

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,115 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
215215
if (auto *expr{UnwrapExpr<Expr<SomeLogical>>(args[0])}) {
216216
return Fold(context, ConvertToType<T>(std::move(*expr)));
217217
}
218+
} else if (name == "out_of_range") {
219+
if (Expr<SomeType> * cx{UnwrapExpr<Expr<SomeType>>(args[0])}) {
220+
auto restorer{context.messages().DiscardMessages()};
221+
*args[0] = Fold(context, std::move(*cx));
222+
if (Expr<SomeType> & folded{DEREF(args[0].value().UnwrapExpr())};
223+
IsActuallyConstant(folded)) {
224+
std::optional<std::vector<typename T::Scalar>> result;
225+
if (Expr<SomeReal> * realMold{UnwrapExpr<Expr<SomeReal>>(args[1])}) {
226+
if (const auto *xInt{UnwrapExpr<Expr<SomeInteger>>(folded)}) {
227+
result.emplace();
228+
std::visit(
229+
[&](const auto &mold, const auto &x) {
230+
using RealType =
231+
typename std::decay_t<decltype(mold)>::Result;
232+
static_assert(RealType::category == TypeCategory::Real);
233+
using Scalar = typename RealType::Scalar;
234+
using xType = typename std::decay_t<decltype(x)>::Result;
235+
const auto &xConst{DEREF(UnwrapExpr<Constant<xType>>(x))};
236+
for (const auto &elt : xConst.values()) {
237+
result->emplace_back(
238+
Scalar::template FromInteger(elt).flags.test(
239+
RealFlag::Overflow));
240+
}
241+
},
242+
realMold->u, xInt->u);
243+
} else if (const auto *xReal{UnwrapExpr<Expr<SomeReal>>(folded)}) {
244+
result.emplace();
245+
std::visit(
246+
[&](const auto &mold, const auto &x) {
247+
using RealType =
248+
typename std::decay_t<decltype(mold)>::Result;
249+
static_assert(RealType::category == TypeCategory::Real);
250+
using Scalar = typename RealType::Scalar;
251+
using xType = typename std::decay_t<decltype(x)>::Result;
252+
const auto &xConst{DEREF(UnwrapExpr<Constant<xType>>(x))};
253+
for (const auto &elt : xConst.values()) {
254+
result->emplace_back(elt.IsFinite() &&
255+
Scalar::template Convert(elt).flags.test(
256+
RealFlag::Overflow));
257+
}
258+
},
259+
realMold->u, xReal->u);
260+
}
261+
} else if (Expr<SomeInteger> *
262+
intMold{UnwrapExpr<Expr<SomeInteger>>(args[1])}) {
263+
if (const auto *xInt{UnwrapExpr<Expr<SomeInteger>>(folded)}) {
264+
result.emplace();
265+
std::visit(
266+
[&](const auto &mold, const auto &x) {
267+
using IntType = typename std::decay_t<decltype(mold)>::Result;
268+
static_assert(IntType::category == TypeCategory::Integer);
269+
using Scalar = typename IntType::Scalar;
270+
using xType = typename std::decay_t<decltype(x)>::Result;
271+
const auto &xConst{DEREF(UnwrapExpr<Constant<xType>>(x))};
272+
for (const auto &elt : xConst.values()) {
273+
result->emplace_back(
274+
Scalar::template ConvertSigned(elt).overflow);
275+
}
276+
},
277+
intMold->u, xInt->u);
278+
} else if (Expr<SomeLogical> *
279+
cRound{args.size() >= 3
280+
? UnwrapExpr<Expr<SomeLogical>>(args[2])
281+
: nullptr};
282+
!cRound || IsActuallyConstant(*args[2]->UnwrapExpr())) {
283+
if (const auto *xReal{UnwrapExpr<Expr<SomeReal>>(folded)}) {
284+
common::RoundingMode roundingMode{common::RoundingMode::ToZero};
285+
if (cRound &&
286+
common::visit(
287+
[](const auto &x) {
288+
using xType =
289+
typename std::decay_t<decltype(x)>::Result;
290+
return GetScalarConstantValue<xType>(x)
291+
.value()
292+
.IsTrue();
293+
},
294+
cRound->u)) {
295+
// ROUND=.TRUE. - convert with NINT()
296+
roundingMode = common::RoundingMode::TiesAwayFromZero;
297+
}
298+
result.emplace();
299+
std::visit(
300+
[&](const auto &mold, const auto &x) {
301+
using IntType =
302+
typename std::decay_t<decltype(mold)>::Result;
303+
static_assert(IntType::category == TypeCategory::Integer);
304+
using Scalar = typename IntType::Scalar;
305+
using xType = typename std::decay_t<decltype(x)>::Result;
306+
const auto &xConst{DEREF(UnwrapExpr<Constant<xType>>(x))};
307+
for (const auto &elt : xConst.values()) {
308+
// Note that OUT_OF_RANGE(Inf/NaN) is .TRUE. for the
309+
// real->integer case, but not for real->real.
310+
result->emplace_back(!elt.IsFinite() ||
311+
elt.template ToInteger<Scalar>(roundingMode)
312+
.flags.test(RealFlag::Overflow));
313+
}
314+
},
315+
intMold->u, xReal->u);
316+
}
317+
}
318+
}
319+
if (result) {
320+
if (auto extents{GetConstantExtents(context, folded)}) {
321+
return Expr<T>{
322+
Constant<T>{std::move(*result), std::move(*extents)}};
323+
}
324+
}
325+
}
326+
}
218327
} else if (name == "parity") {
219328
return FoldAllAnyParity(
220329
context, std::move(funcRef), &Scalar<T>::NEQV, Scalar<T>{false});
@@ -242,9 +351,7 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
242351
name == "__builtin_ieee_support_underflow_control") {
243352
return Expr<T>{true};
244353
}
245-
// TODO: is_iostat_end,
246-
// is_iostat_eor, logical, matmul, out_of_range,
247-
// parity
354+
// TODO: is_iostat_end, is_iostat_eor, logical, matmul, parity
248355
return Expr<T>{std::move(funcRef)};
249356
}
250357

File renamed without changes.

0 commit comments

Comments
 (0)