Skip to content

Commit fc97d2e

Browse files
authored
[flang] Add UNSIGNED (#113504)
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.
1 parent 6f0e9c4 commit fc97d2e

File tree

98 files changed

+3348
-801
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+3348
-801
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6913,6 +6913,7 @@ defm underscoring : OptInFC1FFlag<"underscoring", "Appends one trailing undersco
69136913
defm ppc_native_vec_elem_order: BoolOptionWithoutMarshalling<"f", "ppc-native-vector-element-order",
69146914
PosFlag<SetTrue, [], [ClangOption], "Specifies PowerPC native vector element order (default)">,
69156915
NegFlag<SetFalse, [], [ClangOption], "Specifies PowerPC non-native vector element order">>;
6916+
defm unsigned : OptInFC1FFlag<"unsigned", "Enables UNSIGNED type">;
69166917

69176918
def fno_automatic : Flag<["-"], "fno-automatic">, Group<f_Group>,
69186919
HelpText<"Implies the SAVE attribute for non-automatic local objects in subprograms unless RECURSIVE">;

clang/lib/Driver/ToolChains/Flang.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ void Flang::addOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
122122
options::OPT_fintrinsic_modules_path, options::OPT_pedantic,
123123
options::OPT_std_EQ, options::OPT_W_Joined,
124124
options::OPT_fconvert_EQ, options::OPT_fpass_plugin_EQ,
125-
options::OPT_funderscoring, options::OPT_fno_underscoring});
125+
options::OPT_funderscoring, options::OPT_fno_underscoring,
126+
options::OPT_funsigned, options::OPT_fno_unsigned});
126127

127128
llvm::codegenoptions::DebugInfoKind DebugInfoKind;
128129
if (Args.hasArg(options::OPT_gN_Group)) {

flang/docs/Extensions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ end
420420
[-fimplicit-none-type-never]
421421
* Old-style `PARAMETER pi=3.14` statement without parentheses
422422
[-falternative-parameter-statement]
423+
* `UNSIGNED` type (-funsigned)
423424

424425
### Extensions and legacy features deliberately not supported
425426

flang/docs/Unsigned.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<!--===- docs/Unsigned.md
2+
3+
Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
See https://llvm.org/LICENSE.txt for license information.
5+
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
7+
-->
8+
9+
# Fortran Extensions supported by Flang
10+
11+
```{contents}
12+
---
13+
local:
14+
---
15+
```
16+
17+
For better compatibility with GNU Fortran and Sun Fortran,
18+
this compiler supports an option (`-funsigned`) that enables
19+
the `UNSIGNED` data type, constants, intrinsic functions,
20+
its use with intrinsic operations and `SELECT CASE`, and C
21+
language interoperability.
22+
23+
## `UNSIGNED` type
24+
25+
`UNSIGNED` is a numeric type with the same kinds as `INTEGER`.
26+
It may appear as a type-spec in any context, including
27+
a type declaration statement, a type-decl in an array
28+
constructor or `ALLOCATE` statement, `IMPLICIT`, or a
29+
function statement's prefix.
30+
31+
`UNSIGNED` constants are nonempty strings of decimal digits
32+
followed by the letter `U` and optionally a kind suffix with
33+
an underscore.
34+
35+
## `UNSIGNED` operations
36+
37+
`UNSIGNED` operands are accepted for unary negation (`-`),
38+
the basic four binary arithmetic intrinsic operations `+`, `-`, `*`, and `/`,
39+
components in complex constructors,
40+
and for numeric relational operators.
41+
The power operator `**` does not accept `UNSIGNED` operands.
42+
43+
Mixed operations with other types are not allowed.
44+
Mixed operations with one `UNSIGNED` operand and one BOZ literal
45+
constant operand are allowed.
46+
When the operands' kinds differ, the smaller operand is zero-extended
47+
to the size of the larger.
48+
49+
The arithmetic operations `u+v`, `-u`, `u-v`, and `u*v` are implemented
50+
modulo `MAX(HUGE(u),HUGE(v))+1`;
51+
informally speaking, they always truncate their results, or are
52+
guaranteed to "wrap".
53+
54+
## `UNSIGNED` intrinsic functions
55+
56+
`UNSIGNED` operands are accepted as operands to,
57+
or may be returned as results from,
58+
several intrinsic procedures.
59+
60+
Bitwise operations:
61+
* `NOT`
62+
* `IAND`, `IOR`, `IEOR`, `IBCLR`, `IBSET`, `IBITS`, `MERGE_BITS`
63+
* `BTEST`
64+
* `ISHFT`, `ISHFTC`
65+
* `SHIFTA`, `SHIFTL`, `SHIFTR`
66+
* `TRANSFER`
67+
* `MVBITS`
68+
69+
The existing unsigned comparisons `BLT`, `BLE`, `BGE`, and `BGT`.
70+
71+
The inquiries `BIT_SIZE`, `DIGITS`, `HUGE`, and `RANGE`.
72+
73+
Homogeneous `MAX` and `MIN`.
74+
75+
`RANDOM_NUMBER`.
76+
77+
The intrinsic array functions:
78+
* `MAXVAL`, `MINVAL`
79+
* `SUM`, `PRODUCT`
80+
* `IALL`, `IANY`, `IPARITY`
81+
* `DOT_PRODUCT`, `MATMUL`
82+
83+
All of the restructuring array transformational intrinsics: `CSHIFT`, `EOSHIFT`,
84+
`PACK`, `RESHAPE`, `SPREAD`, `TRANSPOSE`, and `UNPACK`.
85+
86+
The location transformationals `FINDLOC`, `MAXLOC`, and `MINLOC`.
87+
88+
There is a new `SELECTED_UNSIGNED_KIND` intrinsic function; it happens
89+
to work identically to the existing `SELECTED_INT_KIND`.
90+
91+
Two new intrinsic functions `UMASKL` and `UMASKR` work just like
92+
`MASKL` and `MASKR`, returning unsigned results instead of integers.
93+
94+
Conversions to `UNSIGNED`, or between `UNSIGNED` kinds, can be done
95+
via the new `UINT` intrinsic. The `UNSIGNED` intrinsic name is also
96+
supported as an alias.
97+
98+
Support for `UNSIGNED` in the `OUT_OF_RANGE` predicate remains to be implemented.
99+
100+
## Other usage
101+
102+
`UNSIGNED` is allowed in `SELECT CASE`, but not in `DO` loop indices or
103+
limits, or an arithmetic `IF` expression.
104+
105+
`UNSIGNED` array indices are not allowed.
106+
107+
`UNSIGNED` data may be used as data items in I/O statements, including
108+
list-directed and `NAMELIST` I/O.
109+
Format-directed I/O may edit `UNSIGNED` data with `I`, `G`, `B`, `O`, and `Z`
110+
edit descriptors.
111+
112+
## C interoperability
113+
114+
`UNSIGNED` data map to type codes for C's `unsigned` types in the
115+
`type` member of a `cdesc_t` descriptor in the `ISO_Fortran_binding.h`
116+
header file.
117+
118+
## Standard modules
119+
120+
New definitions (`C_UNSIGNED`, `C_UINT8_T`, &c.) were added to ISO_C_BINDING
121+
and new constants (`UINT8`, `UINT16`, &c.) to ISO_FORTRAN_ENV.

flang/docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ on how to get in touch with us and to learn more about the current status.
8787
f2018-grammar.md
8888
fstack-arrays
8989
Real16MathSupport
90+
Unsigned
9091
```
9192

9293
# Indices and tables

flang/include/flang/Common/Fortran-consts.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414

1515
namespace Fortran::common {
1616

17-
// Fortran has five kinds of intrinsic data types, plus the derived types.
18-
ENUM_CLASS(TypeCategory, Integer, Real, Complex, Character, Logical, Derived)
17+
// Fortran has five kinds of standard intrinsic data types, the Unsigned
18+
// extension, and derived types.
19+
ENUM_CLASS(
20+
TypeCategory, Integer, Unsigned, Real, Complex, Character, Logical, Derived)
1921
ENUM_CLASS(VectorElementCategory, Integer, Unsigned, Real)
2022

2123
ENUM_CLASS(IoStmtKind, None, Backspace, Close, Endfile, Flush, Inquire, Open,

flang/include/flang/Common/Fortran-features.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
5454
PolymorphicActualAllocatableOrPointerToMonomorphicDummy, RelaxedPureDummy,
5555
UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr,
5656
SavedLocalInSpecExpr, PrintNamelist, AssumedRankPassedToNonAssumedRank,
57-
IgnoreIrrelevantAttributes)
57+
IgnoreIrrelevantAttributes, Unsigned)
5858

5959
// Portability and suspicious usage warnings
6060
ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
@@ -73,7 +73,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
7373
PreviousScalarUse, RedeclaredInaccessibleComponent, ImplicitShared,
7474
IndexVarRedefinition, IncompatibleImplicitInterfaces, BadTypeForTarget,
7575
VectorSubscriptFinalization, UndefinedFunctionResult, UselessIomsg,
76-
MismatchingDummyProcedure, SubscriptedEmptyArray)
76+
MismatchingDummyProcedure, SubscriptedEmptyArray, UnsignedLiteralTruncation)
7777

7878
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
7979
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;

flang/include/flang/Common/Fortran.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ namespace Fortran::common {
2323
class LanguageFeatureControl;
2424

2525
constexpr bool IsNumericTypeCategory(TypeCategory category) {
26-
return category == TypeCategory::Integer || category == TypeCategory::Real ||
26+
return category == TypeCategory::Integer ||
27+
category == TypeCategory::Unsigned || category == TypeCategory::Real ||
2728
category == TypeCategory::Complex;
2829
}
2930

flang/include/flang/Evaluate/complex.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@ template <typename REAL_TYPE> class Complex {
6161

6262
template <typename INT>
6363
static ValueWithRealFlags<Complex> FromInteger(const INT &n,
64+
bool isUnsigned = false,
6465
Rounding rounding = TargetCharacteristics::defaultRounding) {
6566
ValueWithRealFlags<Complex> result;
66-
result.value.re_ =
67-
Part::FromInteger(n, rounding).AccumulateFlags(result.flags);
67+
result.value.re_ = Part::FromInteger(n, isUnsigned, rounding)
68+
.AccumulateFlags(result.flags);
6869
return result;
6970
}
7071

flang/include/flang/Evaluate/expression.h

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,12 @@ template <typename TO, TypeCategory FROMCAT = TO::category>
209209
struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
210210
// Fortran doesn't have conversions between kinds of CHARACTER apart from
211211
// assignments, and in those the data must be convertible to/from 7-bit ASCII.
212-
static_assert(((TO::category == TypeCategory::Integer ||
213-
TO::category == TypeCategory::Real) &&
214-
(FROMCAT == TypeCategory::Integer ||
215-
FROMCAT == TypeCategory::Real)) ||
212+
static_assert(
213+
((TO::category == TypeCategory::Integer ||
214+
TO::category == TypeCategory::Real ||
215+
TO::category == TypeCategory::Unsigned) &&
216+
(FROMCAT == TypeCategory::Integer || FROMCAT == TypeCategory::Real ||
217+
FROMCAT == TypeCategory::Unsigned)) ||
216218
TO::category == FROMCAT);
217219
using Result = TO;
218220
using Operand = SomeKind<FROMCAT>;
@@ -526,7 +528,8 @@ class Expr<Type<TypeCategory::Integer, KIND>>
526528

527529
private:
528530
using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
529-
Convert<Result, TypeCategory::Real>>;
531+
Convert<Result, TypeCategory::Real>,
532+
Convert<Result, TypeCategory::Unsigned>>;
530533
using Operations = std::tuple<Parentheses<Result>, Negate<Result>,
531534
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
532535
Power<Result>, Extremum<Result>>;
@@ -547,6 +550,29 @@ class Expr<Type<TypeCategory::Integer, KIND>>
547550
u;
548551
};
549552

553+
template <int KIND>
554+
class Expr<Type<TypeCategory::Unsigned, KIND>>
555+
: public ExpressionBase<Type<TypeCategory::Unsigned, KIND>> {
556+
public:
557+
using Result = Type<TypeCategory::Unsigned, KIND>;
558+
559+
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
560+
561+
private:
562+
using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
563+
Convert<Result, TypeCategory::Real>,
564+
Convert<Result, TypeCategory::Unsigned>>;
565+
using Operations =
566+
std::tuple<Parentheses<Result>, Negate<Result>, Add<Result>,
567+
Subtract<Result>, Multiply<Result>, Divide<Result>, Extremum<Result>>;
568+
using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
569+
Designator<Result>, FunctionRef<Result>>;
570+
571+
public:
572+
common::TupleToVariant<common::CombineTuples<Operations, Conversions, Others>>
573+
u;
574+
};
575+
550576
template <int KIND>
551577
class Expr<Type<TypeCategory::Real, KIND>>
552578
: public ExpressionBase<Type<TypeCategory::Real, KIND>> {
@@ -560,7 +586,8 @@ class Expr<Type<TypeCategory::Real, KIND>>
560586
// N.B. Real->Complex and Complex->Real conversions are done with CMPLX
561587
// and part access operations (resp.).
562588
using Conversions = std::variant<Convert<Result, TypeCategory::Integer>,
563-
Convert<Result, TypeCategory::Real>>;
589+
Convert<Result, TypeCategory::Real>,
590+
Convert<Result, TypeCategory::Unsigned>>;
564591
using Operations = std::variant<ComplexComponent<KIND>, Parentheses<Result>,
565592
Negate<Result>, Add<Result>, Subtract<Result>, Multiply<Result>,
566593
Divide<Result>, Power<Result>, RealToIntPower<Result>, Extremum<Result>>;
@@ -590,6 +617,7 @@ class Expr<Type<TypeCategory::Complex, KIND>>
590617
};
591618

592619
FOR_EACH_INTEGER_KIND(extern template class Expr, )
620+
FOR_EACH_UNSIGNED_KIND(extern template class Expr, )
593621
FOR_EACH_REAL_KIND(extern template class Expr, )
594622
FOR_EACH_COMPLEX_KIND(extern template class Expr, )
595623

@@ -629,7 +657,8 @@ class Relational : public Operation<Relational<T>, LogicalResult, T, T> {
629657
static_assert(Operand::category == TypeCategory::Integer ||
630658
Operand::category == TypeCategory::Real ||
631659
Operand::category == TypeCategory::Complex ||
632-
Operand::category == TypeCategory::Character);
660+
Operand::category == TypeCategory::Character ||
661+
Operand::category == TypeCategory::Unsigned);
633662
CLASS_BOILERPLATE(Relational)
634663
Relational(
635664
RelationalOperator r, const Expr<Operand> &a, const Expr<Operand> &b)
@@ -642,7 +671,7 @@ class Relational : public Operation<Relational<T>, LogicalResult, T, T> {
642671

643672
template <> class Relational<SomeType> {
644673
using DirectlyComparableTypes = common::CombineTuples<IntegerTypes, RealTypes,
645-
ComplexTypes, CharacterTypes>;
674+
ComplexTypes, CharacterTypes, UnsignedTypes>;
646675

647676
public:
648677
using Result = LogicalResult;
@@ -656,6 +685,7 @@ template <> class Relational<SomeType> {
656685
};
657686

658687
FOR_EACH_INTEGER_KIND(extern template class Relational, )
688+
FOR_EACH_UNSIGNED_KIND(extern template class Relational, )
659689
FOR_EACH_REAL_KIND(extern template class Relational, )
660690
FOR_EACH_CHARACTER_KIND(extern template class Relational, )
661691
extern template class Relational<SomeType>;
@@ -886,6 +916,7 @@ FOR_EACH_INTRINSIC_KIND(extern template class ArrayConstructor, )
886916
FOR_EACH_INTRINSIC_KIND(template class Expr, ) \
887917
FOR_EACH_CATEGORY_TYPE(template class Expr, ) \
888918
FOR_EACH_INTEGER_KIND(template class Relational, ) \
919+
FOR_EACH_UNSIGNED_KIND(template class Relational, ) \
889920
FOR_EACH_REAL_KIND(template class Relational, ) \
890921
FOR_EACH_CHARACTER_KIND(template class Relational, ) \
891922
template class Relational<SomeType>; \

flang/include/flang/Evaluate/fold.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,19 @@ constexpr std::optional<std::int64_t> ToInt64(
8989
return std::nullopt;
9090
}
9191
}
92+
template <int KIND>
93+
constexpr std::optional<std::int64_t> ToInt64(
94+
const Expr<Type<TypeCategory::Unsigned, KIND>> &expr) {
95+
if (auto scalar{
96+
GetScalarConstantValue<Type<TypeCategory::Unsigned, KIND>>(expr)}) {
97+
return scalar->ToInt64();
98+
} else {
99+
return std::nullopt;
100+
}
101+
}
92102

93103
std::optional<std::int64_t> ToInt64(const Expr<SomeInteger> &);
104+
std::optional<std::int64_t> ToInt64(const Expr<SomeUnsigned> &);
94105
std::optional<std::int64_t> ToInt64(const Expr<SomeType> &);
95106
std::optional<std::int64_t> ToInt64(const ActualArgument &);
96107

flang/include/flang/Evaluate/integer.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@
3333

3434
namespace Fortran::evaluate::value {
3535

36+
// Computes decimal range in the sense of SELECTED_INT_KIND
37+
static constexpr int DecimalRange(int bits) {
38+
// This magic value is LOG10(2.)*1E12.
39+
return static_cast<int>((bits * 301029995664) / 1000000000000);
40+
}
41+
3642
// Implements an integer as an assembly of smaller host integer parts
3743
// that constitute the digits of a large-radix fixed-point number.
3844
// For best performance, the type of these parts should be half of the
@@ -367,9 +373,8 @@ class Integer {
367373
static constexpr int DIGITS{bits - 1}; // don't count the sign bit
368374
static constexpr Integer HUGE() { return MASKR(bits - 1); }
369375
static constexpr Integer Least() { return MASKL(1); }
370-
static constexpr int RANGE{// in the sense of SELECTED_INT_KIND
371-
// This magic value is LOG10(2.)*1E12.
372-
static_cast<int>(((bits - 1) * 301029995664) / 1000000000000)};
376+
static constexpr int RANGE{DecimalRange(bits - 1)};
377+
static constexpr int UnsignedRANGE{DecimalRange(bits)};
373378

374379
constexpr bool IsZero() const {
375380
for (int j{0}; j < parts; ++j) {

flang/include/flang/Evaluate/real.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,9 @@ template <typename WORD, int PREC> class Real {
288288

289289
template <typename INT>
290290
static ValueWithRealFlags<Real> FromInteger(const INT &n,
291+
bool isUnsigned = false,
291292
Rounding rounding = TargetCharacteristics::defaultRounding) {
292-
bool isNegative{n.IsNegative()};
293+
bool isNegative{!isUnsigned && n.IsNegative()};
293294
INT absN{n};
294295
if (isNegative) {
295296
absN = n.Negate().value; // overflow is safe to ignore

0 commit comments

Comments
 (0)