Skip to content
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

[flang] Add UNSIGNED #113504

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

[flang] Add UNSIGNED #113504

wants to merge 1 commit into from

Conversation

klausler
Copy link
Contributor

Implement the UNSIGNED extension type and operations under control of a language feature flag (-funsigned).

This is nearly identical to the UNSIGNED feature that has been available in Sun Fortran for years, and now implemented in GNU Fortran for gfortran 15, and proposed for ISO standardization in J3/24-116.txt.

See the new documentation for details; but in short, this is C's unsigned type, with guaranteed modular arithmetic for +, -, and *, and the related transformational intrinsic functions SUM & al.

More tests to come.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' flang:driver flang:runtime flang Flang issues not falling into any other category flang:fir-hlfir flang:semantics flang:parser labels Oct 23, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 23, 2024

@llvm/pr-subscribers-flang-fir-hlfir
@llvm/pr-subscribers-clang-driver
@llvm/pr-subscribers-flang-runtime

@llvm/pr-subscribers-flang-semantics

Author: Peter Klausler (klausler)

Changes

Implement the UNSIGNED extension type and operations under control of a language feature flag (-funsigned).

This is nearly identical to the UNSIGNED feature that has been available in Sun Fortran for years, and now implemented in GNU Fortran for gfortran 15, and proposed for ISO standardization in J3/24-116.txt.

See the new documentation for details; but in short, this is C's unsigned type, with guaranteed modular arithmetic for +, -, and *, and the related transformational intrinsic functions SUM & al.

More tests to come.


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

71 Files Affected:

  • (modified) clang/include/clang/Driver/Options.td (+1)
  • (modified) clang/lib/Driver/ToolChains/Flang.cpp (+2-1)
  • (modified) flang/docs/Extensions.md (+1)
  • (added) flang/docs/Unsigned.md (+109)
  • (modified) flang/include/flang/Common/Fortran-features.h (+1-1)
  • (modified) flang/include/flang/Common/Fortran.h (+6-3)
  • (modified) flang/include/flang/Evaluate/expression.h (+39-8)
  • (modified) flang/include/flang/Evaluate/integer.h (+8-3)
  • (modified) flang/include/flang/Evaluate/tools.h (+7-3)
  • (modified) flang/include/flang/Evaluate/type.h (+30-10)
  • (modified) flang/include/flang/ISO_Fortran_binding.h (+6-1)
  • (modified) flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h (+74)
  • (modified) flang/include/flang/Optimizer/Dialect/FIROps.td (+3-2)
  • (modified) flang/include/flang/Optimizer/Dialect/FIRTypes.td (+18-1)
  • (modified) flang/include/flang/Optimizer/Support/Utils.h (+15-5)
  • (modified) flang/include/flang/Parser/dump-parse-tree.h (+3-1)
  • (modified) flang/include/flang/Parser/parse-tree.h (+14-6)
  • (modified) flang/include/flang/Runtime/cpp-type.h (+4)
  • (modified) flang/include/flang/Runtime/numeric.h (+1-1)
  • (modified) flang/include/flang/Runtime/reduce.h (+83)
  • (modified) flang/include/flang/Runtime/reduction.h (+93)
  • (modified) flang/include/flang/Semantics/expression.h (+3-2)
  • (modified) flang/lib/Common/default-kinds.cpp (+1)
  • (modified) flang/lib/Evaluate/expression.cpp (+6)
  • (modified) flang/lib/Evaluate/fold-implementation.h (+33)
  • (modified) flang/lib/Evaluate/fold-integer.cpp (+407-327)
  • (modified) flang/lib/Evaluate/fold-logical.cpp (+4)
  • (modified) flang/lib/Evaluate/fold-matmul.h (+2-2)
  • (modified) flang/lib/Evaluate/fold-reduction.h (+17-2)
  • (modified) flang/lib/Evaluate/formatting.cpp (+7-2)
  • (modified) flang/lib/Evaluate/intrinsics.cpp (+124-64)
  • (modified) flang/lib/Evaluate/target.cpp (+2)
  • (modified) flang/lib/Evaluate/tools.cpp (+45-9)
  • (modified) flang/lib/Evaluate/type.cpp (+9)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+6)
  • (modified) flang/lib/Lower/ConvertConstant.cpp (+11-5)
  • (modified) flang/lib/Lower/ConvertExpr.cpp (+109-19)
  • (modified) flang/lib/Lower/ConvertExprToHLFIR.cpp (+81-5)
  • (modified) flang/lib/Lower/ConvertType.cpp (+5-3)
  • (modified) flang/lib/Lower/IO.cpp (+16-14)
  • (modified) flang/lib/Lower/Mangler.cpp (+2)
  • (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+4)
  • (modified) flang/lib/Optimizer/Builder/Runtime/Reduction.cpp (+191)
  • (modified) flang/lib/Optimizer/Dialect/FIRType.cpp (+41-12)
  • (modified) flang/lib/Parser/Fortran-parsers.cpp (+18-7)
  • (modified) flang/lib/Parser/type-parsers.h (+1)
  • (modified) flang/lib/Semantics/check-arithmeticif.cpp (+3)
  • (modified) flang/lib/Semantics/check-case.cpp (+7-1)
  • (modified) flang/lib/Semantics/expression.cpp (+46-33)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+6)
  • (modified) flang/lib/Semantics/scope.cpp (+1)
  • (modified) flang/lib/Semantics/tools.cpp (+3-1)
  • (modified) flang/module/iso_c_binding.f90 (+29)
  • (modified) flang/module/iso_fortran_env.f90 (+3)
  • (modified) flang/module/iso_fortran_env_impl.f90 (+30)
  • (modified) flang/runtime/descriptor-io.h (+26-9)
  • (modified) flang/runtime/dot-product.cpp (+23)
  • (modified) flang/runtime/edit-output.cpp (+7-7)
  • (modified) flang/runtime/edit-output.h (+7-7)
  • (modified) flang/runtime/extrema.cpp (+133-1)
  • (modified) flang/runtime/io-api-minimal.cpp (+1-1)
  • (modified) flang/runtime/numeric.cpp (+2-2)
  • (modified) flang/runtime/reduce.cpp (+214)
  • (modified) flang/runtime/tools.h (+1-1)
  • (modified) flang/runtime/type-code.cpp (+29)
  • (modified) flang/runtime/type-info.cpp (+1)
  • (added) flang/test/Lower/Unsigned/unsigned-ops.f90 (+26)
  • (modified) flang/test/Lower/allocatable-polymorphic.f90 (+1-1)
  • (modified) flang/test/Semantics/typeinfo01.f90 (+4-4)
  • (modified) flang/test/Semantics/typeinfo08.f90 (+1-1)
  • (added) flang/test/Semantics/unsigned-errors.f90 (+77)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 6491e9ac73ce99..881a6b8386209a 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -6831,6 +6831,7 @@ defm underscoring : OptInFC1FFlag<"underscoring", "Appends one trailing undersco
 defm ppc_native_vec_elem_order: BoolOptionWithoutMarshalling<"f", "ppc-native-vector-element-order",
   PosFlag<SetTrue, [], [ClangOption], "Specifies PowerPC native vector element order (default)">,
   NegFlag<SetFalse, [], [ClangOption], "Specifies PowerPC non-native vector element order">>;
+defm unsigned : OptInFC1FFlag<"unsigned", "Enables UNSIGNED type">;
 
 def fno_automatic : Flag<["-"], "fno-automatic">, Group<f_Group>,
   HelpText<"Implies the SAVE attribute for non-automatic local objects in subprograms unless RECURSIVE">;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index e2f8f6e0cca1c6..89f0110c94d44c 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -120,7 +120,8 @@ void Flang::addOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
                    options::OPT_fintrinsic_modules_path, options::OPT_pedantic,
                    options::OPT_std_EQ, options::OPT_W_Joined,
                    options::OPT_fconvert_EQ, options::OPT_fpass_plugin_EQ,
-                   options::OPT_funderscoring, options::OPT_fno_underscoring});
+                   options::OPT_funderscoring, options::OPT_fno_underscoring,
+                   options::OPT_funsigned, options::OPT_fno_unsigned});
 
   llvm::codegenoptions::DebugInfoKind DebugInfoKind;
   if (Args.hasArg(options::OPT_gN_Group)) {
diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index f85a3eb39ed191..5867d9a14c084c 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -417,6 +417,7 @@ end
   [-fimplicit-none-type-never]
 * Old-style `PARAMETER pi=3.14` statement without parentheses
   [-falternative-parameter-statement]
+* `UNSIGNED` type (-funsigned)
 
 ### Extensions and legacy features deliberately not supported
 
diff --git a/flang/docs/Unsigned.md b/flang/docs/Unsigned.md
new file mode 100644
index 00000000000000..d9f8b78507dd3f
--- /dev/null
+++ b/flang/docs/Unsigned.md
@@ -0,0 +1,109 @@
+<!--===- docs/Unsigned.md 
+  
+   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
+  
+-->
+
+# Fortran Extensions supported by Flang
+
+```{contents}
+---
+local:
+---
+```
+
+For better compatibility with GNU Fortran and Sun Fortran,
+this compiler supports an option (`-funsigned`) that enables
+the `UNSIGNED` data type, constants, intrinsic functions,
+its use with intrinsic operations and `SELECT CASE`, and C
+language interoperability.
+
+## `UNSIGNED` type
+
+`UNSIGNED` is a numeric with the same kinds as `INTEGER`.
+It may appear as a type-spec in any context, including
+a type declaration statement, a type-decl in an array
+constructor or `ALLOCATE` statement, `IMPLICIT`, or a
+function statement's prefix.
+
+`UNSIGNED` constants are nonempty strings of decimal digits
+followed by the letter `U` and optionally a kind suffix with
+an underscore.
+
+## `UNSIGNED` operations
+
+`UNSIGNED` operands are accepted for unary negation (`-`),
+the basic four binary arithmetic intrinsic operations `+`, `-`, `*`, and `/`,
+and for numeric relational operators.
+The power operator `**` does not accept `UNSIGNED` operands.
+
+Mixed operations with other types are not allowed.
+Mixed operations with one `UNSIGNED` operand and one BOZ literal
+constant operand are allowed.
+When the operands' kinds differ, the smaller operand is zero-extended
+to the size of the larger.
+
+The arithmetic operations `u+v`, `-u`, `u-v`, and `u*v` are implemented
+modulo `MAX(HUGE(u),HUGE(v))+1`;
+informally speaking, they always truncate their results, or are
+guaranteed to "wrap".
+
+## `UNSIGNED` intrinsic functions
+
+`UNSIGNED` operands are accepted as operands to,
+or may be returned as results from,
+several intrinsic procedures.
+
+Bitwise operations:
+* `NOT`
+* `IAND`, `IOR`, `IEOR`, `IBCLR`, `IBSET`, `IBITS`, `MERGE_BITS`
+* `BTEST`
+* `ISHFT`, `ISHFTC`
+* `SHIFTA`, `SHIFTL`, `SHIFTR`
+* `TRANSFER`
+* `MVBITS`
+
+The existing unsigned comparisons `BLT`, `BLE`, `BGE`, and `BGT`.
+
+The inquiries `BIT_SIZE`, `DIGITS`, `HUGE`, and `RANGE`.
+
+Homogeneous `MAX` and `MIN`.
+
+The reducing transformationals:
+* `MAXVAL`, `MINVAL`
+* `SUM`, `PRODUCT`
+* `IALL`, `IANY`, `IPARITY`
+* `DOT_PRODUCT`, `MATMUL`
+
+All of the restructuring array transformational intrinsics: `CSHIFT`, `EOSHIFT`,
+  `PACK`, `RESHAPE`, `SPREAD`, `TRANSPOSE`, and `UNPACK`.
+
+The location transformationals `FINDLOC`, `MAXLOC`, and `MINLOC`.
+
+Support for `UNSIGNED` in the `OUT_OF_RANGE` predicate and `RANDOM_NUMBER`
+remains to be implemented.
+
+## Other usage
+
+`UNSIGNED` is allowed in `SELECT CASE`, but not in `DO` loop indices or
+limits, or an arithmetic `IF` expression.
+
+`UNSIGNED` array indices are not allowed.
+
+`UNSIGNED` data may be used as data items in I/O statements, including
+list-directed and `NAMELIST` I/O.
+Format-directed I/O may edit `UNSIGNED` data with `I`, `G`, `B`, `O`, and `Z`
+edit descriptors.
+
+## C interoperability
+
+`UNSIGNED` data map to type codes for C's `unsigned` types in the
+`type` member of a `cdesc_t` descriptor in the `ISO_Fortran_binding.h`
+header file.
+
+## Standard modules
+
+New definitions (`C_UNSIGNED`, `C_UINT8_T`, &c.) were added to ISO_C_BINDING
+and new constants (`UINT8`, `UINT16`, &c.) to ISO_FORTRAN_ENV.
diff --git a/flang/include/flang/Common/Fortran-features.h b/flang/include/flang/Common/Fortran-features.h
index 2b57c7ae50642c..bd8f922cf3d440 100644
--- a/flang/include/flang/Common/Fortran-features.h
+++ b/flang/include/flang/Common/Fortran-features.h
@@ -53,7 +53,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
     NonBindCInteroperability, CudaManaged, CudaUnified,
     PolymorphicActualAllocatableOrPointerToMonomorphicDummy, RelaxedPureDummy,
     UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr,
-    SavedLocalInSpecExpr, PrintNamelist)
+    SavedLocalInSpecExpr, PrintNamelist, Unsigned)
 
 // Portability and suspicious usage warnings
 ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
diff --git a/flang/include/flang/Common/Fortran.h b/flang/include/flang/Common/Fortran.h
index 5b2ed43a8f99c0..a4c20854eb7516 100644
--- a/flang/include/flang/Common/Fortran.h
+++ b/flang/include/flang/Common/Fortran.h
@@ -21,12 +21,15 @@
 namespace Fortran::common {
 class LanguageFeatureControl;
 
-// Fortran has five kinds of intrinsic data types, plus the derived types.
-ENUM_CLASS(TypeCategory, Integer, Real, Complex, Character, Logical, Derived)
+// Fortran has five kinds of standard intrinsic data types, the Unsigned
+// extension, and derived types.
+ENUM_CLASS(
+    TypeCategory, Integer, Unsigned, Real, Complex, Character, Logical, Derived)
 ENUM_CLASS(VectorElementCategory, Integer, Unsigned, Real)
 
 constexpr bool IsNumericTypeCategory(TypeCategory category) {
-  return category == TypeCategory::Integer || category == TypeCategory::Real ||
+  return category == TypeCategory::Integer ||
+      category == TypeCategory::Unsigned || category == TypeCategory::Real ||
       category == TypeCategory::Complex;
 }
 
diff --git a/flang/include/flang/Evaluate/expression.h b/flang/include/flang/Evaluate/expression.h
index 2a40193e32306b..9ea037a2f7c429 100644
--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -209,10 +209,12 @@ template <typename TO, TypeCategory FROMCAT = TO::category>
 struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
   // Fortran doesn't have conversions between kinds of CHARACTER apart from
   // assignments, and in those the data must be convertible to/from 7-bit ASCII.
-  static_assert(((TO::category == TypeCategory::Integer ||
-                     TO::category == TypeCategory::Real) &&
-                    (FROMCAT == TypeCategory::Integer ||
-                        FROMCAT == TypeCategory::Real)) ||
+  static_assert(
+      ((TO::category == TypeCategory::Integer ||
+           TO::category == TypeCategory::Real ||
+           TO::category == TypeCategory::Unsigned) &&
+          (FROMCAT == TypeCategory::Integer || FROMCAT == TypeCategory::Real ||
+              FROMCAT == TypeCategory::Unsigned)) ||
       TO::category == FROMCAT);
   using Result = TO;
   using Operand = SomeKind<FROMCAT>;
@@ -526,7 +528,8 @@ class Expr<Type<TypeCategory::Integer, KIND>>
 
 private:
   using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
-      Convert<Result, TypeCategory::Real>>;
+      Convert<Result, TypeCategory::Real>,
+      Convert<Result, TypeCategory::Unsigned>>;
   using Operations = std::tuple<Parentheses<Result>, Negate<Result>,
       Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
       Power<Result>, Extremum<Result>>;
@@ -547,6 +550,29 @@ class Expr<Type<TypeCategory::Integer, KIND>>
       u;
 };
 
+template <int KIND>
+class Expr<Type<TypeCategory::Unsigned, KIND>>
+    : public ExpressionBase<Type<TypeCategory::Unsigned, KIND>> {
+public:
+  using Result = Type<TypeCategory::Unsigned, KIND>;
+
+  EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
+
+private:
+  using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
+      Convert<Result, TypeCategory::Real>,
+      Convert<Result, TypeCategory::Unsigned>>;
+  using Operations =
+      std::tuple<Parentheses<Result>, Negate<Result>, Add<Result>,
+          Subtract<Result>, Multiply<Result>, Divide<Result>, Extremum<Result>>;
+  using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
+      Designator<Result>, FunctionRef<Result>>;
+
+public:
+  common::TupleToVariant<common::CombineTuples<Operations, Conversions, Others>>
+      u;
+};
+
 template <int KIND>
 class Expr<Type<TypeCategory::Real, KIND>>
     : public ExpressionBase<Type<TypeCategory::Real, KIND>> {
@@ -560,7 +586,8 @@ class Expr<Type<TypeCategory::Real, KIND>>
   // N.B. Real->Complex and Complex->Real conversions are done with CMPLX
   // and part access operations (resp.).
   using Conversions = std::variant<Convert<Result, TypeCategory::Integer>,
-      Convert<Result, TypeCategory::Real>>;
+      Convert<Result, TypeCategory::Real>,
+      Convert<Result, TypeCategory::Unsigned>>;
   using Operations = std::variant<ComplexComponent<KIND>, Parentheses<Result>,
       Negate<Result>, Add<Result>, Subtract<Result>, Multiply<Result>,
       Divide<Result>, Power<Result>, RealToIntPower<Result>, Extremum<Result>>;
@@ -590,6 +617,7 @@ class Expr<Type<TypeCategory::Complex, KIND>>
 };
 
 FOR_EACH_INTEGER_KIND(extern template class Expr, )
+FOR_EACH_UNSIGNED_KIND(extern template class Expr, )
 FOR_EACH_REAL_KIND(extern template class Expr, )
 FOR_EACH_COMPLEX_KIND(extern template class Expr, )
 
@@ -629,7 +657,8 @@ class Relational : public Operation<Relational<T>, LogicalResult, T, T> {
   static_assert(Operand::category == TypeCategory::Integer ||
       Operand::category == TypeCategory::Real ||
       Operand::category == TypeCategory::Complex ||
-      Operand::category == TypeCategory::Character);
+      Operand::category == TypeCategory::Character ||
+      Operand::category == TypeCategory::Unsigned);
   CLASS_BOILERPLATE(Relational)
   Relational(
       RelationalOperator r, const Expr<Operand> &a, const Expr<Operand> &b)
@@ -642,7 +671,7 @@ class Relational : public Operation<Relational<T>, LogicalResult, T, T> {
 
 template <> class Relational<SomeType> {
   using DirectlyComparableTypes = common::CombineTuples<IntegerTypes, RealTypes,
-      ComplexTypes, CharacterTypes>;
+      ComplexTypes, CharacterTypes, UnsignedTypes>;
 
 public:
   using Result = LogicalResult;
@@ -656,6 +685,7 @@ template <> class Relational<SomeType> {
 };
 
 FOR_EACH_INTEGER_KIND(extern template class Relational, )
+FOR_EACH_UNSIGNED_KIND(extern template class Relational, )
 FOR_EACH_REAL_KIND(extern template class Relational, )
 FOR_EACH_CHARACTER_KIND(extern template class Relational, )
 extern template class Relational<SomeType>;
@@ -886,6 +916,7 @@ FOR_EACH_INTRINSIC_KIND(extern template class ArrayConstructor, )
   FOR_EACH_INTRINSIC_KIND(template class Expr, ) \
   FOR_EACH_CATEGORY_TYPE(template class Expr, ) \
   FOR_EACH_INTEGER_KIND(template class Relational, ) \
+  FOR_EACH_UNSIGNED_KIND(template class Relational, ) \
   FOR_EACH_REAL_KIND(template class Relational, ) \
   FOR_EACH_CHARACTER_KIND(template class Relational, ) \
   template class Relational<SomeType>; \
diff --git a/flang/include/flang/Evaluate/integer.h b/flang/include/flang/Evaluate/integer.h
index e420eb75e3dff0..fccc2ad774a8fc 100644
--- a/flang/include/flang/Evaluate/integer.h
+++ b/flang/include/flang/Evaluate/integer.h
@@ -33,6 +33,12 @@
 
 namespace Fortran::evaluate::value {
 
+// Computes decimal range in the sense of SELECTED_INT_KIND
+static constexpr int DecimalRange(int bits) {
+  // This magic value is LOG10(2.)*1E12.
+  return static_cast<int>((bits * 301029995664) / 1000000000000);
+}
+
 // Implements an integer as an assembly of smaller host integer parts
 // that constitute the digits of a large-radix fixed-point number.
 // For best performance, the type of these parts should be half of the
@@ -367,9 +373,8 @@ class Integer {
   static constexpr int DIGITS{bits - 1}; // don't count the sign bit
   static constexpr Integer HUGE() { return MASKR(bits - 1); }
   static constexpr Integer Least() { return MASKL(1); }
-  static constexpr int RANGE{// in the sense of SELECTED_INT_KIND
-      // This magic value is LOG10(2.)*1E12.
-      static_cast<int>(((bits - 1) * 301029995664) / 1000000000000)};
+  static constexpr int RANGE{DecimalRange(bits - 1)};
+  static constexpr int UnsignedRANGE{DecimalRange(bits)};
 
   constexpr bool IsZero() const {
     for (int j{0}; j < parts; ++j) {
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index f547138f5a116c..7a97a56b020ad9 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -582,7 +582,8 @@ Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) {
 
 template <typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
   static_assert(IsSpecificIntrinsicType<TO>);
-  if constexpr (TO::category == TypeCategory::Integer) {
+  if constexpr (TO::category == TypeCategory::Integer ||
+      TO::category == TypeCategory::Unsigned) {
     return Expr<TO>{
         Constant<TO>{Scalar<TO>::ConvertUnsigned(std::move(x)).value}};
   } else {
@@ -754,11 +755,11 @@ Expr<SomeKind<CAT>> PromoteAndCombine(
 // one of the operands to the type of the other.  Handles special cases with
 // typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
 // powers.
-template <template <typename> class OPR>
+template <template <typename> class OPR, bool CAN_BE_UNSIGNED = true>
 std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
     Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
 
-extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
+extern template std::optional<Expr<SomeType>> NumericOperation<Power, false>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
     int defaultRealKind);
 extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
@@ -910,6 +911,9 @@ common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> TypedWrapper(
   case TypeCategory::Integer:
     return WrapperHelper<TypeCategory::Integer, WRAPPER, WRAPPED>(
         dyType.kind(), std::move(x));
+  case TypeCategory::Unsigned:
+    return WrapperHelper<TypeCategory::Unsigned, WRAPPER, WRAPPED>(
+        dyType.kind(), std::move(x));
   case TypeCategory::Real:
     return WrapperHelper<TypeCategory::Real, WRAPPER, WRAPPED>(
         dyType.kind(), std::move(x));
diff --git a/flang/include/flang/Evaluate/type.h b/flang/include/flang/Evaluate/type.h
index bd8887dbce4e82..1f9296ac4fea75 100644
--- a/flang/include/flang/Evaluate/type.h
+++ b/flang/include/flang/Evaluate/type.h
@@ -69,6 +69,7 @@ static constexpr bool IsValidKindOfIntrinsicType(
     TypeCategory category, std::int64_t kind) {
   switch (category) {
   case TypeCategory::Integer:
+  case TypeCategory::Unsigned:
     return kind == 1 || kind == 2 || kind == 4 || kind == 8 || kind == 16;
   case TypeCategory::Real:
   case TypeCategory::Complex:
@@ -287,6 +288,13 @@ class Type<TypeCategory::Integer, KIND>
   using Scalar = value::Integer<8 * KIND>;
 };
 
+template <int KIND>
+class Type<TypeCategory::Unsigned, KIND>
+    : public TypeBase<TypeCategory::Unsigned, KIND> {
+public:
+  using Scalar = value::Integer<8 * KIND>;
+};
+
 template <int KIND>
 class Type<TypeCategory::Real, KIND>
     : public TypeBase<TypeCategory::Real, KIND> {
@@ -367,11 +375,13 @@ using RealTypes = CategoryTypes<TypeCategory::Real>;
 using ComplexTypes = CategoryTypes<TypeCategory::Complex>;
 using CharacterTypes = CategoryTypes<TypeCategory::Character>;
 using LogicalTypes = CategoryTypes<TypeCategory::Logical>;
+using UnsignedTypes = CategoryTypes<TypeCategory::Unsigned>;
 
 using FloatingTypes = common::CombineTuples<RealTypes, ComplexTypes>;
-using NumericTypes = common::CombineTuples<IntegerTypes, FloatingTypes>;
-using RelationalTypes =
-    common::CombineTuples<IntegerTypes, RealTypes, CharacterTypes>;
+using NumericTypes =
+    common::CombineTuples<IntegerTypes, FloatingTypes, UnsignedTypes>;
+using RelationalTypes = common::CombineTuples<IntegerTypes, RealTypes,
+    CharacterTypes, UnsignedTypes>;
 using AllIntrinsicTypes =
     common::CombineTuples<NumericTypes, CharacterTypes, LogicalTypes>;
 using LengthlessIntrinsicTypes =
@@ -397,11 +407,13 @@ template <TypeCategory CATEGORY> struct SomeKind {
   }
 };
 
-using NumericCategoryTypes = std::tuple<SomeKind<TypeCategory::Integer>,
-    SomeKind<TypeCategory::Real>, SomeKind<TypeCategory::Complex>>;
-using AllIntrinsicCategoryTypes = std::tuple<SomeKind<TypeCategory::Integer>,
-    SomeKind<TypeCategory::Real>, SomeKind<TypeCategory::Complex>,
-    SomeKind<TypeCategory::Character>, SomeKind<TypeCategory::Logical>>;
+using NumericCategoryTypes =
+    std::tuple<SomeKind<TypeCategory::Integer>, SomeKind<TypeCategory::Real>,
+        SomeKind<TypeCategory::Complex>, SomeKind<TypeCategory::Unsigned>>;
+using AllIntrinsicCategoryTypes =
+    std::tuple<SomeKind<TypeCategory::Integer>, SomeKind<TypeCategory::Real>,
+        SomeKind<TypeCategory::Complex>, SomeKind<TypeCategory::Character>,
+        SomeKind<TypeCategory::Logical>, SomeKind<TypeCategory::Unsigned>>;
 
 // Represents a completely generic type (or, for Expr<SomeType>, a typeless
 // value like a BOZ literal or NULL() pointer).
@@ -448,9 +460,10 @@ using SomeReal = SomeKind<TypeCategory::Real>;
 using SomeComplex = SomeKind<TypeCategory::Complex>;
 using SomeCharacter = SomeKind<TypeCategory::Character>;
 using SomeLogical = SomeKind<TypeCategory::Logical>;
+using SomeUnsigned = SomeKind<TypeCategory::Unsigned>;
 using SomeDerived = SomeKind<TypeCategory::Derived>;
 using SomeCategory = std::tuple<SomeInteger, SomeReal, SomeComplex,
-    SomeCharacter, SomeLogical, SomeDerived>;
+    SomeCharacter, SomeLogical, SomeUnsigned, SomeDerived>;
 
 using AllTypes =
     common::CombineTuples<AllIntrinsicTypes, std::tuple<SomeDerived>>;
@@ -507,6 +520,7 @@ bool AreSameDerivedTypeIgnoringTypeParameters(
 #define EXPAND_FOR_EACH_CHARACTER_KIND(M, P, S) M(P, S, 1) M(P, S, 2) M(P, S, 4)
 #define EXPAND_FOR_EACH_LOGICAL_KIND(M, P, S) \
   M(P, S, 1) M(P, S, 2) M(P, S, 4) M(P, S, 8)
+#define EXPAND_FOR_EACH_UNSIGNED_KIND EXPAND_FOR_EACH_INTEGER_KIND
 
 #define FOR_EACH_INTEGER_KIND_HELP(PREFIX, SUFFIX, K) \
   PREFIX<Type<TypeCategory::Integer, K>> SUFFIX;
@@ -518,6 +532,8 @@ bool AreSameDerivedTypeIgnoringTypeParameters(
   PREFIX<Type<TypeCategory::Character, K>> SUFFIX;
 #define FOR_EACH_LOGICAL_KIND_HELP(PREFIX, SUFFIX, K) \
   PREFIX<Type<TypeCategory::Logical, K>> SUFFIX;
+#define FOR_EACH_UNSIGNED_KIND_HELP(PREFIX, SUFFIX, K) \
+  PREFIX<Type<TypeCategory::Unsigned, K>> SUFFIX;
...
[truncated]

@llvmbot
Copy link
Collaborator

llvmbot commented Oct 23, 2024

@llvm/pr-subscribers-flang-driver

Author: Peter Klausler (klausler)

Changes

Implement the UNSIGNED extension type and operations under control of a language feature flag (-funsigned).

This is nearly identical to the UNSIGNED feature that has been available in Sun Fortran for years, and now implemented in GNU Fortran for gfortran 15, and proposed for ISO standardization in J3/24-116.txt.

See the new documentation for details; but in short, this is C's unsigned type, with guaranteed modular arithmetic for +, -, and *, and the related transformational intrinsic functions SUM & al.

More tests to come.


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

71 Files Affected:

  • (modified) clang/include/clang/Driver/Options.td (+1)
  • (modified) clang/lib/Driver/ToolChains/Flang.cpp (+2-1)
  • (modified) flang/docs/Extensions.md (+1)
  • (added) flang/docs/Unsigned.md (+109)
  • (modified) flang/include/flang/Common/Fortran-features.h (+1-1)
  • (modified) flang/include/flang/Common/Fortran.h (+6-3)
  • (modified) flang/include/flang/Evaluate/expression.h (+39-8)
  • (modified) flang/include/flang/Evaluate/integer.h (+8-3)
  • (modified) flang/include/flang/Evaluate/tools.h (+7-3)
  • (modified) flang/include/flang/Evaluate/type.h (+30-10)
  • (modified) flang/include/flang/ISO_Fortran_binding.h (+6-1)
  • (modified) flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h (+74)
  • (modified) flang/include/flang/Optimizer/Dialect/FIROps.td (+3-2)
  • (modified) flang/include/flang/Optimizer/Dialect/FIRTypes.td (+18-1)
  • (modified) flang/include/flang/Optimizer/Support/Utils.h (+15-5)
  • (modified) flang/include/flang/Parser/dump-parse-tree.h (+3-1)
  • (modified) flang/include/flang/Parser/parse-tree.h (+14-6)
  • (modified) flang/include/flang/Runtime/cpp-type.h (+4)
  • (modified) flang/include/flang/Runtime/numeric.h (+1-1)
  • (modified) flang/include/flang/Runtime/reduce.h (+83)
  • (modified) flang/include/flang/Runtime/reduction.h (+93)
  • (modified) flang/include/flang/Semantics/expression.h (+3-2)
  • (modified) flang/lib/Common/default-kinds.cpp (+1)
  • (modified) flang/lib/Evaluate/expression.cpp (+6)
  • (modified) flang/lib/Evaluate/fold-implementation.h (+33)
  • (modified) flang/lib/Evaluate/fold-integer.cpp (+407-327)
  • (modified) flang/lib/Evaluate/fold-logical.cpp (+4)
  • (modified) flang/lib/Evaluate/fold-matmul.h (+2-2)
  • (modified) flang/lib/Evaluate/fold-reduction.h (+17-2)
  • (modified) flang/lib/Evaluate/formatting.cpp (+7-2)
  • (modified) flang/lib/Evaluate/intrinsics.cpp (+124-64)
  • (modified) flang/lib/Evaluate/target.cpp (+2)
  • (modified) flang/lib/Evaluate/tools.cpp (+45-9)
  • (modified) flang/lib/Evaluate/type.cpp (+9)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+6)
  • (modified) flang/lib/Lower/ConvertConstant.cpp (+11-5)
  • (modified) flang/lib/Lower/ConvertExpr.cpp (+109-19)
  • (modified) flang/lib/Lower/ConvertExprToHLFIR.cpp (+81-5)
  • (modified) flang/lib/Lower/ConvertType.cpp (+5-3)
  • (modified) flang/lib/Lower/IO.cpp (+16-14)
  • (modified) flang/lib/Lower/Mangler.cpp (+2)
  • (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+4)
  • (modified) flang/lib/Optimizer/Builder/Runtime/Reduction.cpp (+191)
  • (modified) flang/lib/Optimizer/Dialect/FIRType.cpp (+41-12)
  • (modified) flang/lib/Parser/Fortran-parsers.cpp (+18-7)
  • (modified) flang/lib/Parser/type-parsers.h (+1)
  • (modified) flang/lib/Semantics/check-arithmeticif.cpp (+3)
  • (modified) flang/lib/Semantics/check-case.cpp (+7-1)
  • (modified) flang/lib/Semantics/expression.cpp (+46-33)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+6)
  • (modified) flang/lib/Semantics/scope.cpp (+1)
  • (modified) flang/lib/Semantics/tools.cpp (+3-1)
  • (modified) flang/module/iso_c_binding.f90 (+29)
  • (modified) flang/module/iso_fortran_env.f90 (+3)
  • (modified) flang/module/iso_fortran_env_impl.f90 (+30)
  • (modified) flang/runtime/descriptor-io.h (+26-9)
  • (modified) flang/runtime/dot-product.cpp (+23)
  • (modified) flang/runtime/edit-output.cpp (+7-7)
  • (modified) flang/runtime/edit-output.h (+7-7)
  • (modified) flang/runtime/extrema.cpp (+133-1)
  • (modified) flang/runtime/io-api-minimal.cpp (+1-1)
  • (modified) flang/runtime/numeric.cpp (+2-2)
  • (modified) flang/runtime/reduce.cpp (+214)
  • (modified) flang/runtime/tools.h (+1-1)
  • (modified) flang/runtime/type-code.cpp (+29)
  • (modified) flang/runtime/type-info.cpp (+1)
  • (added) flang/test/Lower/Unsigned/unsigned-ops.f90 (+26)
  • (modified) flang/test/Lower/allocatable-polymorphic.f90 (+1-1)
  • (modified) flang/test/Semantics/typeinfo01.f90 (+4-4)
  • (modified) flang/test/Semantics/typeinfo08.f90 (+1-1)
  • (added) flang/test/Semantics/unsigned-errors.f90 (+77)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 6491e9ac73ce99..881a6b8386209a 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -6831,6 +6831,7 @@ defm underscoring : OptInFC1FFlag<"underscoring", "Appends one trailing undersco
 defm ppc_native_vec_elem_order: BoolOptionWithoutMarshalling<"f", "ppc-native-vector-element-order",
   PosFlag<SetTrue, [], [ClangOption], "Specifies PowerPC native vector element order (default)">,
   NegFlag<SetFalse, [], [ClangOption], "Specifies PowerPC non-native vector element order">>;
+defm unsigned : OptInFC1FFlag<"unsigned", "Enables UNSIGNED type">;
 
 def fno_automatic : Flag<["-"], "fno-automatic">, Group<f_Group>,
   HelpText<"Implies the SAVE attribute for non-automatic local objects in subprograms unless RECURSIVE">;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index e2f8f6e0cca1c6..89f0110c94d44c 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -120,7 +120,8 @@ void Flang::addOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
                    options::OPT_fintrinsic_modules_path, options::OPT_pedantic,
                    options::OPT_std_EQ, options::OPT_W_Joined,
                    options::OPT_fconvert_EQ, options::OPT_fpass_plugin_EQ,
-                   options::OPT_funderscoring, options::OPT_fno_underscoring});
+                   options::OPT_funderscoring, options::OPT_fno_underscoring,
+                   options::OPT_funsigned, options::OPT_fno_unsigned});
 
   llvm::codegenoptions::DebugInfoKind DebugInfoKind;
   if (Args.hasArg(options::OPT_gN_Group)) {
diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index f85a3eb39ed191..5867d9a14c084c 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -417,6 +417,7 @@ end
   [-fimplicit-none-type-never]
 * Old-style `PARAMETER pi=3.14` statement without parentheses
   [-falternative-parameter-statement]
+* `UNSIGNED` type (-funsigned)
 
 ### Extensions and legacy features deliberately not supported
 
diff --git a/flang/docs/Unsigned.md b/flang/docs/Unsigned.md
new file mode 100644
index 00000000000000..d9f8b78507dd3f
--- /dev/null
+++ b/flang/docs/Unsigned.md
@@ -0,0 +1,109 @@
+<!--===- docs/Unsigned.md 
+  
+   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
+  
+-->
+
+# Fortran Extensions supported by Flang
+
+```{contents}
+---
+local:
+---
+```
+
+For better compatibility with GNU Fortran and Sun Fortran,
+this compiler supports an option (`-funsigned`) that enables
+the `UNSIGNED` data type, constants, intrinsic functions,
+its use with intrinsic operations and `SELECT CASE`, and C
+language interoperability.
+
+## `UNSIGNED` type
+
+`UNSIGNED` is a numeric with the same kinds as `INTEGER`.
+It may appear as a type-spec in any context, including
+a type declaration statement, a type-decl in an array
+constructor or `ALLOCATE` statement, `IMPLICIT`, or a
+function statement's prefix.
+
+`UNSIGNED` constants are nonempty strings of decimal digits
+followed by the letter `U` and optionally a kind suffix with
+an underscore.
+
+## `UNSIGNED` operations
+
+`UNSIGNED` operands are accepted for unary negation (`-`),
+the basic four binary arithmetic intrinsic operations `+`, `-`, `*`, and `/`,
+and for numeric relational operators.
+The power operator `**` does not accept `UNSIGNED` operands.
+
+Mixed operations with other types are not allowed.
+Mixed operations with one `UNSIGNED` operand and one BOZ literal
+constant operand are allowed.
+When the operands' kinds differ, the smaller operand is zero-extended
+to the size of the larger.
+
+The arithmetic operations `u+v`, `-u`, `u-v`, and `u*v` are implemented
+modulo `MAX(HUGE(u),HUGE(v))+1`;
+informally speaking, they always truncate their results, or are
+guaranteed to "wrap".
+
+## `UNSIGNED` intrinsic functions
+
+`UNSIGNED` operands are accepted as operands to,
+or may be returned as results from,
+several intrinsic procedures.
+
+Bitwise operations:
+* `NOT`
+* `IAND`, `IOR`, `IEOR`, `IBCLR`, `IBSET`, `IBITS`, `MERGE_BITS`
+* `BTEST`
+* `ISHFT`, `ISHFTC`
+* `SHIFTA`, `SHIFTL`, `SHIFTR`
+* `TRANSFER`
+* `MVBITS`
+
+The existing unsigned comparisons `BLT`, `BLE`, `BGE`, and `BGT`.
+
+The inquiries `BIT_SIZE`, `DIGITS`, `HUGE`, and `RANGE`.
+
+Homogeneous `MAX` and `MIN`.
+
+The reducing transformationals:
+* `MAXVAL`, `MINVAL`
+* `SUM`, `PRODUCT`
+* `IALL`, `IANY`, `IPARITY`
+* `DOT_PRODUCT`, `MATMUL`
+
+All of the restructuring array transformational intrinsics: `CSHIFT`, `EOSHIFT`,
+  `PACK`, `RESHAPE`, `SPREAD`, `TRANSPOSE`, and `UNPACK`.
+
+The location transformationals `FINDLOC`, `MAXLOC`, and `MINLOC`.
+
+Support for `UNSIGNED` in the `OUT_OF_RANGE` predicate and `RANDOM_NUMBER`
+remains to be implemented.
+
+## Other usage
+
+`UNSIGNED` is allowed in `SELECT CASE`, but not in `DO` loop indices or
+limits, or an arithmetic `IF` expression.
+
+`UNSIGNED` array indices are not allowed.
+
+`UNSIGNED` data may be used as data items in I/O statements, including
+list-directed and `NAMELIST` I/O.
+Format-directed I/O may edit `UNSIGNED` data with `I`, `G`, `B`, `O`, and `Z`
+edit descriptors.
+
+## C interoperability
+
+`UNSIGNED` data map to type codes for C's `unsigned` types in the
+`type` member of a `cdesc_t` descriptor in the `ISO_Fortran_binding.h`
+header file.
+
+## Standard modules
+
+New definitions (`C_UNSIGNED`, `C_UINT8_T`, &c.) were added to ISO_C_BINDING
+and new constants (`UINT8`, `UINT16`, &c.) to ISO_FORTRAN_ENV.
diff --git a/flang/include/flang/Common/Fortran-features.h b/flang/include/flang/Common/Fortran-features.h
index 2b57c7ae50642c..bd8f922cf3d440 100644
--- a/flang/include/flang/Common/Fortran-features.h
+++ b/flang/include/flang/Common/Fortran-features.h
@@ -53,7 +53,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
     NonBindCInteroperability, CudaManaged, CudaUnified,
     PolymorphicActualAllocatableOrPointerToMonomorphicDummy, RelaxedPureDummy,
     UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr,
-    SavedLocalInSpecExpr, PrintNamelist)
+    SavedLocalInSpecExpr, PrintNamelist, Unsigned)
 
 // Portability and suspicious usage warnings
 ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
diff --git a/flang/include/flang/Common/Fortran.h b/flang/include/flang/Common/Fortran.h
index 5b2ed43a8f99c0..a4c20854eb7516 100644
--- a/flang/include/flang/Common/Fortran.h
+++ b/flang/include/flang/Common/Fortran.h
@@ -21,12 +21,15 @@
 namespace Fortran::common {
 class LanguageFeatureControl;
 
-// Fortran has five kinds of intrinsic data types, plus the derived types.
-ENUM_CLASS(TypeCategory, Integer, Real, Complex, Character, Logical, Derived)
+// Fortran has five kinds of standard intrinsic data types, the Unsigned
+// extension, and derived types.
+ENUM_CLASS(
+    TypeCategory, Integer, Unsigned, Real, Complex, Character, Logical, Derived)
 ENUM_CLASS(VectorElementCategory, Integer, Unsigned, Real)
 
 constexpr bool IsNumericTypeCategory(TypeCategory category) {
-  return category == TypeCategory::Integer || category == TypeCategory::Real ||
+  return category == TypeCategory::Integer ||
+      category == TypeCategory::Unsigned || category == TypeCategory::Real ||
       category == TypeCategory::Complex;
 }
 
diff --git a/flang/include/flang/Evaluate/expression.h b/flang/include/flang/Evaluate/expression.h
index 2a40193e32306b..9ea037a2f7c429 100644
--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -209,10 +209,12 @@ template <typename TO, TypeCategory FROMCAT = TO::category>
 struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
   // Fortran doesn't have conversions between kinds of CHARACTER apart from
   // assignments, and in those the data must be convertible to/from 7-bit ASCII.
-  static_assert(((TO::category == TypeCategory::Integer ||
-                     TO::category == TypeCategory::Real) &&
-                    (FROMCAT == TypeCategory::Integer ||
-                        FROMCAT == TypeCategory::Real)) ||
+  static_assert(
+      ((TO::category == TypeCategory::Integer ||
+           TO::category == TypeCategory::Real ||
+           TO::category == TypeCategory::Unsigned) &&
+          (FROMCAT == TypeCategory::Integer || FROMCAT == TypeCategory::Real ||
+              FROMCAT == TypeCategory::Unsigned)) ||
       TO::category == FROMCAT);
   using Result = TO;
   using Operand = SomeKind<FROMCAT>;
@@ -526,7 +528,8 @@ class Expr<Type<TypeCategory::Integer, KIND>>
 
 private:
   using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
-      Convert<Result, TypeCategory::Real>>;
+      Convert<Result, TypeCategory::Real>,
+      Convert<Result, TypeCategory::Unsigned>>;
   using Operations = std::tuple<Parentheses<Result>, Negate<Result>,
       Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
       Power<Result>, Extremum<Result>>;
@@ -547,6 +550,29 @@ class Expr<Type<TypeCategory::Integer, KIND>>
       u;
 };
 
+template <int KIND>
+class Expr<Type<TypeCategory::Unsigned, KIND>>
+    : public ExpressionBase<Type<TypeCategory::Unsigned, KIND>> {
+public:
+  using Result = Type<TypeCategory::Unsigned, KIND>;
+
+  EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
+
+private:
+  using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
+      Convert<Result, TypeCategory::Real>,
+      Convert<Result, TypeCategory::Unsigned>>;
+  using Operations =
+      std::tuple<Parentheses<Result>, Negate<Result>, Add<Result>,
+          Subtract<Result>, Multiply<Result>, Divide<Result>, Extremum<Result>>;
+  using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
+      Designator<Result>, FunctionRef<Result>>;
+
+public:
+  common::TupleToVariant<common::CombineTuples<Operations, Conversions, Others>>
+      u;
+};
+
 template <int KIND>
 class Expr<Type<TypeCategory::Real, KIND>>
     : public ExpressionBase<Type<TypeCategory::Real, KIND>> {
@@ -560,7 +586,8 @@ class Expr<Type<TypeCategory::Real, KIND>>
   // N.B. Real->Complex and Complex->Real conversions are done with CMPLX
   // and part access operations (resp.).
   using Conversions = std::variant<Convert<Result, TypeCategory::Integer>,
-      Convert<Result, TypeCategory::Real>>;
+      Convert<Result, TypeCategory::Real>,
+      Convert<Result, TypeCategory::Unsigned>>;
   using Operations = std::variant<ComplexComponent<KIND>, Parentheses<Result>,
       Negate<Result>, Add<Result>, Subtract<Result>, Multiply<Result>,
       Divide<Result>, Power<Result>, RealToIntPower<Result>, Extremum<Result>>;
@@ -590,6 +617,7 @@ class Expr<Type<TypeCategory::Complex, KIND>>
 };
 
 FOR_EACH_INTEGER_KIND(extern template class Expr, )
+FOR_EACH_UNSIGNED_KIND(extern template class Expr, )
 FOR_EACH_REAL_KIND(extern template class Expr, )
 FOR_EACH_COMPLEX_KIND(extern template class Expr, )
 
@@ -629,7 +657,8 @@ class Relational : public Operation<Relational<T>, LogicalResult, T, T> {
   static_assert(Operand::category == TypeCategory::Integer ||
       Operand::category == TypeCategory::Real ||
       Operand::category == TypeCategory::Complex ||
-      Operand::category == TypeCategory::Character);
+      Operand::category == TypeCategory::Character ||
+      Operand::category == TypeCategory::Unsigned);
   CLASS_BOILERPLATE(Relational)
   Relational(
       RelationalOperator r, const Expr<Operand> &a, const Expr<Operand> &b)
@@ -642,7 +671,7 @@ class Relational : public Operation<Relational<T>, LogicalResult, T, T> {
 
 template <> class Relational<SomeType> {
   using DirectlyComparableTypes = common::CombineTuples<IntegerTypes, RealTypes,
-      ComplexTypes, CharacterTypes>;
+      ComplexTypes, CharacterTypes, UnsignedTypes>;
 
 public:
   using Result = LogicalResult;
@@ -656,6 +685,7 @@ template <> class Relational<SomeType> {
 };
 
 FOR_EACH_INTEGER_KIND(extern template class Relational, )
+FOR_EACH_UNSIGNED_KIND(extern template class Relational, )
 FOR_EACH_REAL_KIND(extern template class Relational, )
 FOR_EACH_CHARACTER_KIND(extern template class Relational, )
 extern template class Relational<SomeType>;
@@ -886,6 +916,7 @@ FOR_EACH_INTRINSIC_KIND(extern template class ArrayConstructor, )
   FOR_EACH_INTRINSIC_KIND(template class Expr, ) \
   FOR_EACH_CATEGORY_TYPE(template class Expr, ) \
   FOR_EACH_INTEGER_KIND(template class Relational, ) \
+  FOR_EACH_UNSIGNED_KIND(template class Relational, ) \
   FOR_EACH_REAL_KIND(template class Relational, ) \
   FOR_EACH_CHARACTER_KIND(template class Relational, ) \
   template class Relational<SomeType>; \
diff --git a/flang/include/flang/Evaluate/integer.h b/flang/include/flang/Evaluate/integer.h
index e420eb75e3dff0..fccc2ad774a8fc 100644
--- a/flang/include/flang/Evaluate/integer.h
+++ b/flang/include/flang/Evaluate/integer.h
@@ -33,6 +33,12 @@
 
 namespace Fortran::evaluate::value {
 
+// Computes decimal range in the sense of SELECTED_INT_KIND
+static constexpr int DecimalRange(int bits) {
+  // This magic value is LOG10(2.)*1E12.
+  return static_cast<int>((bits * 301029995664) / 1000000000000);
+}
+
 // Implements an integer as an assembly of smaller host integer parts
 // that constitute the digits of a large-radix fixed-point number.
 // For best performance, the type of these parts should be half of the
@@ -367,9 +373,8 @@ class Integer {
   static constexpr int DIGITS{bits - 1}; // don't count the sign bit
   static constexpr Integer HUGE() { return MASKR(bits - 1); }
   static constexpr Integer Least() { return MASKL(1); }
-  static constexpr int RANGE{// in the sense of SELECTED_INT_KIND
-      // This magic value is LOG10(2.)*1E12.
-      static_cast<int>(((bits - 1) * 301029995664) / 1000000000000)};
+  static constexpr int RANGE{DecimalRange(bits - 1)};
+  static constexpr int UnsignedRANGE{DecimalRange(bits)};
 
   constexpr bool IsZero() const {
     for (int j{0}; j < parts; ++j) {
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index f547138f5a116c..7a97a56b020ad9 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -582,7 +582,8 @@ Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) {
 
 template <typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
   static_assert(IsSpecificIntrinsicType<TO>);
-  if constexpr (TO::category == TypeCategory::Integer) {
+  if constexpr (TO::category == TypeCategory::Integer ||
+      TO::category == TypeCategory::Unsigned) {
     return Expr<TO>{
         Constant<TO>{Scalar<TO>::ConvertUnsigned(std::move(x)).value}};
   } else {
@@ -754,11 +755,11 @@ Expr<SomeKind<CAT>> PromoteAndCombine(
 // one of the operands to the type of the other.  Handles special cases with
 // typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
 // powers.
-template <template <typename> class OPR>
+template <template <typename> class OPR, bool CAN_BE_UNSIGNED = true>
 std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
     Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
 
-extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
+extern template std::optional<Expr<SomeType>> NumericOperation<Power, false>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
     int defaultRealKind);
 extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
@@ -910,6 +911,9 @@ common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> TypedWrapper(
   case TypeCategory::Integer:
     return WrapperHelper<TypeCategory::Integer, WRAPPER, WRAPPED>(
         dyType.kind(), std::move(x));
+  case TypeCategory::Unsigned:
+    return WrapperHelper<TypeCategory::Unsigned, WRAPPER, WRAPPED>(
+        dyType.kind(), std::move(x));
   case TypeCategory::Real:
     return WrapperHelper<TypeCategory::Real, WRAPPER, WRAPPED>(
         dyType.kind(), std::move(x));
diff --git a/flang/include/flang/Evaluate/type.h b/flang/include/flang/Evaluate/type.h
index bd8887dbce4e82..1f9296ac4fea75 100644
--- a/flang/include/flang/Evaluate/type.h
+++ b/flang/include/flang/Evaluate/type.h
@@ -69,6 +69,7 @@ static constexpr bool IsValidKindOfIntrinsicType(
     TypeCategory category, std::int64_t kind) {
   switch (category) {
   case TypeCategory::Integer:
+  case TypeCategory::Unsigned:
     return kind == 1 || kind == 2 || kind == 4 || kind == 8 || kind == 16;
   case TypeCategory::Real:
   case TypeCategory::Complex:
@@ -287,6 +288,13 @@ class Type<TypeCategory::Integer, KIND>
   using Scalar = value::Integer<8 * KIND>;
 };
 
+template <int KIND>
+class Type<TypeCategory::Unsigned, KIND>
+    : public TypeBase<TypeCategory::Unsigned, KIND> {
+public:
+  using Scalar = value::Integer<8 * KIND>;
+};
+
 template <int KIND>
 class Type<TypeCategory::Real, KIND>
     : public TypeBase<TypeCategory::Real, KIND> {
@@ -367,11 +375,13 @@ using RealTypes = CategoryTypes<TypeCategory::Real>;
 using ComplexTypes = CategoryTypes<TypeCategory::Complex>;
 using CharacterTypes = CategoryTypes<TypeCategory::Character>;
 using LogicalTypes = CategoryTypes<TypeCategory::Logical>;
+using UnsignedTypes = CategoryTypes<TypeCategory::Unsigned>;
 
 using FloatingTypes = common::CombineTuples<RealTypes, ComplexTypes>;
-using NumericTypes = common::CombineTuples<IntegerTypes, FloatingTypes>;
-using RelationalTypes =
-    common::CombineTuples<IntegerTypes, RealTypes, CharacterTypes>;
+using NumericTypes =
+    common::CombineTuples<IntegerTypes, FloatingTypes, UnsignedTypes>;
+using RelationalTypes = common::CombineTuples<IntegerTypes, RealTypes,
+    CharacterTypes, UnsignedTypes>;
 using AllIntrinsicTypes =
     common::CombineTuples<NumericTypes, CharacterTypes, LogicalTypes>;
 using LengthlessIntrinsicTypes =
@@ -397,11 +407,13 @@ template <TypeCategory CATEGORY> struct SomeKind {
   }
 };
 
-using NumericCategoryTypes = std::tuple<SomeKind<TypeCategory::Integer>,
-    SomeKind<TypeCategory::Real>, SomeKind<TypeCategory::Complex>>;
-using AllIntrinsicCategoryTypes = std::tuple<SomeKind<TypeCategory::Integer>,
-    SomeKind<TypeCategory::Real>, SomeKind<TypeCategory::Complex>,
-    SomeKind<TypeCategory::Character>, SomeKind<TypeCategory::Logical>>;
+using NumericCategoryTypes =
+    std::tuple<SomeKind<TypeCategory::Integer>, SomeKind<TypeCategory::Real>,
+        SomeKind<TypeCategory::Complex>, SomeKind<TypeCategory::Unsigned>>;
+using AllIntrinsicCategoryTypes =
+    std::tuple<SomeKind<TypeCategory::Integer>, SomeKind<TypeCategory::Real>,
+        SomeKind<TypeCategory::Complex>, SomeKind<TypeCategory::Character>,
+        SomeKind<TypeCategory::Logical>, SomeKind<TypeCategory::Unsigned>>;
 
 // Represents a completely generic type (or, for Expr<SomeType>, a typeless
 // value like a BOZ literal or NULL() pointer).
@@ -448,9 +460,10 @@ using SomeReal = SomeKind<TypeCategory::Real>;
 using SomeComplex = SomeKind<TypeCategory::Complex>;
 using SomeCharacter = SomeKind<TypeCategory::Character>;
 using SomeLogical = SomeKind<TypeCategory::Logical>;
+using SomeUnsigned = SomeKind<TypeCategory::Unsigned>;
 using SomeDerived = SomeKind<TypeCategory::Derived>;
 using SomeCategory = std::tuple<SomeInteger, SomeReal, SomeComplex,
-    SomeCharacter, SomeLogical, SomeDerived>;
+    SomeCharacter, SomeLogical, SomeUnsigned, SomeDerived>;
 
 using AllTypes =
     common::CombineTuples<AllIntrinsicTypes, std::tuple<SomeDerived>>;
@@ -507,6 +520,7 @@ bool AreSameDerivedTypeIgnoringTypeParameters(
 #define EXPAND_FOR_EACH_CHARACTER_KIND(M, P, S) M(P, S, 1) M(P, S, 2) M(P, S, 4)
 #define EXPAND_FOR_EACH_LOGICAL_KIND(M, P, S) \
   M(P, S, 1) M(P, S, 2) M(P, S, 4) M(P, S, 8)
+#define EXPAND_FOR_EACH_UNSIGNED_KIND EXPAND_FOR_EACH_INTEGER_KIND
 
 #define FOR_EACH_INTEGER_KIND_HELP(PREFIX, SUFFIX, K) \
   PREFIX<Type<TypeCategory::Integer, K>> SUFFIX;
@@ -518,6 +532,8 @@ bool AreSameDerivedTypeIgnoringTypeParameters(
   PREFIX<Type<TypeCategory::Character, K>> SUFFIX;
 #define FOR_EACH_LOGICAL_KIND_HELP(PREFIX, SUFFIX, K) \
   PREFIX<Type<TypeCategory::Logical, K>> SUFFIX;
+#define FOR_EACH_UNSIGNED_KIND_HELP(PREFIX, SUFFIX, K) \
+  PREFIX<Type<TypeCategory::Unsigned, K>> SUFFIX;
...
[truncated]

@klausler klausler marked this pull request as draft October 23, 2024 23:41
Copy link
Contributor

@jeanPerier jeanPerier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great, I have little to say on the lowering and made a few questions/comments inline

@@ -400,6 +400,69 @@ constexpr TypeBuilderFunc getModel<bool &>() {
return fir::ReferenceType::get(f(context));
};
}
template <>
constexpr TypeBuilderFunc getModel<unsigned short>() {
return getModel<short>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to keep track of the unsigned aspect in the signature because the LLVM signature for unsigned short is different than for signed short (zeroext attribute vs signext attribute)

See https://godbolt.org/z/Yo94sdxE9, that shows clang -emit-llvm output for:

short test_signed(short x) {return x;}
unsigned short test_unsigned(unsigned short x) {return x;}

LLVM:

define dso_local signext i16 @test_signed(i16 noundef signext)
define dso_local zeroext i16 @test_unsigned(i16 noundef zeroext)

These attributes are important (flang used to not generate signext for short, and this caused a bug in gamess).

The same concern probably applies to BIND(C) contexts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing code does not distinguish unsigned int from signed int, so I followed that precedent for other sizes of unsigned types. Should the patch be changed to mark the signature for unsigned int as being unsigned, along with the other sizes, or just the other sizes?

Copy link
Contributor

@jeanPerier jeanPerier Oct 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing code does not distinguish unsigned int from signed int

Oh, I see.

That is likely just because only the short case is likely problematic. The code dealing with target specific aspects that adds signext/zeroext is only triggered for integer types strictly smaller that the int type.

I think I would favor things being consistent and having the unsigned aspect always set here and leave it up to the target aspects to do whatever is needed when generating the LLVM IR (that does not have an unsigned integer type, just specific operations and integer extension attributes).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did try marking all of these explicitly unsigned where appropriate and it broke the world in ways I didn't understand. We can look at this further later when/if I merge this feature and try to clean it up.

flang/lib/Lower/ConvertExprToHLFIR.cpp Outdated Show resolved Hide resolved
flang/lib/Evaluate/intrinsics.cpp Outdated Show resolved Hide resolved
flang/lib/Evaluate/intrinsics.cpp Outdated Show resolved Hide resolved
@DanielCChen
Copy link
Contributor

Seems a place that needs to add UNSIGNED type case

llvm-project/flang/lib/Semantics/resolve-names.cpp:7597:16: error: enumeration value 'Unsigned' not handled in switch [-Werror,-Wswitch]
  switch (type.category()) {
          ~~~~~^~~~~~~~~~
llvm-project/flang/lib/Semantics/resolve-names.cpp:7621:1: error: non-void function does not return a value in all control paths [-Werror,-Wreturn-type]
}

@klausler
Copy link
Contributor Author

Seems a place that needs to add UNSIGNED type case

llvm-project/flang/lib/Semantics/resolve-names.cpp:7597:16: error: enumeration value 'Unsigned' not handled in switch [-Werror,-Wswitch]
  switch (type.category()) {
          ~~~~~^~~~~~~~~~
llvm-project/flang/lib/Semantics/resolve-names.cpp:7621:1: error: non-void function does not return a value in all control paths [-Werror,-Wreturn-type]
}

Thanks, added a case.

Implement the UNSIGNED extension type and operations under control
of a language feature flag (-funsigned).

This is nearly identical to the UNSIGNED feature that has been
available in Sun Fortran for years, and now implemented in GNU
Fortran for gfortran 15, and proposed for ISO standardization
in J3/24-116.txt.

See the new documentation for details; but in short, this is C's
unsigned type, with guaranteed modular arithmetic for +, -, and *,
and the related transformational intrinsic functions SUM & al.

More tests to come.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category flang:driver flang:fir-hlfir flang:parser flang:runtime flang:semantics flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants