[llvm-branch-commits] [flang] [Flang] KIND De-templatization (PR #216960)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Aug 25 01:12:26 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Michael Kruse (Meinersbur)

<details>
<summary>Changes</summary>

Avoid template instantiation based on a type's kind, but pass the kind as runtime value. The cross-product of template instantiations (including nested `visit` calls of methods that are already per-kind) leads to super-linear compile-time bloat.
For more details, see the RFC: https://discourse.llvm.org/t/rfc-the-cost-of-templates-when-building-flang/91197.

By its nature, this patch is large, like the renaming of a central type would be. These kind of refactoring cannot be easily split up. However, it has already been split up into the following PR stack:

 * #<!-- -->212956
 * #<!-- -->216958 
 * #<!-- -->216960 (this PR)

Feel free to suggest what else could be *feasibly* split off.


### Implementation notes

 * The conversion is meant to be mostly mechanical, wherever possible. That is, the following rules were applied:

   1. If a function implementation is independent of KIND, just remove the template argument
   2. If another object is passed as argument (or exists as a field of the class) with the same KIND, get KIND from its `.kind()` method, and store it in a variable `const int kind`.
   3. Otherwise, add a `kind` argument as the *first* function argument.
   4. For class template arguments, implement a `kind()` method from another member that has the same `KIND` template parameter. This can be a `std::variant` case.
   5. If there is none, introduce a `int kind_;` member.

   `kind` as the first argument is because as a template parameter, it is written before all function arguments. But mostly I seriously confused myself with optional arguments. Having it as first function argument provides consistency.

 * Most of the time KIND was part of a `Type<CAT,KIND>` template argument. This was reduced to `Type<CAT>` with a `kind_` runtime member.

 * Some classes already had `GetKind()` methods. I canonicalized them to `kind()`

 * Predefined Types such as `SubscriptInteger` now also have a corresponding `SubscriptIntegerKind`.

 * Uses of `TypeCategory::Derived` have to use a kind of 0. In every other case it must be non-zero.

 * Some `kind()` methods, such as `ProcedureRef::kind()` only exist because the compiler requires them to exist; they are never actually called.

 * Propagation of kinds is checked for consistency with `CHECK` and `CHECK_KIND`. Consistency here means that it must have the same kind as the KIND template-parameter would have. For instance, two function arguments that originally have the same KIND template parameter, must pass `CHECK(x.kind() == y.kind())`.

 * There are reoccuring idioms such as `Expr<T>{Constant<T>{Scalar<T>{c}}}` (sometime implicit through conversions). Each of these may need a new `kind` function argument. To avoid it being repeated up to three times on the same lines, I introduced helpers such as `MakeConstantExpr` to pass it just once. Some of them like `MakeExtentExpr` already existed. Most of the time this makes the statements shorter than even before KIND-detemplatiziation.


 * A mechanical translation wasn't possible everywhere. Here are the most notable cases:

   1. `common::SearchTypes` moved to `evaluate::SearchTypes`; despite the name `SearchTypes` was in "common", but the actual types to iterate through were provided by the caller. With this PR the caller can only decide a TypeCategory, the possible kinds are known by `evaluate::SearchTypes` itself. Since it know gained domain knowledge, I moved it out of `common`.

   2. `lower::HashEvaluateExpr` uses the kind in its hash value and considers expressions with different kind to be unequal.

   3. `ComplexPartExtractor` is dead code and doesn't even compile. It doesn't cause a build failure only because it was a template and thus the compiler skipped verifying it. With removing the template state, I had to delete this code.

  4. In some cases a lookup depends on the type being looked up and would could return an actual object with a different kind instead of `nullopt`. This is the case for e.g. `match.h`, `std::get_if`, `UnwrapExpr`. In only very few cases this could actually happen in which I added a `kind() == that.kind()` check, normally by construction it would have the same kind anyway (because the template arguments are the same). I also experimented with `UnwrapExpr` having the `kind` argument mandatory, but would introduce many more changes just for propagating `kind` which in the end is tautologially the same anyway. 

 * While this PR is NFCI (No Functional Change Intended), it includes some AI-generated regression tests, that it found to not yet be covered during the creation of this PR. It also includes #<!-- -->213573 and #<!-- -->208760. All tests should pass without this PR as well.

 * Not strictly belonging to this topic, but the AI doesn't like the same object used twice in a statement, at least on of them with `std::move`, because C++ doesn't define the evaluation order. I added a `converted` temporary in those.


### Potential Followup-Work

 * Cleanup: Some purely mechanical changes allow a larger cleanup. For instance, now many `evaluate::SearchTypes` only iterate over a single TypeCategory, i.e. the entire mechanism becomes superfluous. If left them to not make this PR larger than it already is.

 * The added `CHECK` and check `CHECK_KIND` are mostly used to verify the that the runtime kind matches the original KIND template parameter. With further changes, this invariant may not need to be upheld anymore.

 * Some `kind` arguments are only needed to `CHECK` or to propagate it correctly. It may be possible to remove them again, such as for `Constant<T>` which could get the kind from the scalar passed to it. In this case, the scalar could be in monostate, such that it is not yet a reliable source for kind.

 * Some type declaration became pointless, such as `Int1` and `Int8`, which now are the same type.

 * `Type<CAT>` could be replaced globally with just `TypeCategory` when used as a template parameter. When instantiated, it acts like `DynamicType`, but with static `TypeCategory`.


Assisted-by: AI (Claude, ChatGPT, Composer, Grok)

---

Patch is 618.51 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/216960.diff


100 Files Affected:

- (modified) flang/CMakeLists.txt (+1) 
- (modified) flang/include/flang/Common/template.h (-32) 
- (modified) flang/include/flang/Evaluate/call.h (+21-4) 
- (removed) flang/include/flang/Evaluate/char.h (-88) 
- (modified) flang/include/flang/Evaluate/characteristics.h (+1-2) 
- (modified) flang/include/flang/Evaluate/common.h (+7) 
- (modified) flang/include/flang/Evaluate/complex-value.h (+1-1) 
- (removed) flang/include/flang/Evaluate/complex.h (-141) 
- (modified) flang/include/flang/Evaluate/constant.h (+92-16) 
- (modified) flang/include/flang/Evaluate/expression.h (+293-124) 
- (modified) flang/include/flang/Evaluate/fold-designator.h (+5-5) 
- (modified) flang/include/flang/Evaluate/fold.h (+19-10) 
- (modified) flang/include/flang/Evaluate/initial-image.h (+8-10) 
- (removed) flang/include/flang/Evaluate/logical.h (-128) 
- (modified) flang/include/flang/Evaluate/match.h (+7-10) 
- (modified) flang/include/flang/Evaluate/rewrite.h (+10-9) 
- (modified) flang/include/flang/Evaluate/shape.h (+21-1) 
- (modified) flang/include/flang/Evaluate/static-data.h (+1) 
- (modified) flang/include/flang/Evaluate/tools.h (+88-101) 
- (modified) flang/include/flang/Evaluate/type.h (+157-104) 
- (modified) flang/include/flang/Evaluate/variable.h (+48-4) 
- (modified) flang/include/flang/Lower/DirectivesCommon.h (+8-11) 
- (modified) flang/include/flang/Lower/Mangler.h (+10-9) 
- (modified) flang/include/flang/Lower/Support/Utils.h (+9-7) 
- (modified) flang/include/flang/Semantics/dump-expr.h (+2-4) 
- (modified) flang/include/flang/Semantics/scope.h (+1-2) 
- (modified) flang/include/flang/Semantics/type.h (+3) 
- (modified) flang/lib/Evaluate/CMakeLists.txt (+7-4) 
- (modified) flang/lib/Evaluate/call.cpp (+2-1) 
- (modified) flang/lib/Evaluate/character.h (+28-12) 
- (modified) flang/lib/Evaluate/characteristics.cpp (+4-4) 
- (modified) flang/lib/Evaluate/check-expression.cpp (+14-16) 
- (modified) flang/lib/Evaluate/common.cpp (+6) 
- (removed) flang/lib/Evaluate/complex.cpp (-136) 
- (modified) flang/lib/Evaluate/constant.cpp (+48-48) 
- (modified) flang/lib/Evaluate/expression.cpp (+77-50) 
- (modified) flang/lib/Evaluate/fold-character.cpp (+42-41) 
- (modified) flang/lib/Evaluate/fold-complex.cpp (+18-19) 
- (modified) flang/lib/Evaluate/fold-designator.cpp (+15-11) 
- (modified) flang/lib/Evaluate/fold-implementation.h (+344-242) 
- (modified) flang/lib/Evaluate/fold-integer.cpp (+330-280) 
- (modified) flang/lib/Evaluate/fold-logical.cpp (+206-348) 
- (modified) flang/lib/Evaluate/fold-matmul.h (+6-5) 
- (modified) flang/lib/Evaluate/fold-real.cpp (+94-75) 
- (modified) flang/lib/Evaluate/fold-reduction.cpp (+3-2) 
- (modified) flang/lib/Evaluate/fold-reduction.h (+55-44) 
- (modified) flang/lib/Evaluate/fold.cpp (+5-3) 
- (modified) flang/lib/Evaluate/formatting.cpp (+30-41) 
- (modified) flang/lib/Evaluate/host.h (+30-25) 
- (modified) flang/lib/Evaluate/initial-image.cpp (+15-13) 
- (modified) flang/lib/Evaluate/int-power.h (+7-3) 
- (modified) flang/lib/Evaluate/intrinsics-library.cpp (+11-5) 
- (removed) flang/lib/Evaluate/logical.cpp (-17) 
- (modified) flang/lib/Evaluate/real-value-impl.cpp (+4) 
- (modified) flang/lib/Evaluate/shape.cpp (+49-45) 
- (modified) flang/lib/Evaluate/static-data.cpp (+6) 
- (modified) flang/lib/Evaluate/target.cpp (+19-19) 
- (modified) flang/lib/Evaluate/tools.cpp (+186-218) 
- (modified) flang/lib/Evaluate/type.cpp (+12-14) 
- (modified) flang/lib/Evaluate/variable.cpp (+19-20) 
- (modified) flang/lib/Lower/Bridge.cpp (+1-2) 
- (modified) flang/lib/Lower/CallInterface.cpp (+3-2) 
- (modified) flang/lib/Lower/ConvertArrayConstructor.cpp (+19-17) 
- (modified) flang/lib/Lower/ConvertConstant.cpp (+97-90) 
- (modified) flang/lib/Lower/ConvertExprToHLFIR.cpp (+108-103) 
- (modified) flang/lib/Lower/ConvertType.cpp (+10-30) 
- (modified) flang/lib/Lower/OpenMP/Atomic.cpp (+2-6) 
- (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+2-2) 
- (modified) flang/lib/Lower/Support/Utils.cpp (+121-112) 
- (modified) flang/lib/Semantics/check-call.cpp (+1-1) 
- (modified) flang/lib/Semantics/check-case.cpp (+22-18) 
- (modified) flang/lib/Semantics/check-coarray.cpp (+1-1) 
- (modified) flang/lib/Semantics/check-data.cpp (+1-1) 
- (modified) flang/lib/Semantics/check-io.h (+5-2) 
- (modified) flang/lib/Semantics/check-omp-atomic.cpp (+15-14) 
- (modified) flang/lib/Semantics/check-omp-structure.cpp (+1-1) 
- (modified) flang/lib/Semantics/data-to-inits.cpp (+2-2) 
- (modified) flang/lib/Semantics/dump-expr.cpp (+2-7) 
- (modified) flang/lib/Semantics/expression.cpp (+92-87) 
- (modified) flang/lib/Semantics/openmp-utils.cpp (+2-2) 
- (modified) flang/lib/Semantics/pointer-assignment.cpp (+2-2) 
- (modified) flang/lib/Semantics/resolve-names-utils.cpp (+3-3) 
- (modified) flang/lib/Semantics/resolve-names.cpp (+22-17) 
- (modified) flang/lib/Semantics/runtime-type-info.cpp (+44-42) 
- (modified) flang/lib/Semantics/scope.cpp (+5-4) 
- (modified) flang/lib/Semantics/semantics.cpp (+2-2) 
- (modified) flang/lib/Semantics/tools.cpp (+2-2) 
- (modified) flang/lib/Semantics/type.cpp (+11-9) 
- (modified) flang/test/Evaluate/fold-ibits.f90 (+29) 
- (added) flang/test/Evaluate/fold-real-storage-size.f90 (+24) 
- (added) flang/test/Evaluate/fold-real10-storage-size.f90 (+42) 
- (added) flang/test/Lower/constant-literal-kinds.f90 (+63) 
- (added) flang/test/Semantics/modfile86.f90 (+18) 
- (modified) flang/unittests/CMakeLists.txt (+8) 
- (modified) flang/unittests/Evaluate/designator-path.cpp (+1-1) 
- (modified) flang/unittests/Evaluate/expression.cpp (+14-9) 
- (modified) flang/unittests/Evaluate/folding.cpp (+18-9) 
- (modified) flang/unittests/Evaluate/intrinsics.cpp (+107-95) 
- (modified) flang/unittests/Evaluate/logical.cpp (+28-29) 
- (modified) flang/unittests/Evaluate/real.cpp (+9-8) 


``````````diff
diff --git a/flang/CMakeLists.txt b/flang/CMakeLists.txt
index 0c5a690f8712f..5a3b7c1d378be 100644
--- a/flang/CMakeLists.txt
+++ b/flang/CMakeLists.txt
@@ -301,6 +301,7 @@ endif()
 set(LLVM_BUILD_TOOLS ON)
 
 include_directories(BEFORE
+  ${FLANG_BINARY_DIR}/include/object-sizes/$<CONFIG>
   ${FLANG_BINARY_DIR}/include
   ${FLANG_SOURCE_DIR}/include)
 
diff --git a/flang/include/flang/Common/template.h b/flang/include/flang/Common/template.h
index 593a8ba804a60..80511954f4fc4 100644
--- a/flang/include/flang/Common/template.h
+++ b/flang/include/flang/Common/template.h
@@ -316,37 +316,5 @@ struct type_index<Target, List<Ts...>> {
 template <typename Target, typename List>
 inline constexpr std::size_t type_index_v = type_index<Target, List>::value;
 
-// Given a VISITOR class of the general form
-//   struct VISITOR {
-//     using Result = ...;
-//     using Types = std::tuple<...>;
-//     template<typename T> Result Test() { ... }
-//   };
-// SearchTypes will traverse the element types in the tuple in order
-// and invoke VISITOR::Test<T>() on each until it returns a value that
-// casts to true.  If no invocation of Test succeeds, SearchTypes will
-// return a default value.
-template <std::size_t J, typename VISITOR>
-common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypesHelper(
-    VISITOR &&visitor, typename VISITOR::Result &&defaultResult) {
-  using Tuple = typename VISITOR::Types;
-  if constexpr (J < std::tuple_size_v<Tuple>) {
-    if (auto result{visitor.template Test<std::tuple_element_t<J, Tuple>>()}) {
-      return result;
-    }
-    return SearchTypesHelper<J + 1, VISITOR>(
-        std::move(visitor), std::move(defaultResult));
-  } else {
-    return std::move(defaultResult);
-  }
-}
-
-template <typename VISITOR>
-common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypes(
-    VISITOR &&visitor,
-    typename VISITOR::Result defaultResult = typename VISITOR::Result{}) {
-  return SearchTypesHelper<0, VISITOR>(
-      std::move(visitor), std::move(defaultResult));
-}
 } // namespace Fortran::common
 #endif // FORTRAN_COMMON_TEMPLATE_H_
diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index f04ec6c373c88..5001fbc85920e 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -296,6 +296,11 @@ struct SpecificIntrinsic {
 };
 
 struct ProcedureDesignator {
+  static int kind() {
+    llvm_unreachable("This class has no kind");
+    return 0;
+  }
+
   EVALUATE_UNION_CLASS_BOILERPLATE(ProcedureDesignator)
   explicit ProcedureDesignator(SpecificIntrinsic &&i) : u{std::move(i)} {}
   explicit ProcedureDesignator(const Symbol &n) : u{n} {}
@@ -333,6 +338,11 @@ using Chevrons = std::vector<Expr<SomeType>>;
 
 class ProcedureRef {
 public:
+  static int kind() {
+    llvm_unreachable("This class has no kind");
+    return 0;
+  }
+
   CLASS_BOILERPLATE(ProcedureRef)
   ProcedureRef(ProcedureDesignator &&p, ActualArguments &&a,
       bool hasAlternateReturns = false)
@@ -393,15 +403,19 @@ class ProcedureRef {
 
 template <typename A> class FunctionRef : public ProcedureRef {
 public:
+  constexpr int kind() const { return kind_; }
+
   using Result = A;
   CLASS_BOILERPLATE(FunctionRef)
-  explicit FunctionRef(ProcedureRef &&pr) : ProcedureRef{std::move(pr)} {}
-  FunctionRef(ProcedureDesignator &&p, ActualArguments &&a)
-      : ProcedureRef{std::move(p), std::move(a)} {}
+
+  explicit FunctionRef(int kind, ProcedureRef &&pr)
+      : ProcedureRef{std::move(pr)}, kind_{kind} {}
+  FunctionRef(int kind, ProcedureDesignator &&p, ActualArguments &&a)
+      : ProcedureRef{std::move(p), std::move(a)}, kind_{kind} {}
 
   std::optional<DynamicType> GetType() const {
     if constexpr (IsLengthlessIntrinsicType<A>) {
-      return A::GetType();
+      return DynamicType{A::category, kind_};
     } else if (auto type{proc_.GetType()}) {
       // TODO: Non constant explicit length parameters of PDTs result should
       // likely be dropped too. This is not as easy as for characters since some
@@ -413,6 +427,9 @@ template <typename A> class FunctionRef : public ProcedureRef {
       return std::nullopt;
     }
   }
+
+private:
+  int kind_;
 };
 } // namespace Fortran::evaluate
 #endif // FORTRAN_EVALUATE_CALL_H_
diff --git a/flang/include/flang/Evaluate/char.h b/flang/include/flang/Evaluate/char.h
deleted file mode 100644
index 75db8d35c0cf3..0000000000000
--- a/flang/include/flang/Evaluate/char.h
+++ /dev/null
@@ -1,88 +0,0 @@
-//===-- include/flang/Evaluate/char.h ---------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef FORTRAN_EVALUATE_CHAR_H_
-#define FORTRAN_EVALUATE_CHAR_H_
-
-#include "flang/Evaluate/type.h"
-#include <string>
-
-namespace Fortran::evaluate::value {
-
-/// Simple wrapper around a std::string/std:u16string/std::u32string
-template <int KIND> class Character {
-  using Word = Scalar<Type<TypeCategory::Character, KIND>>;
-  using CharT = typename Word::value_type;
-
-public:
-  // rule-of-five
-  ~Character() = default;
-  Character(const Character &v) : word_(v) {}
-  Character(Character &&v) : word_(std::move(v)) {}
-  Character &operator=(const Character &v) {
-    word_ = v.word_;
-    return &this;
-  }
-  Character &operator=(Character &&v) {
-    word_ = std::move(v.word_);
-    return *this;
-  }
-
-  // ctors
-  Character() = default;
-  Character(const Word &v) : word_(v) {}
-  Character(Word &&v) : word_(std::move(v)) {}
-  Character &operator=(const Word &v) { word_ = v; }
-  Character &operator=(Word &&v) { word_ = std::move(v); }
-
-  /// Returns the number of characters stored; not the number of bytes
-  auto size() const { return word_.size(); }
-
-  /// Reads a string of characters from \p raw. \p is the number of bytes to
-  /// read; must be a multiple of the size of a single character.
-  static Word FromRawBytes(const void *raw, std::size_t size) {
-    CHECK(size % sizeof(CharT) == 0);
-    Word s;
-    if (size > 0) {
-      s.assign(static_cast<const CharT *>(raw), size / sizeof(CharT));
-    }
-    return s;
-  }
-
-  /// Writes a string of characters to \p dst. \o is the the number of bytes to
-  /// be written; must be a multiple of the size of a single character.  If the
-  /// string is smaller that \p size, the rest of the memory padded with spaces.
-  /// If the string is shorter than size, only the first characters are written.
-  /// If \p changes points to bool, it will be set to true if any bytes at
-  /// \p dst have changed.
-  void StoreRawBytes(void *dst, std::size_t size, bool *changed = nullptr) {
-    CHECK(size % sizeof(CharT) == 0);
-    if (size > 0) {
-      std::size_t payloadSize{std::min(size, sizeof(CharT) * word_.size())};
-      std::size_t padSize{size - payloadSize};
-
-      // Pad with spaces
-      Word strWithPadding{word_};
-      strWithPadding.append(padSize / sizeof(CharT), static_cast<CharT>(' '));
-
-      if (changed) {
-        if (std::memcmp(dst, strWithPadding.data(), size) == 0) {
-          return;
-        }
-        *changed = true;
-      }
-      std::memcpy(dst, strWithPadding.data(), size);
-    }
-  }
-
-private:
-  Word word_;
-};
-
-} // namespace Fortran::evaluate::value
-#endif // FORTRAN_EVALUATE_CHAR_H_
diff --git a/flang/include/flang/Evaluate/characteristics.h b/flang/include/flang/Evaluate/characteristics.h
index 1ba0bf693c763..492a128ab10f1 100644
--- a/flang/include/flang/Evaluate/characteristics.h
+++ b/flang/include/flang/Evaluate/characteristics.h
@@ -118,9 +118,8 @@ class TypeAndShape {
   }
 
   // Specialization for character designators
-  template <int KIND>
   static std::optional<TypeAndShape> Characterize(
-      const Designator<Type<TypeCategory::Character, KIND>> &x,
+      const Designator<Type<TypeCategory::Character>> &x,
       FoldingContext &context, bool invariantOnly = true) {
     const auto *symbol{UnwrapWholeSymbolOrComponentDataRef(x)};
     if (symbol && !symbol->owner().IsDerivedType()) { // Whole variable
diff --git a/flang/include/flang/Evaluate/common.h b/flang/include/flang/Evaluate/common.h
index 6adf395442edf..7368a424b46b1 100644
--- a/flang/include/flang/Evaluate/common.h
+++ b/flang/include/flang/Evaluate/common.h
@@ -34,6 +34,10 @@ namespace Fortran::evaluate {
 class IntrinsicProcTable;
 class TargetCharacteristics;
 
+namespace value {
+class CharacterValue;
+}
+
 using common::ConstantSubscript;
 using common::RealFlag;
 using common::RealFlags;
@@ -74,6 +78,9 @@ static constexpr Ordering Compare(
   }
 }
 
+Ordering Compare(
+    const value::CharacterValue &x, const value::CharacterValue &y);
+
 static constexpr Ordering Reverse(Ordering ordering) {
   if (ordering == Ordering::Less) {
     return Ordering::Greater;
diff --git a/flang/include/flang/Evaluate/complex-value.h b/flang/include/flang/Evaluate/complex-value.h
index d1501b24f7480..eed00cc503962 100644
--- a/flang/include/flang/Evaluate/complex-value.h
+++ b/flang/include/flang/Evaluate/complex-value.h
@@ -83,7 +83,7 @@ class ComplexValue {
   std::size_t bytesStored() const {
     return re_.bytesStored() + im_.bytesStored();
   }
-  static std::size_t bytesStored(int kind) {
+  static constexpr std::size_t bytesStored(int kind) {
     return 2 * RealValue::bytesStored(kind);
   }
 
diff --git a/flang/include/flang/Evaluate/complex.h b/flang/include/flang/Evaluate/complex.h
deleted file mode 100644
index cb5b8be603c5c..0000000000000
--- a/flang/include/flang/Evaluate/complex.h
+++ /dev/null
@@ -1,141 +0,0 @@
-//===-- include/flang/Evaluate/complex.h ------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef FORTRAN_EVALUATE_COMPLEX_H_
-#define FORTRAN_EVALUATE_COMPLEX_H_
-
-#include "formatting.h"
-#include "real.h"
-#include <string>
-
-namespace llvm {
-class raw_ostream;
-}
-
-namespace Fortran::evaluate::value {
-
-template <typename REAL_TYPE> class Complex {
-public:
-  using Part = REAL_TYPE;
-  static constexpr int bits{2 * Part::bits};
-
-  constexpr Complex() {} // (+0.0, +0.0)
-  constexpr Complex(const Complex &) = default;
-  constexpr Complex(const Part &r, const Part &i) : re_{r}, im_{i} {}
-  explicit constexpr Complex(const Part &r) : re_{r} {}
-  constexpr Complex &operator=(const Complex &) = default;
-  constexpr Complex &operator=(Complex &&) = default;
-
-  constexpr bool operator==(const Complex &that) const {
-    return re_ == that.re_ && im_ == that.im_;
-  }
-
-  constexpr const Part &REAL() const { return re_; }
-  constexpr const Part &AIMAG() const { return im_; }
-  constexpr Complex CONJG() const { return {re_, im_.Negate()}; }
-  constexpr Complex Negate() const { return {re_.Negate(), im_.Negate()}; }
-
-  constexpr bool Equals(const Complex &that) const {
-    return re_.Compare(that.re_) == Relation::Equal &&
-        im_.Compare(that.im_) == Relation::Equal;
-  }
-
-  constexpr bool IsZero() const { return re_.IsZero() && im_.IsZero(); }
-
-  constexpr bool IsInfinite() const {
-    return re_.IsInfinite() || im_.IsInfinite();
-  }
-
-  constexpr bool IsNotANumber() const {
-    return re_.IsNotANumber() || im_.IsNotANumber();
-  }
-
-  constexpr bool IsSignalingNaN() const {
-    return re_.IsSignalingNaN() || im_.IsSignalingNaN();
-  }
-
-  template <typename INT>
-  static ValueWithRealFlags<Complex> FromInteger(const INT &n,
-      bool isUnsigned = false,
-      Rounding rounding = TargetCharacteristics::defaultRounding) {
-    ValueWithRealFlags<Complex> result;
-    result.value.re_ = Part::FromInteger(n, isUnsigned, rounding)
-                           .AccumulateFlags(result.flags);
-    return result;
-  }
-
-  ValueWithRealFlags<Complex> Add(const Complex &,
-      Rounding rounding = TargetCharacteristics::defaultRounding) const;
-  ValueWithRealFlags<Complex> Subtract(const Complex &,
-      Rounding rounding = TargetCharacteristics::defaultRounding) const;
-  ValueWithRealFlags<Complex> Multiply(const Complex &,
-      Rounding rounding = TargetCharacteristics::defaultRounding) const;
-  ValueWithRealFlags<Complex> Divide(const Complex &,
-      Rounding rounding = TargetCharacteristics::defaultRounding) const;
-  ValueWithRealFlags<Complex> KahanSummation(const Complex &,
-      Complex &correction,
-      Rounding rounding = TargetCharacteristics::defaultRounding) const;
-
-  // ABS/CABS = HYPOT(re_, imag_) = SQRT(re_**2 + im_**2)
-  ValueWithRealFlags<Part> ABS(
-      Rounding rounding = TargetCharacteristics::defaultRounding) const {
-    return re_.HYPOT(im_, rounding);
-  }
-
-  constexpr Complex FlushSubnormalToZero() const {
-    return {re_.FlushSubnormalToZero(), im_.FlushSubnormalToZero()};
-  }
-
-  static constexpr Complex NotANumber() {
-    return {Part::NotANumber(), Part::NotANumber()};
-  }
-
-  std::string DumpHexadecimal() const;
-  llvm::raw_ostream &AsFortran(llvm::raw_ostream &, int kind) const;
-
-  /// Number of bytes that FromRawBytes/StoreRawBytes would accesses.
-  /// Note that for COMPLEX(10), this is 32.
-  constexpr static std::size_t bytesStored() { return 2 * Part::bytesStored(); }
-
-  /// De-serializes a complex from \p raw. \p expectedSize must match the the
-  /// number of bytes to be read.
-  static Complex FromRawBytes(
-      const void *raw, [[maybe_unused]] std::size_t expectedSize) {
-    CHECK(bytesStored() == expectedSize);
-    const char *data{static_cast<const char *>(raw)};
-    Part realPart{Part::FromRawBytes(data, Part::bytesStored())};
-    Part imagPart{
-        Part::FromRawBytes(data + Part::bytesStored(), Part::bytesStored())};
-    return {realPart, imagPart};
-  }
-
-  /// Serializes this complex to \p dst. \p expectedSize must match the the
-  /// number of bytes to be written. If \p changed points to a boolean, it will
-  /// be set to true if any bytes at \p dst have changed.
-  void StoreRawBytes(void *dst, [[maybe_unused]] size_t expectedSize,
-      bool *changed = nullptr) const {
-    CHECK(expectedSize == bytesStored());
-    re_.StoreRawBytes(dst, Part::bytesStored(), changed);
-    im_.StoreRawBytes(static_cast<char *>(dst) + Part::bytesStored(),
-        Part::bytesStored(), changed);
-  }
-
-  // TODO: unit testing
-
-private:
-  Part re_, im_;
-};
-
-extern template class Complex<Real<Integer<16>, 11>>;
-extern template class Complex<Real<Integer<16>, 8>>;
-extern template class Complex<Real<Integer<32>, 24>>;
-extern template class Complex<Real<Integer<64>, 53>>;
-extern template class Complex<Real<X87IntegerContainer, 64>>;
-extern template class Complex<Real<Integer<128>, 113>>;
-} // namespace Fortran::evaluate::value
-#endif // FORTRAN_EVALUATE_COMPLEX_H_
diff --git a/flang/include/flang/Evaluate/constant.h b/flang/include/flang/Evaluate/constant.h
index 9ae37cd999aa9..530328e1802bb 100644
--- a/flang/include/flang/Evaluate/constant.h
+++ b/flang/include/flang/Evaluate/constant.h
@@ -110,16 +110,48 @@ class ConstantBase : public ConstantBounds {
   using Result = RESULT;
   using Element = ELEMENT;
 
+  constexpr int kind() const { return kind_; }
+
   // Constructor for creating ConstantBase from an actual value (i.e.
   // literals, etc.)
-  template <typename A,
-      typename = std::enable_if_t<std::is_convertible_v<A, Element>>>
-  ConstantBase(const A &x, Result res = Result{}) : result_{res}, values_{x} {}
+  template <typename A>
+  ConstantBase(int kind, const A &x, Result res)
+      : kind_{kind}, result_{res}, values_{A{kind, x}} {
+    CHECK_KIND(kind, RESULT);
+  }
+  ConstantBase(int kind, ELEMENT &&x)
+      : kind_{kind}, result_{Result{kind}}, values_{std::move(x)} {
+    CHECK_KIND(kind, RESULT);
+  }
 
-  ConstantBase(ELEMENT &&x, Result res = Result{})
-      : result_{res}, values_{std::move(x)} {}
+  template <TypeCategory CAT>
+  ConstantBase(int kind, const SomeKind<CAT> &x)
+      : kind_{kind}, result_{Result{kind}}, values_{x} {
+    CHECK_KIND(kind, RESULT);
+  }
+  template <TypeCategory CAT>
+  ConstantBase(int kind, SomeKind<CAT> &&x)
+      : kind_{kind}, result_{Result{kind}}, values_{std::move(x)} {
+    CHECK_KIND(kind, RESULT);
+  }
+
+  template <typename A>
+  ConstantBase(int kind, const A &x)
+      : kind_{kind}, result_{Result{kind}}, values_{A{kind, x}} {
+    CHECK_KIND(kind, RESULT);
+  }
+  ConstantBase(int kind, ELEMENT &&x, Result res)
+      : kind_{kind}, result_{res}, values_{std::move(x)} {
+    CHECK_KIND(kind, RESULT);
+  }
+
+  ConstantBase(int kind, std::vector<Element> &&x, ConstantSubscripts &&sh)
+      : ConstantBase{kind, std::move(x), std::move(sh), Result{kind}} {}
   ConstantBase(
-      std::vector<Element> &&, ConstantSubscripts &&, Result = Result{});
+      int kind, std::vector<Element> &&, ConstantSubscripts &&, Result);
+  template <typename A, typename B, typename C>
+  ConstantBase(int kind, const std::map<A, B, C> &x, Result res = Result{})
+      : kind_{kind}, result_{res}, values_{x} {}
 
   DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ConstantBase)
   ~ConstantBase();
@@ -140,6 +172,7 @@ class ConstantBase : public ConstantBounds {
   std::size_t CopyFrom(const ConstantBase &source, std::size_t count,
       ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
 
+  int kind_;
   Result result_; // usually empty except for Real & Complex
   std::vector<Element> values_;
 };
@@ -170,22 +203,24 @@ template <typename T> class Constant : public ConstantBase<T> {
       ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
 };
 
-template <int KIND>
-class Constant<Type<TypeCategory::Character, KIND>> : public ConstantBounds {
+template <>
+class Constant<Type<TypeCategory::Character>> : public ConstantBounds {
 public:
-  using Result = Type<TypeCategory::Character, KIND>;
+  using Result = Type<TypeCategory::Character>;
   using Element = Scalar<Result>;
 
+  constexpr int kind() const { return kind_; }
+
   CLASS_BOILERPLATE(Constant)
-  explicit Constant(const Scalar<Result> &);
-  explicit Constant(Scalar<Result> &&);
-  Constant(
-      ConstantSubscript length, std::vector<Element> &&, ConstantSubscripts &&);
+  explicit Constant(int kind, const Scalar<Result> &);
+  explicit Constant(int kind, Scalar<Result> &&);
+  Constant(int kind, ConstantSubscript length, std::vector<Element> &&,
+      ConstantSubscripts &&);
   ~Constant();
 
   bool operator==(const Constant &that) const {
-    return LEN() == that.LEN() && shape() == that.shape() &&
-        values_ == that.values_;
+    return kind() == that.kind() && LEN() == that.LEN() &&
+        shape() == that.shape() && values_ == that.values_;
   }
   bool empty() const;
   std::size_t size() const;
@@ -212,11 +247,12 @@ class Constant<Type<TypeCategory::Character, KIND>> : public ConstantBounds {
   Constant Reshape(ConstantSubscripts &&) const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
   std::string AsFortran() const;
-  DynamicType GetType() const { return {KIND, length_}; }
+  DynamicType GetType() const { return {kind_, length_}; }
   std::size_t CopyFrom(const Constant &source, std::size_t count,
       ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
 
 private:
+  int kind_;
   Scalar<Result> values_; // one contiguous string
   ConstantSubscript length_;
   bool wasHollerith_{false};
@@ -239,6 +275,12 @@ class Constant<SomeDerived>
 
   Constant(const StructureConstructor &);
   Constant(StructureConstructor &&);
+  Constant(int kind, const StructureConstructor &v) : Constant(v) {
+    CHECK(kind == 0);
+  }
+  Constant(int kind, StructureConstructor &&v) : Constant(std::move(v)) {
+    CHECK(kind == 0);
+  }
   Constant(const semantics::DerivedTypeSpec &,
       std::vector<StructureConstructorValues> &&, ConstantSubscripts &&);
   Constant(const semantics::DerivedTypeSpec &,
@@ -254,6 +296,40 @@ class Constant<SomeDerived>
       ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
 };
 
+inline Constant<SubscriptInteger> MakeSubscriptIntCon...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/216960


More information about the llvm-branch-commits mailing list