From 4c452a0db24c4ad487f119c4dd8f21c1a52246f5 Mon Sep 17 00:00:00 2001 From: Peter Klausler Date: Wed, 16 Oct 2024 14:12:09 -0700 Subject: [PATCH] [flang] Add UNSIGNED 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. --- clang/include/clang/Driver/Options.td | 1 + clang/lib/Driver/ToolChains/Flang.cpp | 3 +- flang/docs/Extensions.md | 1 + flang/docs/Unsigned.md | 116 +++ flang/docs/index.md | 1 + flang/include/flang/Common/Fortran-features.h | 2 +- flang/include/flang/Common/Fortran.h | 9 +- flang/include/flang/Evaluate/complex.h | 5 +- flang/include/flang/Evaluate/expression.h | 47 +- flang/include/flang/Evaluate/integer.h | 11 +- flang/include/flang/Evaluate/real.h | 3 +- flang/include/flang/Evaluate/tools.h | 10 +- flang/include/flang/Evaluate/type.h | 40 +- flang/include/flang/ISO_Fortran_binding.h | 7 +- .../flang/Optimizer/Builder/FIRBuilder.h | 25 + .../Optimizer/Builder/Runtime/RTBuilder.h | 89 +++ .../include/flang/Optimizer/Dialect/FIROps.td | 5 +- .../flang/Optimizer/Dialect/FIRTypes.td | 19 +- flang/include/flang/Optimizer/Support/Utils.h | 20 +- flang/include/flang/Parser/dump-parse-tree.h | 4 +- flang/include/flang/Parser/parse-tree.h | 20 +- flang/include/flang/Runtime/cpp-type.h | 4 + flang/include/flang/Runtime/numeric.h | 2 +- flang/include/flang/Runtime/reduce.h | 83 ++ flang/include/flang/Runtime/reduction.h | 93 +++ flang/include/flang/Semantics/expression.h | 5 +- flang/lib/Common/default-kinds.cpp | 1 + flang/lib/Evaluate/expression.cpp | 6 + flang/lib/Evaluate/fold-implementation.h | 58 +- flang/lib/Evaluate/fold-integer.cpp | 734 ++++++++++-------- flang/lib/Evaluate/fold-logical.cpp | 24 +- flang/lib/Evaluate/fold-matmul.h | 4 +- flang/lib/Evaluate/fold-reduction.h | 19 +- flang/lib/Evaluate/formatting.cpp | 9 +- flang/lib/Evaluate/intrinsics.cpp | 180 +++-- flang/lib/Evaluate/target.cpp | 2 + flang/lib/Evaluate/tools.cpp | 62 +- flang/lib/Evaluate/type.cpp | 9 + flang/lib/Frontend/CompilerInvocation.cpp | 6 + flang/lib/Lower/ConvertConstant.cpp | 16 +- flang/lib/Lower/ConvertExpr.cpp | 127 ++- flang/lib/Lower/ConvertExprToHLFIR.cpp | 84 +- flang/lib/Lower/ConvertType.cpp | 8 +- flang/lib/Lower/IO.cpp | 30 +- flang/lib/Lower/Mangler.cpp | 2 + flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 95 ++- .../Optimizer/Builder/Runtime/Reduction.cpp | 191 +++++ flang/lib/Optimizer/Dialect/FIRType.cpp | 53 +- flang/lib/Parser/Fortran-parsers.cpp | 21 +- flang/lib/Parser/type-parsers.h | 1 + flang/lib/Semantics/check-arithmeticif.cpp | 3 + flang/lib/Semantics/check-case.cpp | 8 +- flang/lib/Semantics/expression.cpp | 107 ++- flang/lib/Semantics/resolve-names.cpp | 11 + flang/lib/Semantics/scope.cpp | 1 + flang/lib/Semantics/tools.cpp | 8 +- flang/module/iso_c_binding.f90 | 29 + flang/module/iso_fortran_env.f90 | 3 + flang/module/iso_fortran_env_impl.f90 | 30 + flang/runtime/descriptor-io.h | 35 +- flang/runtime/dot-product.cpp | 23 + flang/runtime/edit-output.cpp | 14 +- flang/runtime/edit-output.h | 14 +- flang/runtime/extrema.cpp | 134 +++- flang/runtime/io-api-minimal.cpp | 2 +- flang/runtime/numeric.cpp | 4 +- flang/runtime/reduce.cpp | 214 +++++ flang/runtime/tools.h | 2 +- flang/runtime/type-code.cpp | 29 + flang/runtime/type-info.cpp | 1 + flang/test/Evaluate/fold-unsigned.f90 | 120 +++ flang/test/Lower/allocatable-polymorphic.f90 | 2 +- flang/test/Lower/unsigned-ops.f90 | 26 + flang/test/Semantics/typeinfo01.f90 | 8 +- flang/test/Semantics/typeinfo08.f90 | 2 +- flang/test/Semantics/unsigned-errors.f90 | 77 ++ flang/unittests/Evaluate/real.cpp | 4 +- 77 files changed, 2650 insertions(+), 628 deletions(-) create mode 100644 flang/docs/Unsigned.md create mode 100644 flang/test/Evaluate/fold-unsigned.f90 create mode 100644 flang/test/Lower/unsigned-ops.f90 create mode 100644 flang/test/Semantics/unsigned-errors.f90 diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 805b79491e6ea4..482184e9b48da7 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -6873,6 +6873,7 @@ defm underscoring : OptInFC1FFlag<"underscoring", "Appends one trailing undersco defm ppc_native_vec_elem_order: BoolOptionWithoutMarshalling<"f", "ppc-native-vector-element-order", PosFlag, NegFlag>; +defm unsigned : OptInFC1FFlag<"unsigned", "Enables UNSIGNED type">; def fno_automatic : Flag<["-"], "fno-automatic">, 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 f9d2fdffe3b2fc..d49964ff68d947 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..39f53691be8d38 --- /dev/null +++ b/flang/docs/Unsigned.md @@ -0,0 +1,116 @@ + + +# 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 type 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`. + +There is a new `SELECTED_UNSIGNED_KIND` intrinsic function; it happens +to work identically to the existing `SELECTED_INT_KIND`. + +Conversions to `UNSIGNED`, or between `UNSIGNED` kinds, can be done +via the new `UINT` intrinsic. The `UNSIGNED` intrinsic name is also +supported as an alias. + +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/docs/index.md b/flang/docs/index.md index 70478fa0936d0b..c35f634746e68b 100644 --- a/flang/docs/index.md +++ b/flang/docs/index.md @@ -87,6 +87,7 @@ on how to get in touch with us and to learn more about the current status. f2018-grammar.md fstack-arrays Real16MathSupport + Unsigned ``` # Indices and tables diff --git a/flang/include/flang/Common/Fortran-features.h b/flang/include/flang/Common/Fortran-features.h index 74edbe44fdbb1c..cecd4f5b098293 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/complex.h b/flang/include/flang/Evaluate/complex.h index 06eef842410944..2dcd28b59968cd 100644 --- a/flang/include/flang/Evaluate/complex.h +++ b/flang/include/flang/Evaluate/complex.h @@ -61,10 +61,11 @@ template class Complex { template static ValueWithRealFlags FromInteger(const INT &n, + bool isUnsigned = false, Rounding rounding = TargetCharacteristics::defaultRounding) { ValueWithRealFlags result; - result.value.re_ = - Part::FromInteger(n, rounding).AccumulateFlags(result.flags); + result.value.re_ = Part::FromInteger(n, isUnsigned, rounding) + .AccumulateFlags(result.flags); return result; } 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 struct Convert : public Operation, TO, SomeKind> { // 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; @@ -526,7 +528,8 @@ class Expr> private: using Conversions = std::tuple, - Convert>; + Convert, + Convert>; using Operations = std::tuple, Negate, Add, Subtract, Multiply, Divide, Power, Extremum>; @@ -547,6 +550,29 @@ class Expr> u; }; +template +class Expr> + : public ExpressionBase> { +public: + using Result = Type; + + EVALUATE_UNION_CLASS_BOILERPLATE(Expr) + +private: + using Conversions = std::tuple, + Convert, + Convert>; + using Operations = + std::tuple, Negate, Add, + Subtract, Multiply, Divide, Extremum>; + using Others = std::tuple, ArrayConstructor, + Designator, FunctionRef>; + +public: + common::TupleToVariant> + u; +}; + template class Expr> : public ExpressionBase> { @@ -560,7 +586,8 @@ class Expr> // N.B. Real->Complex and Complex->Real conversions are done with CMPLX // and part access operations (resp.). using Conversions = std::variant, - Convert>; + Convert, + Convert>; using Operations = std::variant, Parentheses, Negate, Add, Subtract, Multiply, Divide, Power, RealToIntPower, Extremum>; @@ -590,6 +617,7 @@ class Expr> }; 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, 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 &a, const Expr &b) @@ -642,7 +671,7 @@ class Relational : public Operation, LogicalResult, T, T> { template <> class Relational { using DirectlyComparableTypes = common::CombineTuples; + ComplexTypes, CharacterTypes, UnsignedTypes>; public: using Result = LogicalResult; @@ -656,6 +685,7 @@ template <> class Relational { }; 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; @@ -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; \ 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((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(((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/real.h b/flang/include/flang/Evaluate/real.h index 11cc8f776b0e95..03294881850a13 100644 --- a/flang/include/flang/Evaluate/real.h +++ b/flang/include/flang/Evaluate/real.h @@ -288,8 +288,9 @@ template class Real { template static ValueWithRealFlags FromInteger(const INT &n, + bool isUnsigned = false, Rounding rounding = TargetCharacteristics::defaultRounding) { - bool isNegative{n.IsNegative()}; + bool isNegative{!isUnsigned && n.IsNegative()}; INT absN{n}; if (isNegative) { absN = n.Negate().value; // overflow is safe to ignore 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 ConvertToType(Expr> &&x) { template Expr ConvertToType(BOZLiteralConstant &&x) { static_assert(IsSpecificIntrinsicType); - if constexpr (TO::category == TypeCategory::Integer) { + if constexpr (TO::category == TypeCategory::Integer || + TO::category == TypeCategory::Unsigned) { return Expr{ Constant{Scalar::ConvertUnsigned(std::move(x)).value}}; } else { @@ -754,11 +755,11 @@ Expr> 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