diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 7b544d2534d46..14e47f083ecec 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -6913,6 +6913,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 8cffa20c4a2d3..7034e5b475c1d 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -122,7 +122,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 4b4b516d0fb69..626bf4399d632 100644 --- a/flang/docs/Extensions.md +++ b/flang/docs/Extensions.md @@ -420,6 +420,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 0000000000000..5c90e2aa185bc --- /dev/null +++ b/flang/docs/Unsigned.md @@ -0,0 +1,121 @@ + + +# 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 `/`, +components in complex constructors, +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`. + +`RANDOM_NUMBER`. + +The intrinsic array functions: +* `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`. + +Two new intrinsic functions `UMASKL` and `UMASKR` work just like +`MASKL` and `MASKR`, returning unsigned results instead of integers. + +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 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 70478fa0936d0..c35f634746e68 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-consts.h b/flang/include/flang/Common/Fortran-consts.h index cf7884e7454c0..3ce5b6ac7b686 100644 --- a/flang/include/flang/Common/Fortran-consts.h +++ b/flang/include/flang/Common/Fortran-consts.h @@ -14,8 +14,10 @@ namespace Fortran::common { -// 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) ENUM_CLASS(IoStmtKind, None, Backspace, Close, Endfile, Flush, Inquire, Open, diff --git a/flang/include/flang/Common/Fortran-features.h b/flang/include/flang/Common/Fortran-features.h index b04f6117ae965..44f88009f8f2c 100644 --- a/flang/include/flang/Common/Fortran-features.h +++ b/flang/include/flang/Common/Fortran-features.h @@ -54,7 +54,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines, PolymorphicActualAllocatableOrPointerToMonomorphicDummy, RelaxedPureDummy, UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr, SavedLocalInSpecExpr, PrintNamelist, AssumedRankPassedToNonAssumedRank, - IgnoreIrrelevantAttributes) + IgnoreIrrelevantAttributes, Unsigned) // Portability and suspicious usage warnings ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable, @@ -73,7 +73,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable, PreviousScalarUse, RedeclaredInaccessibleComponent, ImplicitShared, IndexVarRedefinition, IncompatibleImplicitInterfaces, BadTypeForTarget, VectorSubscriptFinalization, UndefinedFunctionResult, UselessIomsg, - MismatchingDummyProcedure, SubscriptedEmptyArray) + MismatchingDummyProcedure, SubscriptedEmptyArray, UnsignedLiteralTruncation) using LanguageFeatures = EnumSet; using UsageWarnings = EnumSet; diff --git a/flang/include/flang/Common/Fortran.h b/flang/include/flang/Common/Fortran.h index 72e4348a42a3f..e1922f7654bb1 100644 --- a/flang/include/flang/Common/Fortran.h +++ b/flang/include/flang/Common/Fortran.h @@ -23,7 +23,8 @@ namespace Fortran::common { class LanguageFeatureControl; 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 06eef84241094..2dcd28b59968c 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 2a40193e32306..9ea037a2f7c42 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/fold.h b/flang/include/flang/Evaluate/fold.h index d2a153fb7919e..b21c0f311fd35 100644 --- a/flang/include/flang/Evaluate/fold.h +++ b/flang/include/flang/Evaluate/fold.h @@ -89,8 +89,19 @@ constexpr std::optional ToInt64( return std::nullopt; } } +template +constexpr std::optional ToInt64( + const Expr> &expr) { + if (auto scalar{ + GetScalarConstantValue>(expr)}) { + return scalar->ToInt64(); + } else { + return std::nullopt; + } +} std::optional ToInt64(const Expr &); +std::optional ToInt64(const Expr &); std::optional ToInt64(const Expr &); std::optional ToInt64(const ActualArgument &); diff --git a/flang/include/flang/Evaluate/integer.h b/flang/include/flang/Evaluate/integer.h index e420eb75e3dff..fccc2ad774a8f 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 11cc8f776b0e9..03294881850a1 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 dafacdf1ba0c5..f586c59d46e54 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