[flang] mixed Complex; removed BOZConstant in favor of converting in place
authorpeter klausler <pklausler@nvidia.com>
Thu, 6 Sep 2018 00:12:03 +0000 (17:12 -0700)
committerpeter klausler <pklausler@nvidia.com>
Wed, 12 Sep 2018 23:29:13 +0000 (16:29 -0700)
Original-commit: flang-compiler/f18@17a18ddd223d8a91687d59f0299dbbf35f771f72
Reviewed-on: https://github.com/flang-compiler/f18/pull/183
Tree-same-pre-rewrite: false

flang/lib/common/interval.h
flang/lib/evaluate/common.h
flang/lib/evaluate/expression.cc
flang/lib/evaluate/expression.h
flang/lib/evaluate/logical.cc
flang/lib/evaluate/logical.h
flang/lib/evaluate/tools.cc
flang/lib/evaluate/tools.h
flang/lib/evaluate/type.h
flang/lib/parser/features.h

index 0f90100..caffcd7 100644 (file)
@@ -53,6 +53,9 @@ public:
   constexpr bool Contains(const Interval &that) const {
     return Contains(that.start_) && Contains(that.start_ + (that.size_ - 1));
   }
+  constexpr bool IsDisjointWith(const Interval &that) const {
+    return that.NextAfter() <= start_ || NextAfter() <= that.start_;
+  }
   constexpr bool ImmediatelyPrecedes(const Interval &that) const {
     return NextAfter() == that.start_;
   }
@@ -86,6 +89,20 @@ public:
     return {start_ + n, size_ - n};
   }
 
+  constexpr Interval Intersection(const Interval &that) const {
+    if (start_ >= that.NextAfter()) {
+      return {};
+    } else if (start_ >= that.start_) {
+      auto skip{start_ - that.start_};
+      return {start_, std::min(size_, that.size_ - skip)};
+    } else if (NextAfter() <= that.start_) {
+      return {};
+    } else {
+      auto skip{that.start_ - start_};
+      return {that.start_, std::min(that.size_ - size_ - skip)};
+    }
+  }
+
 private:
   A start_;
   std::size_t size_{0};
index a7afead..89b75ea 100644 (file)
@@ -70,7 +70,7 @@ static constexpr Relation Reverse(Relation relation) {
 ENUM_CLASS(
     RealFlag, Overflow, DivideByZero, InvalidArgument, Underflow, Inexact)
 
-using RealFlags = common::EnumSet<RealFlag, 5>;
+using RealFlags = common::EnumSet<RealFlag, RealFlag_enumSize>;
 
 template<typename A> struct ValueWithRealFlags {
   A AccumulateFlags(RealFlags &f) {
index 79762ef..531dd34 100644 (file)
@@ -33,7 +33,6 @@ namespace Fortran::evaluate {
 template<typename D, typename R, typename... O>
 auto Operation<D, R, O...>::Fold(FoldingContext &context)
     -> std::optional<Constant<Result>> {
-  // TODO pmk: generalize
   auto c0{operand<0>().Fold(context)};
   if constexpr (operands() == 1) {
     if (c0.has_value()) {
@@ -42,6 +41,7 @@ auto Operation<D, R, O...>::Fold(FoldingContext &context)
       }
     }
   } else {
+    static_assert(operands() == 2);  // TODO: generalize to N operands?
     auto c1{operand<1>().Fold(context)};
     if (c0.has_value() && c1.has_value()) {
       if (auto scalar{derived().FoldScalar(context, c0->value, c1->value)}) {
@@ -446,10 +446,6 @@ template<typename T> std::ostream &Constant<T>::Dump(std::ostream &o) const {
   }
 }
 
-template<typename T> std::ostream &BOZConstant<T>::Dump(std::ostream &o) const {
-  return o << "Z'" << value.Hexadecimal() << "'";
-}
-
 template<typename RESULT>
 std::ostream &ExpressionBase<RESULT>::Dump(std::ostream &o) const {
   std::visit(
index caa1ced..090619e 100644 (file)
@@ -69,21 +69,10 @@ template<typename T> struct Constant {
 };
 
 // BOZ literal "typeless" constants must be wide enough to hold a numeric
-// value of any supported kind.  They must also be distinguishable from
-// other integer constants, since they are permitted to be used in only a
-// few situations.
-using BOZLiteralConstant = value::Integer<8 * 2 * DefaultComplex::kind>;
-
-// "Typeless" operands to INTEGER, REAL, and COMPLEX operations.
-template<typename T> struct BOZConstant {
-  using Result = T;
-  using Value = BOZLiteralConstant;
-  CLASS_BOILERPLATE(BOZConstant)
-  BOZConstant(const BOZLiteralConstant &x) : value{x} {}
-  BOZConstant(BOZLiteralConstant &&x) : value{std::move(x)} {}
-  std::ostream &Dump(std::ostream &) const;
-  Value value;
-};
+// value of any supported kind of INTEGER or REAL.  They must also be
+// distinguishable from other integer constants, since they are permitted
+// to be used in only a few situations.
+using BOZLiteralConstant = typename LargestReal::Scalar::Word;
 
 // These wrappers around data and function references expose their resolved
 // types.
@@ -414,8 +403,8 @@ private:
   using Operations = std::variant<Parentheses<Result>, Negate<Result>,
       Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
       Power<Result>, Extremum<Result>>;
-  using Others = std::variant<Constant<Result>, BOZConstant<Result>,
-      DataReference<Result>, FunctionReference<Result>>;
+  using Others = std::variant<Constant<Result>, DataReference<Result>,
+      FunctionReference<Result>>;
 
 public:
   common::CombineVariants<Operations, Conversions, Others> u;
@@ -445,8 +434,8 @@ private:
   using Operations = std::variant<ComplexComponent<KIND>, Parentheses<Result>,
       Negate<Result>, Add<Result>, Subtract<Result>, Multiply<Result>,
       Divide<Result>, Power<Result>, RealToIntPower<Result>, Extremum<Result>>;
-  using Others = std::variant<Constant<Result>, BOZConstant<Result>,
-      DataReference<Result>, FunctionReference<Result>>;
+  using Others = std::variant<Constant<Result>, DataReference<Result>,
+      FunctionReference<Result>>;
 
 public:
   common::CombineVariants<Operations, Conversions, Others> u;
@@ -466,11 +455,13 @@ public:
   Expr(const DataRef &x) : u{DataReference<Result>{x}} {}
   Expr(const FunctionRef &x) : u{FunctionReference<Result>{x}} {}
 
+  // TODO pmk: Remove Negate, Add, Subtract in favor of component-wise
+  // operations.
   using Operations = std::variant<Parentheses<Result>, Negate<Result>,
       Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
       Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
-  using Others = std::variant<Constant<Result>, BOZConstant<Result>,
-      DataReference<Result>, FunctionReference<Result>>;
+  using Others = std::variant<Constant<Result>, DataReference<Result>,
+      FunctionReference<Result>>;
 
 public:
   common::CombineVariants<Operations, Others> u;
index 2beb748..da7f34b 100644 (file)
@@ -20,6 +20,5 @@ template class Logical<8>;
 template class Logical<16>;
 template class Logical<32>;
 template class Logical<64>;
-template class Logical<128>;
 
 }  // namespace Fortran::evaluate::value
index df39768..cfab07f 100644 (file)
@@ -57,6 +57,5 @@ extern template class Logical<8>;
 extern template class Logical<16>;
 extern template class Logical<32>;
 extern template class Logical<64>;
-extern template class Logical<128>;
 }  // namespace Fortran::evaluate::value
 #endif  // FORTRAN_EVALUATE_LOGICAL_H_
index 09988bc..20bed09 100644 (file)
@@ -104,42 +104,82 @@ Expr<SomeReal> GetComplexPart(const Expr<SomeComplex> &z, bool isImaginary) {
       z.u);
 }
 
-template<template<typename> class OPR>
-std::optional<Expr<SomeType>> MixedComplex(parser::ContextualMessages &messages,
-    Expr<SomeComplex> &&zx, Expr<SomeType> &&iry) {
+// Handle mixed COMPLEX+REAL (or INTEGER) operations in a smarter way
+// than just converting the second operand to COMPLEX and performing the
+// corresponding COMPLEX+COMPLEX operation.
+template<template<typename> class OPR, TypeCategory RCAT>
+std::optional<Expr<SomeType>> MixedComplexLeft(
+    parser::ContextualMessages &messages, Expr<SomeComplex> &&zx,
+    Expr<SomeKind<RCAT>> &&iry) {
   Expr<SomeReal> zr{GetComplexPart(zx, false)};
   Expr<SomeReal> zi{GetComplexPart(zx, true)};
   if constexpr (std::is_same_v<OPR<DefaultReal>, Add<DefaultReal>> ||
       std::is_same_v<OPR<DefaultReal>, Subtract<DefaultReal>>) {
-    // Addition and subtraction: apply the operation to the real part of the
-    // complex operand, and a transfer/convert its imaginary part.
-    // i.e., (a,b) + c = (a+c, b)
-    if (std::optional<Expr<SomeType>> rr{
-            NumericOperation<OPR>(messages, std::move(zr), std::move(iry))}) {
-      return Package(ConstructComplex(messages, AsGenericExpr(std::move(*rr)),
-          AsGenericExpr(std::move(zi))));
+    // (a,b) + x -> (a+x, b)
+    // (a,b) - x -> (a-x, b)
+    if (std::optional<Expr<SomeType>> rr{NumericOperation<OPR>(messages,
+            AsGenericExpr(std::move(zr)), AsGenericExpr(std::move(iry)))}) {
+      return Package(ConstructComplex(
+          messages, std::move(*rr), AsGenericExpr(std::move(zi))));
     }
   } else if constexpr (std::is_same_v<OPR<DefaultReal>,
                            Multiply<DefaultReal>> ||
       std::is_same_v<OPR<DefaultReal>, Divide<DefaultReal>>) {
-    // Multiplication and division of a COMPLEX value by an INTEGER or REAL
-    // operand: apply the operation to both components of the COMPLEX value,
-    // then convert and recombine them.
-    // i.e., (a,b) * c = (a*c, b*c)
+    // (a,b) * x -> (a*x, b*x)
+    // (a,b) / x -> (a/x, b/x)
     auto copy{iry};
-    auto rr{NumericOperation<OPR>(messages, std::move(zr), std::move(iry))};
-    auto ri{NumericOperation<OPR>(messages, std::move(zi), std::move(copy))};
+    auto rr{NumericOperation<Multiply>(
+        messages, AsGenericExpr(std::move(zr)), AsGenericExpr(std::move(iry)))};
+    auto ri{NumericOperation<Multiply>(messages, AsGenericExpr(std::move(zi)),
+        AsGenericExpr(std::move(copy)))};
     if (auto parts{common::AllPresent(std::move(rr), std::move(ri))}) {
       return Package(ConstructComplex(messages, std::move(std::get<0>(*parts)),
           std::move(std::get<1>(*parts))));
     }
+  } else {
+    // (a,b) ? x -> (a,b) ? (x,0)
+    Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))};
+    return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
+  }
+  return std::nullopt;
+}
+
+// Mixed COMPLEX operations with the COMPLEX operand on the right.
+//  x + (a,b) -> (x+a, b)
+//  x - (a,b) -> (x-a, -b)
+//  x * (a,b) -> (x*a, x*b)
+//  x / (a,b) -> (x,0) / (a,b)
+template<template<typename> class OPR, TypeCategory LCAT>
+std::optional<Expr<SomeType>> MixedComplexRight(
+    parser::ContextualMessages &messages, Expr<SomeKind<LCAT>> &&irx,
+    Expr<SomeComplex> &&zy) {
+  if constexpr (std::is_same_v<OPR<DefaultReal>, Add<DefaultReal>> ||
+      std::is_same_v<OPR<DefaultReal>, Multiply<DefaultReal>>) {
+    // x + (a,b) -> (a,b) + x -> (a+x, b)
+    // x * (a,b) -> (a,b) * x -> (a*x, b*x)
+    return MixedComplexLeft<Add, LCAT>(messages, std::move(zy), std::move(irx));
+  } else if constexpr (std::is_same_v<OPR<DefaultReal>,
+                           Subtract<DefaultReal>>) {
+    // x - (a,b) -> (x-a, -b)
+    Expr<SomeReal> zr{GetComplexPart(zy, false)};
+    Expr<SomeReal> zi{GetComplexPart(zy, true)};
+    if (std::optional<Expr<SomeType>> rr{NumericOperation<Subtract>(messages,
+            AsGenericExpr(std::move(irx)), AsGenericExpr(std::move(zr)))}) {
+      return Package(ConstructComplex(
+          messages, std::move(*rr), AsGenericExpr(-std::move(zi))));
+    }
+  } else {
+    // x / (a,b) -> (x,0) / (a,b)    and any other operators that make it here
+    Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
+    return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
   }
   return std::nullopt;
 }
 
 // N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
-// the operands to a dyadic INTEGER or REAL operation, it assumes the type
-// and kind of the other operand.
+// the operands to a dyadic operation, it assumes the type and kind of the
+// other operand.
+// TODO pmk: add Power, RealToIntPower, &c.
 template<template<typename> class OPR>
 std::optional<Expr<SomeType>> NumericOperation(
     parser::ContextualMessages &messages, Expr<SomeType> &&x,
@@ -154,6 +194,7 @@ std::optional<Expr<SomeType>> NumericOperation(
             return Package(PromoteAndCombine<OPR, TypeCategory::Real>(
                 std::move(rx), std::move(ry)));
           },
+          // Mixed INTEGER/REAL operations
           [](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
             return Package(std::visit(
                 [&](auto &&rxk) -> Expr<SomeReal> {
@@ -173,18 +214,28 @@ std::optional<Expr<SomeType>> NumericOperation(
                 },
                 std::move(ry.u)));
           },
+          // Homogenous and mixed COMPLEX operations
           [](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) {
             return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
                 std::move(zx), std::move(zy)));
           },
           [&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&zy) {
-            return MixedComplex<OPR>(messages, std::move(zx), std::move(zy));
+            return MixedComplexLeft<OPR>(
+                messages, std::move(zx), std::move(zy));
           },
           [&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&zy) {
-            return MixedComplex<OPR>(messages, std::move(zx), std::move(zy));
+            return MixedComplexLeft<OPR>(
+                messages, std::move(zx), std::move(zy));
+          },
+          [&](Expr<SomeInteger> &&zx, Expr<SomeComplex> &&zy) {
+            return MixedComplexRight<OPR>(
+                messages, std::move(zx), std::move(zy));
+          },
+          [&](Expr<SomeReal> &&zx, Expr<SomeComplex> &&zy) {
+            return MixedComplexRight<OPR>(
+                messages, std::move(zx), std::move(zy));
           },
-          // TODO pmk: mixed r+complex, &c.; r/z is tricky
-          // TODO pmk: mixed complex + boz?  yes but what about COMPLEX*16?
+          // Operations with one typeless operand
           [&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) {
             return NumericOperation<OPR>(
                 messages, ConvertTo(iy, std::move(bx)), std::move(y));
@@ -201,6 +252,7 @@ std::optional<Expr<SomeType>> NumericOperation(
             return NumericOperation<OPR>(
                 messages, std::move(x), ConvertTo(rx, std::move(by)));
           },
+          // Default case
           [&](auto &&, auto &&) {
             messages.Say("non-numeric operands to numeric operation"_err_en_US);
             return std::optional<Expr<SomeType>>{std::nullopt};
index 2a614cd..88d9738 100644 (file)
@@ -107,8 +107,16 @@ template<typename TO, TypeCategory FROMCAT>
 Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
   static_assert(TO::isSpecificType);
   if constexpr (FROMCAT != TO::category) {
-    return {Convert<TO, FROMCAT>{std::move(x)}};
+    if constexpr (TO::category == TypeCategory::Complex) {
+      using Part = typename TO::Part;
+      Scalar<Part> zero;
+      return {ComplexConstructor<TO::kind>{
+          ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
+    } else {
+      return {Convert<TO, FROMCAT>{std::move(x)}};
+    }
   } else {
+    // Same type category
     if (auto already{common::GetIf<Expr<TO>>(x.u)}) {
       return std::move(*already);
     }
@@ -134,10 +142,15 @@ Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
 }
 
 template<typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
-  // TODO: check rank == 0
-  // TODO: pmk: truncation warnings
   static_assert(TO::isSpecificType);
-  return Expr<TO>{BOZConstant<TO>{std::move(x)}};
+  using Value = typename Constant<TO>::Value;
+  if constexpr (TO::category == TypeCategory::Integer) {
+    return Expr<TO>{Constant<TO>{Value::ConvertUnsigned(std::move(x)).value}};
+  } else {
+    static_assert(TO::category == TypeCategory::Real);
+    using Word = typename Value::Word;
+    return Expr<TO>{Constant<TO>{Word::ConvertUnsigned(std::move(x)).value}};
+  }
 }
 
 template<TypeCategory TC, int TK, TypeCategory FC>
@@ -256,8 +269,8 @@ template<typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
 
 // Given two expressions of arbitrary kind in the same intrinsic type
 // category, convert one of them if necessary to the larger kind of the
-// other, then combine them with a operation and return a new expression
-// in the same type category.
+// other, then combine the resulting homogenized operands with a given
+// operation, returning a new expression in the same type category.
 template<template<typename> class OPR, TypeCategory CAT>
 Expr<SomeKind<CAT>> PromoteAndCombine(
     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
@@ -287,6 +300,9 @@ extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 
 // Convenience functions and operator overloadings for expression construction.
+// These interfaces are defined only for those situations that cannot possibly
+// need to emit any messages.  Use the more general NumericOperation<>
+// template (above) in other situations.
 
 template<TypeCategory C, int K>
 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
index 2add276..ab7e8f3 100644 (file)
@@ -129,6 +129,7 @@ using DefaultCharacter = Type<TypeCategory::Character, 1>;
 
 using SubscriptInteger = Type<TypeCategory::Integer, 8>;
 using LogicalResult = Type<TypeCategory::Logical, 1>;
+using LargestReal = Type<TypeCategory::Real, 16>;
 
 // For an intrinsic type category CAT, CategoryTypes<CAT> is an instantiation
 // of std::tuple<Type<CAT, K>> over each supported kind K in that category.
index bf5ed6e..82cd2cd 100644 (file)
@@ -31,7 +31,8 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
     ProgramParentheses, PercentRefAndVal, OmitFunctionDummies, CrayPointer,
     Hollerith, ArithmeticIF, Assign, AssignedGOTO, Pause, OpenMP)
 
-using LanguageFeatures = common::EnumSet<LanguageFeature, 64>;
+using LanguageFeatures =
+    common::EnumSet<LanguageFeature, LanguageFeature_enumSize>;
 
 class LanguageFeatureControl {
 public: