Skip to content

Commit b0ab1e7

Browse files
committed
[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.
1 parent 668f2c7 commit b0ab1e7

File tree

96 files changed

+3338
-799
lines changed

Some content is hidden

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

96 files changed

+3338
-799
lines changed

clang/include/clang/Driver/Options.td

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

68856886
def fno_automatic : Flag<["-"], "fno-automatic">, Group<f_Group>,
68866887
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
@@ -120,7 +120,8 @@ void Flang::addOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
120120
options::OPT_fintrinsic_modules_path, options::OPT_pedantic,
121121
options::OPT_std_EQ, options::OPT_W_Joined,
122122
options::OPT_fconvert_EQ, options::OPT_fpass_plugin_EQ,
123-
options::OPT_funderscoring, options::OPT_fno_underscoring});
123+
options::OPT_funderscoring, options::OPT_fno_underscoring,
124+
options::OPT_funsigned, options::OPT_fno_unsigned});
124125

125126
llvm::codegenoptions::DebugInfoKind DebugInfoKind;
126127
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
@@ -417,6 +417,7 @@ end
417417
[-fimplicit-none-type-never]
418418
* Old-style `PARAMETER pi=3.14` statement without parentheses
419419
[-falternative-parameter-statement]
420+
* `UNSIGNED` type (-funsigned)
420421

421422
### Extensions and legacy features deliberately not supported
422423

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-features.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
5353
NonBindCInteroperability, CudaManaged, CudaUnified,
5454
PolymorphicActualAllocatableOrPointerToMonomorphicDummy, RelaxedPureDummy,
5555
UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr,
56-
SavedLocalInSpecExpr, PrintNamelist, AssumedRankPassedToNonAssumedRank)
56+
SavedLocalInSpecExpr, PrintNamelist, AssumedRankPassedToNonAssumedRank,
57+
Unsigned)
5758

5859
// Portability and suspicious usage warnings
5960
ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,

flang/include/flang/Common/Fortran.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@
2121
namespace Fortran::common {
2222
class LanguageFeatureControl;
2323

24-
// Fortran has five kinds of intrinsic data types, plus the derived types.
25-
ENUM_CLASS(TypeCategory, Integer, Real, Complex, Character, Logical, Derived)
24+
// Fortran has five kinds of standard intrinsic data types, the Unsigned
25+
// extension, and derived types.
26+
ENUM_CLASS(
27+
TypeCategory, Integer, Unsigned, Real, Complex, Character, Logical, Derived)
2628
ENUM_CLASS(VectorElementCategory, Integer, Unsigned, Real)
2729

2830
constexpr bool IsNumericTypeCategory(TypeCategory category) {
29-
return category == TypeCategory::Integer || category == TypeCategory::Real ||
31+
return category == TypeCategory::Integer ||
32+
category == TypeCategory::Unsigned || category == TypeCategory::Real ||
3033
category == TypeCategory::Complex;
3134
}
3235

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

flang/include/flang/Evaluate/tools.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,8 @@ Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) {
582582

583583
template <typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
584584
static_assert(IsSpecificIntrinsicType<TO>);
585-
if constexpr (TO::category == TypeCategory::Integer) {
585+
if constexpr (TO::category == TypeCategory::Integer ||
586+
TO::category == TypeCategory::Unsigned) {
586587
return Expr<TO>{
587588
Constant<TO>{Scalar<TO>::ConvertUnsigned(std::move(x)).value}};
588589
} else {
@@ -754,11 +755,11 @@ Expr<SomeKind<CAT>> PromoteAndCombine(
754755
// one of the operands to the type of the other. Handles special cases with
755756
// typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
756757
// powers.
757-
template <template <typename> class OPR>
758+
template <template <typename> class OPR, bool CAN_BE_UNSIGNED = true>
758759
std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
759760
Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
760761

761-
extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
762+
extern template std::optional<Expr<SomeType>> NumericOperation<Power, false>(
762763
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
763764
int defaultRealKind);
764765
extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
@@ -910,6 +911,9 @@ common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> TypedWrapper(
910911
case TypeCategory::Integer:
911912
return WrapperHelper<TypeCategory::Integer, WRAPPER, WRAPPED>(
912913
dyType.kind(), std::move(x));
914+
case TypeCategory::Unsigned:
915+
return WrapperHelper<TypeCategory::Unsigned, WRAPPER, WRAPPED>(
916+
dyType.kind(), std::move(x));
913917
case TypeCategory::Real:
914918
return WrapperHelper<TypeCategory::Real, WRAPPER, WRAPPED>(
915919
dyType.kind(), std::move(x));

0 commit comments

Comments
 (0)