-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[flang] Complete implementation of OUT_OF_RANGE() #89334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-flang-semantics @llvm/pr-subscribers-flang-runtime Author: Peter Klausler (klausler) ChangesThe intrinsic function OUT_OF_RANGE() lacks support in lowering and the runtime. This patch obviates a need for any such support by implementing OUT_OF_RANGE() via rewriting in semantics. This rewriting of OUT_OF_RANGE() calls replaces the existing code that folds OUT_OF_RANGE() calls with constant arguments. Some changes and fixes were necessary outside of OUT_OF_RANGE()'s folding code (now rewriting code), whose testing exposed some other issues worth fixing.
Patch is 53.89 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/89334.diff 12 Files Affected:
diff --git a/flang/include/flang/Common/real.h b/flang/include/flang/Common/real.h
index 49c400b368a2c1..623cb294db3011 100644
--- a/flang/include/flang/Common/real.h
+++ b/flang/include/flang/Common/real.h
@@ -108,7 +108,28 @@ static constexpr int PrecisionOfRealKind(int kind) {
}
}
-template <int BINARY_PRECISION> class RealDetails {
+// RealCharacteristics is constexpr, but also useful when constructed
+// with a non-constant precision argument.
+class RealCharacteristics {
+public:
+ explicit constexpr RealCharacteristics(int p) : binaryPrecision{p} {}
+
+ RT_OFFLOAD_VAR_GROUP_BEGIN
+ int binaryPrecision;
+ int bits{BitsForBinaryPrecision(binaryPrecision)};
+ bool isImplicitMSB{binaryPrecision != 64 /*x87*/};
+ int significandBits{binaryPrecision - isImplicitMSB};
+ int exponentBits{bits - significandBits - 1 /*sign*/};
+ int maxExponent{(1 << exponentBits) - 1};
+ int exponentBias{maxExponent / 2};
+ int decimalPrecision{LogBaseTwoToLogBaseTen(binaryPrecision - 1)};
+ int decimalRange{LogBaseTwoToLogBaseTen(exponentBias - 1)};
+ // Number of significant decimal digits in the fraction of the
+ // exact conversion of the least nonzero subnormal.
+ int maxDecimalConversionDigits{MaxDecimalConversionDigits(binaryPrecision)};
+ int maxHexadecimalConversionDigits{
+ MaxHexadecimalConversionDigits(binaryPrecision)};
+ RT_OFFLOAD_VAR_GROUP_END
private:
// Converts bit widths to whole decimal digits
static constexpr int LogBaseTwoToLogBaseTen(int logb2) {
@@ -118,33 +139,6 @@ template <int BINARY_PRECISION> class RealDetails {
(logb2 * LogBaseTenOfTwoTimesTenToThe12th) / TenToThe12th};
return static_cast<int>(logb10);
}
-
-public:
- RT_OFFLOAD_VAR_GROUP_BEGIN
- static constexpr int binaryPrecision{BINARY_PRECISION};
- static constexpr int bits{BitsForBinaryPrecision(binaryPrecision)};
- static constexpr bool isImplicitMSB{binaryPrecision != 64 /*x87*/};
- static constexpr int significandBits{binaryPrecision - isImplicitMSB};
- static constexpr int exponentBits{bits - significandBits - 1 /*sign*/};
- static constexpr int maxExponent{(1 << exponentBits) - 1};
- static constexpr int exponentBias{maxExponent / 2};
-
- static constexpr int decimalPrecision{
- LogBaseTwoToLogBaseTen(binaryPrecision - 1)};
- static constexpr int decimalRange{LogBaseTwoToLogBaseTen(exponentBias - 1)};
-
- // Number of significant decimal digits in the fraction of the
- // exact conversion of the least nonzero subnormal.
- static constexpr int maxDecimalConversionDigits{
- MaxDecimalConversionDigits(binaryPrecision)};
-
- static constexpr int maxHexadecimalConversionDigits{
- MaxHexadecimalConversionDigits(binaryPrecision)};
- RT_OFFLOAD_VAR_GROUP_END
-
- static_assert(binaryPrecision > 0);
- static_assert(exponentBits > 1);
- static_assert(exponentBits <= 15);
};
} // namespace Fortran::common
diff --git a/flang/include/flang/Decimal/binary-floating-point.h b/flang/include/flang/Decimal/binary-floating-point.h
index 4919c1f9d240f4..1e0cde97d98e61 100644
--- a/flang/include/flang/Decimal/binary-floating-point.h
+++ b/flang/include/flang/Decimal/binary-floating-point.h
@@ -30,21 +30,20 @@ enum FortranRounding {
RoundCompatible, /* RC: like RN, but ties go away from 0 */
};
-template <int BINARY_PRECISION>
-class BinaryFloatingPointNumber : public common::RealDetails<BINARY_PRECISION> {
+template <int BINARY_PRECISION> class BinaryFloatingPointNumber {
public:
- using Details = common::RealDetails<BINARY_PRECISION>;
- using Details::binaryPrecision;
- using Details::bits;
- using Details::decimalPrecision;
- using Details::decimalRange;
- using Details::exponentBias;
- using Details::exponentBits;
- using Details::isImplicitMSB;
- using Details::maxDecimalConversionDigits;
- using Details::maxExponent;
- using Details::maxHexadecimalConversionDigits;
- using Details::significandBits;
+ static constexpr common::RealCharacteristics realChars{BINARY_PRECISION};
+ static constexpr int binaryPrecision{BINARY_PRECISION};
+ static constexpr int bits{realChars.bits};
+ static constexpr int isImplicitMSB{realChars.isImplicitMSB};
+ static constexpr int significandBits{realChars.significandBits};
+ static constexpr int exponentBits{realChars.exponentBits};
+ static constexpr int exponentBias{realChars.exponentBias};
+ static constexpr int maxExponent{realChars.maxExponent};
+ static constexpr int decimalPrecision{realChars.decimalPrecision};
+ static constexpr int decimalRange{realChars.decimalRange};
+ static constexpr int maxDecimalConversionDigits{
+ realChars.maxDecimalConversionDigits};
using RawType = common::HostUnsignedIntType<bits>;
static_assert(CHAR_BIT * sizeof(RawType) >= bits);
diff --git a/flang/include/flang/Evaluate/initial-image.h b/flang/include/flang/Evaluate/initial-image.h
index dc9a9bfbfdf220..d9efad6f1c3be0 100644
--- a/flang/include/flang/Evaluate/initial-image.h
+++ b/flang/include/flang/Evaluate/initial-image.h
@@ -46,7 +46,8 @@ class InitialImage {
if (offset < 0 || offset + bytes > data_.size()) {
return OutOfRange;
} else {
- auto elementBytes{ToInt64(x.GetType().MeasureSizeInBytes(context, true))};
+ auto elementBytes{
+ ToInt64(x.GetType().MeasureSizeInBytes(context, /*aligned=*/false))};
if (!elementBytes ||
bytes !=
x.values().size() * static_cast<std::size_t>(*elementBytes)) {
@@ -115,7 +116,8 @@ class InitialImage {
std::optional<Expr<SomeType>> AsConstant(FoldingContext &,
const DynamicType &, std::optional<std::int64_t> charLength,
const ConstantSubscripts &, bool padWithZero = false,
- ConstantSubscript offset = 0) const;
+ ConstantSubscript offset = 0,
+ std::optional<std::size_t> elementBytes = std::nullopt) const;
std::optional<Expr<SomeType>> AsConstantPointer(
ConstantSubscript offset = 0) const;
diff --git a/flang/include/flang/Evaluate/integer.h b/flang/include/flang/Evaluate/integer.h
index 7395645701265d..aea0243f2734c4 100644
--- a/flang/include/flang/Evaluate/integer.h
+++ b/flang/include/flang/Evaluate/integer.h
@@ -50,7 +50,10 @@ namespace Fortran::evaluate::value {
// named accordingly in ALL CAPS so that they can be referenced easily in
// the language standard.
template <int BITS, bool IS_LITTLE_ENDIAN = isHostLittleEndian,
- int PARTBITS = BITS <= 32 ? BITS : 32,
+ int PARTBITS = BITS <= 32 ? BITS
+ : BITS % 32 == 0 ? 32
+ : BITS % 16 == 0 ? 16
+ : 8,
typename PART = HostUnsignedInt<PARTBITS>,
typename BIGPART = HostUnsignedInt<PARTBITS * 2>>
class Integer {
diff --git a/flang/include/flang/Evaluate/real.h b/flang/include/flang/Evaluate/real.h
index b7af0ff6b431c8..1987484e83b916 100644
--- a/flang/include/flang/Evaluate/real.h
+++ b/flang/include/flang/Evaluate/real.h
@@ -35,20 +35,19 @@ static constexpr std::int64_t ScaledLogBaseTenOfTwo{301029995664};
// class template must be (or look like) an instance of Integer<>;
// the second specifies the number of effective bits (binary precision)
// in the fraction.
-template <typename WORD, int PREC>
-class Real : public common::RealDetails<PREC> {
+template <typename WORD, int PREC> class Real {
public:
using Word = WORD;
static constexpr int binaryPrecision{PREC};
- using Details = common::RealDetails<PREC>;
- using Details::exponentBias;
- using Details::exponentBits;
- using Details::isImplicitMSB;
- using Details::maxExponent;
- using Details::significandBits;
+ static constexpr common::RealCharacteristics realChars{PREC};
+ static constexpr int exponentBias{realChars.exponentBias};
+ static constexpr int exponentBits{realChars.exponentBits};
+ static constexpr int isImplicitMSB{realChars.isImplicitMSB};
+ static constexpr int maxExponent{realChars.maxExponent};
+ static constexpr int significandBits{realChars.significandBits};
static constexpr int bits{Word::bits};
- static_assert(bits >= Details::bits);
+ static_assert(bits >= realChars.bits);
using Fraction = Integer<binaryPrecision>; // all bits made explicit
template <typename W, int P> friend class Real;
@@ -205,8 +204,8 @@ class Real : public common::RealDetails<PREC> {
}
static constexpr int DIGITS{binaryPrecision};
- static constexpr int PRECISION{Details::decimalPrecision};
- static constexpr int RANGE{Details::decimalRange};
+ static constexpr int PRECISION{realChars.decimalPrecision};
+ static constexpr int RANGE{realChars.decimalRange};
static constexpr int MAXEXPONENT{maxExponent - exponentBias};
static constexpr int MINEXPONENT{2 - exponentBias};
Real RRSPACING() const;
@@ -371,6 +370,10 @@ class Real : public common::RealDetails<PREC> {
return result;
}
bool isNegative{x.IsNegative()};
+ if (x.IsInfinite()) {
+ result.value = Infinity(isNegative);
+ return result;
+ }
A absX{x};
if (isNegative) {
absX = x.Negate();
diff --git a/flang/lib/Decimal/big-radix-floating-point.h b/flang/lib/Decimal/big-radix-floating-point.h
index 6ce8ae7925c150..f9afebf5b3d703 100644
--- a/flang/lib/Decimal/big-radix-floating-point.h
+++ b/flang/lib/Decimal/big-radix-floating-point.h
@@ -83,6 +83,8 @@ template <int PREC, int LOG10RADIX = 16> class BigRadixFloatingPointNumber {
return *this;
}
+ RT_API_ATTRS bool IsInteger() const { return exponent_ >= 0; }
+
// Converts decimal floating-point to binary.
RT_API_ATTRS ConversionToBinaryResult<PREC> ConvertToBinary();
diff --git a/flang/lib/Evaluate/fold-logical.cpp b/flang/lib/Evaluate/fold-logical.cpp
index 5a9596f3c274b5..4c1afe9a0f2952 100644
--- a/flang/lib/Evaluate/fold-logical.cpp
+++ b/flang/lib/Evaluate/fold-logical.cpp
@@ -41,6 +41,586 @@ static Expr<T> FoldAllAnyParity(FoldingContext &context, FunctionRef<T> &&ref,
return Expr<T>{std::move(ref)};
}
+// OUT_OF_RANGE(x,mold[,round]) references are entirely rewritten here into
+// expressions, which are then folded into constants when 'x' and 'round'
+// are constant. It is guaranteed that 'x' is evaluated at most once.
+
+template <int X_RKIND, int MOLD_IKIND>
+Expr<SomeReal> RealToIntBoundHelper(bool round, bool negate) {
+ using RType = Type<TypeCategory::Real, X_RKIND>;
+ using RealType = Scalar<RType>;
+ using IntType = Scalar<Type<TypeCategory::Integer, MOLD_IKIND>>;
+ RealType result{}; // 0.
+ common::RoundingMode roundingMode{round
+ ? common::RoundingMode::TiesAwayFromZero
+ : common::RoundingMode::ToZero};
+ // Add decreasing powers of two to the result to find the largest magnitude
+ // value that can be converted to the integer type without overflow.
+ RealType at{RealType::FromInteger(IntType{negate ? -1 : 1}).value};
+ bool decrement{true};
+ while (!at.template ToInteger<IntType>(roundingMode)
+ .flags.test(RealFlag::Overflow)) {
+ auto tmp{at.SCALE(IntType{1})};
+ if (tmp.flags.test(RealFlag::Overflow)) {
+ decrement = false;
+ break;
+ }
+ at = tmp.value;
+ }
+ while (true) {
+ if (decrement) {
+ at = at.SCALE(IntType{-1}).value;
+ } else {
+ decrement = true;
+ }
+ auto tmp{at.Add(result)};
+ if (tmp.flags.test(RealFlag::Inexact)) {
+ break;
+ } else if (!tmp.value.template ToInteger<IntType>(roundingMode)
+ .flags.test(RealFlag::Overflow)) {
+ result = tmp.value;
+ }
+ }
+ return AsCategoryExpr(Constant<RType>{std::move(result)});
+}
+
+static Expr<SomeReal> RealToIntBound(
+ int xRKind, int moldIKind, bool round, bool negate) {
+ switch (xRKind) {
+#define ICASES(RK) \
+ switch (moldIKind) { \
+ case 1: \
+ return RealToIntBoundHelper<RK, 1>(round, negate); \
+ break; \
+ case 2: \
+ return RealToIntBoundHelper<RK, 2>(round, negate); \
+ break; \
+ case 4: \
+ return RealToIntBoundHelper<RK, 4>(round, negate); \
+ break; \
+ case 8: \
+ return RealToIntBoundHelper<RK, 8>(round, negate); \
+ break; \
+ case 16: \
+ return RealToIntBoundHelper<RK, 16>(round, negate); \
+ break; \
+ } \
+ break
+ case 2:
+ ICASES(2);
+ break;
+ case 3:
+ ICASES(3);
+ break;
+ case 4:
+ ICASES(4);
+ break;
+ case 8:
+ ICASES(8);
+ break;
+ case 10:
+ ICASES(10);
+ break;
+ case 16:
+ ICASES(16);
+ break;
+ }
+ DIE("RealToIntBound: no case");
+#undef ICASES
+}
+
+class RealToIntLimitHelper {
+public:
+ using Result = std::optional<Expr<SomeReal>>;
+ using Types = RealTypes;
+ RealToIntLimitHelper(
+ FoldingContext &context, Expr<SomeReal> &&hi, Expr<SomeReal> &lo)
+ : context_{context}, hi_{std::move(hi)}, lo_{lo} {}
+ template <typename T> Result Test() {
+ if (UnwrapExpr<Expr<T>>(hi_)) {
+ bool promote{T::kind < 16};
+ Result constResult;
+ if (auto hiV{GetScalarConstantValue<T>(hi_)}) {
+ auto loV{GetScalarConstantValue<T>(lo_)};
+ CHECK(loV.has_value());
+ auto diff{hiV->Subtract(*loV, Rounding{common::RoundingMode::ToZero})};
+ promote = promote &&
+ (diff.flags.test(RealFlag::Overflow) ||
+ diff.flags.test(RealFlag::Inexact));
+ constResult = AsCategoryExpr(Constant<T>{std::move(diff.value)});
+ }
+ if (promote) {
+ constexpr int nextKind{T::kind < 4 ? 4 : T::kind == 4 ? 8 : 16};
+ using T2 = Type<TypeCategory::Real, nextKind>;
+ hi_ = Expr<SomeReal>{Fold(context_, ConvertToType<T2>(std::move(hi_)))};
+ lo_ = Expr<SomeReal>{Fold(context_, ConvertToType<T2>(std::move(lo_)))};
+ if (constResult) {
+ // Use promoted constants on next iteration of SearchTypes
+ return std::nullopt;
+ }
+ }
+ if (constResult) {
+ return constResult;
+ } else {
+ return AsCategoryExpr(std::move(hi_) - Expr<SomeReal>{lo_});
+ }
+ } else {
+ return std::nullopt;
+ }
+ }
+
+private:
+ FoldingContext &context_;
+ Expr<SomeReal> hi_;
+ Expr<SomeReal> &lo_;
+};
+
+static std::optional<Expr<SomeReal>> RealToIntLimit(
+ FoldingContext &context, Expr<SomeReal> &&hi, Expr<SomeReal> &lo) {
+ return common::SearchTypes(RealToIntLimitHelper{context, std::move(hi), lo});
+}
+
+// RealToRealBounds() returns a pair (HUGE(x),REAL(HUGE(mold),KIND(x)))
+// when REAL(HUGE(x),KIND(mold)) overflows, and std::nullopt otherwise.
+template <int X_RKIND, int MOLD_RKIND>
+std::optional<std::pair<Expr<SomeReal>, Expr<SomeReal>>>
+RealToRealBoundsHelper() {
+ using RType = Type<TypeCategory::Real, X_RKIND>;
+ using RealType = Scalar<RType>;
+ using MoldRealType = Scalar<Type<TypeCategory::Real, MOLD_RKIND>>;
+ if (!MoldRealType::Convert(RealType::HUGE()).flags.test(RealFlag::Overflow)) {
+ return std::nullopt;
+ } else {
+ return std::make_pair(AsCategoryExpr(Constant<RType>{
+ RealType::Convert(MoldRealType::HUGE()).value}),
+ AsCategoryExpr(Constant<RType>{RealType::HUGE()}));
+ }
+}
+
+static std::optional<std::pair<Expr<SomeReal>, Expr<SomeReal>>>
+RealToRealBounds(int xRKind, int moldRKind) {
+ switch (xRKind) {
+#define RCASES(RK) \
+ switch (moldRKind) { \
+ case 2: \
+ return RealToRealBoundsHelper<RK, 2>(); \
+ break; \
+ case 3: \
+ return RealToRealBoundsHelper<RK, 3>(); \
+ break; \
+ case 4: \
+ return RealToRealBoundsHelper<RK, 4>(); \
+ break; \
+ case 8: \
+ return RealToRealBoundsHelper<RK, 8>(); \
+ break; \
+ case 10: \
+ return RealToRealBoundsHelper<RK, 10>(); \
+ break; \
+ case 16: \
+ return RealToRealBoundsHelper<RK, 16>(); \
+ break; \
+ } \
+ break
+ case 2:
+ RCASES(2);
+ break;
+ case 3:
+ RCASES(3);
+ break;
+ case 4:
+ RCASES(4);
+ break;
+ case 8:
+ RCASES(8);
+ break;
+ case 10:
+ RCASES(10);
+ break;
+ case 16:
+ RCASES(16);
+ break;
+ }
+ DIE("RealToRealBounds: no case");
+#undef RCASES
+}
+
+template <int X_IKIND, int MOLD_RKIND>
+std::optional<Expr<SomeInteger>> IntToRealBoundHelper(bool negate) {
+ using IType = Type<TypeCategory::Integer, X_IKIND>;
+ using IntType = Scalar<IType>;
+ using RealType = Scalar<Type<TypeCategory::Real, MOLD_RKIND>>;
+ IntType result{}; // 0
+ while (true) {
+ std::optional<IntType> next;
+ for (int bit{0}; bit < IntType::bits; ++bit) {
+ IntType power{IntType{}.IBSET(bit)};
+ if (power.IsNegative()) {
+ if (!negate) {
+ break;
+ }
+ } else if (negate) {
+ power = power.Negate().value;
+ }
+ auto tmp{power.AddSigned(result)};
+ if (tmp.overflow ||
+ RealType::FromInteger(tmp.value).flags.test(RealFlag::Overflow)) {
+ break;
+ }
+ next = tmp.value;
+ }
+ if (next) {
+ CHECK(result.CompareSigned(*next) != Ordering::Equal);
+ result = *next;
+ } else {
+ break;
+ }
+ }
+ if (result.CompareSigned(IntType::HUGE()) == Ordering::Equal) {
+ return std::nullopt;
+ } else {
+ return AsCategoryExpr(Constant<IType>{std::move(result)});
+ }
+}
+
+static std::optional<Expr<SomeInteger>> IntToRealBound(
+ int xIKind, int moldRKind, bool negate) {
+ switch (xIKind) {
+#define RCASES(IK) \
+ switch (moldRKind) { \
+ case 2: \
+ return IntToRealBoundHelper<IK, 2>(negate); \
+ break; \
+ case 3: \
+ return IntToRealBoundHelper<IK, 3>(negate); \
+ break; \
+ case 4: \
+ return IntToRealBoundHelper<IK, 4>(negate); \
+ break; \
+ case 8: \
+ return IntToRealBoundHelper<IK, 8>(negate); \
+ break; \
+ case 10: \
+ return IntToRealBoundHelper<IK, 10>(negate); \
+ break; \
+ case 16: \
+ return IntToRealBoundHelper<IK, 16>(negate); \
+ break; \
+ } \
+ break
+ case 1:
+ RCASES(1);
+ break;
+ case 2:
+ RCASES(2);
+ break;
+ case 4:
+ RCASES(4);
+ break;
+ case 8:
+ RCASES(8);
+ break;
+ case 16:
+ RCASES(16);
+ break;
+ }
+ DIE("IntToRealBound: no case");
+#undef RCASES
+}
+
+template <int X_IKIND, int MOLD_IKIND>
+std::optional<Expr<SomeInteger>> IntToIntBoundHelper() {
+ if constexpr (X_IKIND <= MOLD_IKIND) {
+ return std::nullopt;
+ } else {
+ using XIType = Type<TypeCategory::Integer, X_IKIND>;
+ using IntegerType = Scalar<XIType>;
+ using MoldIType = Type<TypeCategory::Integer, MOLD_IKIND>;
+ using MoldIntegerType = Scalar<MoldIType>;
+ return AsCategoryExpr(Constant<XIType>{
+ IntegerType::ConvertSigned(MoldIntegerType::HUGE()).value});
+ }
+}
+
+static std::optional<Expr<SomeInteger>> IntToIntBound(
+ int xIKind, int moldIKind) {
+ switch (xIKind) {
+#define ICASES(IK) \
+ switch (moldIKind) { \
+ case 1: \
+ return IntToIntBoundHelper<IK, 1>(); \
+ break; \
+ case 2: \
+ return IntToIntBoundHelper<IK, 2>(); \
+ break; \
+ case 4: \
+ return IntToIntBoundHelper<IK, 4>(); \
+ break; \
+ case 8: \
+ return IntToIntBoundHelper<IK, 8>(); \
+ break; \
+ case 16: \
+ return IntToIntBoundHelper<IK, 16>(); \
+ break; \
+ } \
+ break
+ case 1:
+ ICASES(1);
+ break;
+ case 2:
+ ICASES(2);
+ break;
+ case 4:
+ ICASES(4);
+ break;
+ case 8:
+ ICASES(8);
+ break;
+ case 16:
+ ICASES(16);
+ break;
+ }
+ DIE("IntToIntBound: no case");
+#undef ICASES
+}
+
+// ApplyIntrinsic() constructs the typed expression representation
+// for a specific intrinsic function reference.
+// TODO: maybe move into tools.h?
+class IntrinsicCallHelper {
+public:
+ explicit IntrinsicCallHelper(SpecificCall &&call) : call_{call} {
+ CHECK(proc_.IsFunction());
+ typeAndShape_ = proc_.functionResult->GetTypeAndShape();
+ CHECK(typeAndShape_ != nullptr);
+ }
+ using Result = std::optional<Expr<SomeType>>;
+ using Types = LengthlessIntrinsicTypes;
+ template <typename T> Result Test() {
+ if (T::category == typeAndShape_->type().category() &&
+ T::kind == typeAndShape_->type().kind()) {
+ return AsGenericExpr(FunctionRef<T>{
+ ProcedureDesignator{std::move(call_.specificIntrinsic)},
+ std::move(call_.arguments)});
+ } else {
+ return std::nullopt;
+ }
+ }
+
+private:
+ SpecificCall call_;
+ const characteristics::Procedure &proc_{
+ call_.specificIntrinsic.characteristics.value()};
+ const characteristics::TypeAndShape *typeAndShape_{nullptr};
+};
+
+static Expr<SomeType> ApplyIntrinsic(
+ FoldingContext &context, const std::s...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely a non trivial intrinsic. I can't validate the rewrite formulas, but it looks like the code is doing what the comments say and it has good testing via folding.
Maybe adding a couple rewrite tests (does not have to be as exhaustive as folding) would be good to avoid regressions by people assuming the rewrite should only happens with constant arguments.
I added an exhaustive rewriting test. |
ba73946
to
e249cbe
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
The intrinsic function OUT_OF_RANGE() lacks support in lowering and the runtime. This patch obviates a need for any such support by implementing OUT_OF_RANGE() via rewriting in semantics. This rewriting of OUT_OF_RANGE() calls replaces the existing code that folds OUT_OF_RANGE() calls with constant arguments. Some changes and fixes were necessary outside of OUT_OF_RANGE()'s folding code (now rewriting code), whose testing exposed some other issues worth fixing. - The common::RealDetails<> template class was recoded in terms of a new base class with a constexpr constructor, so that the the characteristics of the various REAL kinds could be queried dynamically as well. This affected some client usage. - There were bugs in the code that folds TRANSFER() when the type of X or MOLD was REAL(10) -- this is a type that occupies 16 bytes per element in execution memory but only 10 bytes (was 12) in the data of std::vector<Scalar<>> in a Constant<>. - Folds of REAL->REAL conversions weren't preserving infinities.
The intrinsic function OUT_OF_RANGE() lacks support in lowering and the runtime. This patch obviates a need for any such support by implementing OUT_OF_RANGE() via rewriting in semantics. This rewriting of OUT_OF_RANGE() calls replaces the existing code that folds OUT_OF_RANGE() calls with constant arguments.
Some changes and fixes were necessary outside of OUT_OF_RANGE()'s folding code (now rewriting code), whose testing exposed some other issues worth fixing.