Skip to content

Commit 61ba148

Browse files
author
Erich Keane
committed
Implement _ExtInt as an extended int type specifier.
Introduction/Motivation: LLVM-IR supports integers of non-power-of-2 bitwidth, in the iN syntax. Integers of non-power-of-two aren't particularly interesting or useful on most hardware, so much so that no language in Clang has been motivated to expose it before. However, in the case of FPGA hardware normal integer types where the full bitwidth isn't used, is extremely wasteful and has severe performance/space concerns. Because of this, Intel has introduced this functionality in the High Level Synthesis compiler[0] under the name "Arbitrary Precision Integer" (ap_int for short). This has been extremely useful and effective for our users, permitting them to optimize their storage and operation space on an architecture where both can be extremely expensive. We are proposing upstreaming a more palatable version of this to the community, in the form of this proposal and accompanying patch. We are proposing the syntax _ExtInt(N). We intend to propose this to the WG14 committee[1], and the underscore-capital seems like the active direction for a WG14 paper's acceptance. An alternative that Richard Smith suggested on the initial review was __int(N), however we believe that is much less acceptable by WG14. We considered _Int, however _Int is used as an identifier in libstdc++ and there is no good way to fall back to an identifier (since _Int(5) is indistinguishable from an unnamed initializer of a template type named _Int). [0]https://www.intel.com/content/www/us/en/software/programmable/quartus-prime/hls-compiler.html) [1]http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2472.pdf Differential Revision: https://reviews.llvm.org/D73967
1 parent e1c6727 commit 61ba148

Some content is hidden

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

55 files changed

+1872
-36
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3461,3 +3461,56 @@ Since the size of ``buffer`` can't be known at compile time, Clang will fold
34613461
``__builtin_object_size(buffer, 0)`` into ``-1``. However, if this was written
34623462
as ``__builtin_dynamic_object_size(buffer, 0)``, Clang will fold it into
34633463
``size``, providing some extra runtime safety.
3464+
3465+
Extended Integer Types
3466+
======================
3467+
3468+
Clang supports a set of extended integer types under the syntax ``_ExtInt(N)``
3469+
where ``N`` is an integer that specifies the number of bits that are used to represent
3470+
the type, including the sign bit. The keyword ``_ExtInt`` is a type specifier, thus
3471+
it can be used in any place a type can, including as a non-type-template-parameter,
3472+
as the type of a bitfield, and as the underlying type of an enumeration.
3473+
3474+
An extended integer can be declared either signed, or unsigned by using the
3475+
``signed``/``unsigned`` keywords. If no sign specifier is used or if the ``signed``
3476+
keyword is used, the extended integer type is a signed integer and can represent
3477+
negative values.
3478+
3479+
The ``N`` expression is an integer constant expression, which specifies the number
3480+
of bits used to represent the type, following normal integer representations for
3481+
both signed and unsigned types. Both a signed and unsigned extended integer of the
3482+
same ``N`` value will have the same number of bits in its representation. Many
3483+
architectures don't have a way of representing non power-of-2 integers, so these
3484+
architectures emulate these types using larger integers. In these cases, they are
3485+
expected to follow the 'as-if' rule and do math 'as-if' they were done at the
3486+
specified number of bits.
3487+
3488+
In order to be consistent with the C language specification, and make the extended
3489+
integer types useful for their intended purpose, extended integers follow the C
3490+
standard integer conversion ranks. An extended integer type has a greater rank than
3491+
any integer type with less precision. However, they have lower rank than any
3492+
of the built in or other integer types (such as __int128). Usual arithmetic conversions
3493+
also work the same, where the smaller ranked integer is converted to the larger.
3494+
3495+
The one exception to the C rules for integers for these types is Integer Promotion.
3496+
Unary +, -, and ~ operators typically will promote operands to ``int``. Doing these
3497+
promotions would inflate the size of required hardware on some platforms, so extended
3498+
integer types aren't subject to the integer promotion rules in these cases.
3499+
3500+
In languages (such as OpenCL) that define shift by-out-of-range behavior as a mask,
3501+
non-power-of-two versions of these types use an unsigned remainder operation to constrain
3502+
the value to the proper range, preventing undefined behavior.
3503+
3504+
Extended integer types are aligned to the next greatest power-of-2 up to 64 bits.
3505+
The size of these types for the purposes of layout and ``sizeof`` are the number of
3506+
bits aligned to this calculated alignment. This permits the use of these types in
3507+
allocated arrays using common ``sizeof(Array)/sizeof(ElementType)`` pattern.
3508+
3509+
Extended integer types work with the C _Atomic type modifier, however only precisions
3510+
that are powers-of-2 greater than 8 bit are accepted.
3511+
3512+
Extended integer types align with existing calling conventions. They have the same size
3513+
and alignment as the smallest basic type that can contain them. Types that are larger
3514+
than 64 bits are handled in the same way as _int128 is handled; they are conceptually
3515+
treated as struct of register size chunks. They number of chunks are the smallest
3516+
number that can contain the types which does not necessarily mean a power-of-2 size.

clang/docs/ReleaseNotes.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ Non-comprehensive list of changes in this release
6262
in the Arm C Language Extensions.
6363

6464

65+
* clang adds support for a set of extended integer types (``_ExtInt(N)``) that
66+
permit non-power of 2 integers, exposing the LLVM integer types. Since a major
67+
motivating use case for these types is to limit 'bit' usage, these types don't
68+
automatically promote to 'int' when operations are done between two ``ExtInt(N)``
69+
types, instead math occurs at the size of the largest ``ExtInt(N)`` type.
70+
71+
72+
6573
New Compiler Flags
6674
------------------
6775

clang/include/clang/AST/ASTContext.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
224224
mutable llvm::FoldingSet<AtomicType> AtomicTypes;
225225
llvm::FoldingSet<AttributedType> AttributedTypes;
226226
mutable llvm::FoldingSet<PipeType> PipeTypes;
227+
mutable llvm::FoldingSet<ExtIntType> ExtIntTypes;
228+
mutable llvm::FoldingSet<DependentExtIntType> DependentExtIntTypes;
227229

228230
mutable llvm::FoldingSet<QualifiedTemplateName> QualifiedTemplateNames;
229231
mutable llvm::FoldingSet<DependentTemplateName> DependentTemplateNames;
@@ -1203,6 +1205,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
12031205
/// Return a write_only pipe type for the specified type.
12041206
QualType getWritePipeType(QualType T) const;
12051207

1208+
/// Return an extended integer type with the specified signedness and bit
1209+
/// count.
1210+
QualType getExtIntType(bool Unsigned, unsigned NumBits) const;
1211+
1212+
/// Return a dependent extended integer type with the specified signedness and
1213+
/// bit count.
1214+
QualType getDependentExtIntType(bool Unsigned, Expr *BitsExpr) const;
1215+
12061216
/// Gets the struct used to keep track of the extended descriptor for
12071217
/// pointer to blocks.
12081218
QualType getBlockDescriptorExtendedType() const;

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,10 @@ DEF_TRAVERSE_TYPE(AtomicType, { TRY_TO(TraverseType(T->getValueType())); })
11151115

11161116
DEF_TRAVERSE_TYPE(PipeType, { TRY_TO(TraverseType(T->getElementType())); })
11171117

1118+
DEF_TRAVERSE_TYPE(ExtIntType, {})
1119+
DEF_TRAVERSE_TYPE(DependentExtIntType,
1120+
{ TRY_TO(TraverseStmt(T->getNumBitsExpr())); })
1121+
11181122
#undef DEF_TRAVERSE_TYPE
11191123

11201124
// ----------------- TypeLoc traversal -----------------
@@ -1385,6 +1389,11 @@ DEF_TRAVERSE_TYPELOC(AtomicType, { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); })
13851389

13861390
DEF_TRAVERSE_TYPELOC(PipeType, { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); })
13871391

1392+
DEF_TRAVERSE_TYPELOC(ExtIntType, {})
1393+
DEF_TRAVERSE_TYPELOC(DependentExtIntType, {
1394+
TRY_TO(TraverseStmt(TL.getTypePtr()->getNumBitsExpr()));
1395+
})
1396+
13881397
#undef DEF_TRAVERSE_TYPELOC
13891398

13901399
// ----------------- Decl traversal -----------------

clang/include/clang/AST/Type.h

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2101,6 +2101,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
21012101
bool isOCLExtOpaqueType() const; // Any OpenCL extension type
21022102

21032103
bool isPipeType() const; // OpenCL pipe type
2104+
bool isExtIntType() const; // Extended Int Type
21042105
bool isOpenCLSpecificType() const; // Any OpenCL specific type
21052106

21062107
/// Determines if this type, which must satisfy
@@ -6127,6 +6128,64 @@ class PipeType : public Type, public llvm::FoldingSetNode {
61276128
bool isReadOnly() const { return isRead; }
61286129
};
61296130

6131+
/// A fixed int type of a specified bitwidth.
6132+
class ExtIntType final : public Type, public llvm::FoldingSetNode {
6133+
friend class ASTContext;
6134+
unsigned IsUnsigned : 1;
6135+
unsigned NumBits : 24;
6136+
6137+
protected:
6138+
ExtIntType(bool isUnsigned, unsigned NumBits);
6139+
6140+
public:
6141+
bool isUnsigned() const { return IsUnsigned; }
6142+
bool isSigned() const { return !IsUnsigned; }
6143+
unsigned getNumBits() const { return NumBits; }
6144+
6145+
bool isSugared() const { return false; }
6146+
QualType desugar() const { return QualType(this, 0); }
6147+
6148+
void Profile(llvm::FoldingSetNodeID &ID) {
6149+
Profile(ID, isUnsigned(), getNumBits());
6150+
}
6151+
6152+
static void Profile(llvm::FoldingSetNodeID &ID, bool IsUnsigned,
6153+
unsigned NumBits) {
6154+
ID.AddBoolean(IsUnsigned);
6155+
ID.AddInteger(NumBits);
6156+
}
6157+
6158+
static bool classof(const Type *T) { return T->getTypeClass() == ExtInt; }
6159+
};
6160+
6161+
class DependentExtIntType final : public Type, public llvm::FoldingSetNode {
6162+
friend class ASTContext;
6163+
const ASTContext &Context;
6164+
llvm::PointerIntPair<Expr*, 1, bool> ExprAndUnsigned;
6165+
6166+
protected:
6167+
DependentExtIntType(const ASTContext &Context, bool IsUnsigned,
6168+
Expr *NumBits);
6169+
6170+
public:
6171+
bool isUnsigned() const;
6172+
bool isSigned() const { return !isUnsigned(); }
6173+
Expr *getNumBitsExpr() const;
6174+
6175+
bool isSugared() const { return false; }
6176+
QualType desugar() const { return QualType(this, 0); }
6177+
6178+
void Profile(llvm::FoldingSetNodeID &ID) {
6179+
Profile(ID, Context, isUnsigned(), getNumBitsExpr());
6180+
}
6181+
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
6182+
bool IsUnsigned, Expr *NumBitsExpr);
6183+
6184+
static bool classof(const Type *T) {
6185+
return T->getTypeClass() == DependentExtInt;
6186+
}
6187+
};
6188+
61306189
/// A qualifier set is used to build a set of qualifiers.
61316190
class QualifierCollector : public Qualifiers {
61326191
public:
@@ -6646,6 +6705,10 @@ inline bool Type::isPipeType() const {
66466705
return isa<PipeType>(CanonicalType);
66476706
}
66486707

6708+
inline bool Type::isExtIntType() const {
6709+
return isa<ExtIntType>(CanonicalType);
6710+
}
6711+
66496712
#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \
66506713
inline bool Type::is##Id##Type() const { \
66516714
return isSpecificBuiltinType(BuiltinType::Id); \
@@ -6741,7 +6804,7 @@ inline bool Type::isIntegerType() const {
67416804
return IsEnumDeclComplete(ET->getDecl()) &&
67426805
!IsEnumDeclScoped(ET->getDecl());
67436806
}
6744-
return false;
6807+
return isExtIntType();
67456808
}
67466809

67476810
inline bool Type::isFixedPointType() const {
@@ -6798,7 +6861,8 @@ inline bool Type::isScalarType() const {
67986861
isa<BlockPointerType>(CanonicalType) ||
67996862
isa<MemberPointerType>(CanonicalType) ||
68006863
isa<ComplexType>(CanonicalType) ||
6801-
isa<ObjCObjectPointerType>(CanonicalType);
6864+
isa<ObjCObjectPointerType>(CanonicalType) ||
6865+
isExtIntType();
68026866
}
68036867

68046868
inline bool Type::isIntegralOrEnumerationType() const {
@@ -6811,7 +6875,7 @@ inline bool Type::isIntegralOrEnumerationType() const {
68116875
if (const auto *ET = dyn_cast<EnumType>(CanonicalType))
68126876
return IsEnumDeclComplete(ET->getDecl());
68136877

6814-
return false;
6878+
return isExtIntType();
68156879
}
68166880

68176881
inline bool Type::isBooleanType() const {

clang/include/clang/AST/TypeLoc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2450,6 +2450,12 @@ inline T TypeLoc::getAsAdjusted() const {
24502450
}
24512451
return Cur.getAs<T>();
24522452
}
2453+
class ExtIntTypeLoc final
2454+
: public InheritingConcreteTypeLoc<TypeSpecTypeLoc, ExtIntTypeLoc,
2455+
ExtIntType> {};
2456+
class DependentExtIntTypeLoc final
2457+
: public InheritingConcreteTypeLoc<TypeSpecTypeLoc, DependentExtIntTypeLoc,
2458+
DependentExtIntType> {};
24532459

24542460
} // namespace clang
24552461

clang/include/clang/AST/TypeProperties.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,3 +833,28 @@ let Class = PipeType in {
833833
return ctx.getPipeType(elementType, isReadOnly);
834834
}]>;
835835
}
836+
837+
let Class = ExtIntType in {
838+
def : Property<"isUnsigned", Bool> {
839+
let Read = [{ node->isUnsigned() }];
840+
}
841+
def : Property <"numBits", UInt32> {
842+
let Read = [{ node->getNumBits() }];
843+
}
844+
845+
def : Creator<[{
846+
return ctx.getExtIntType(isUnsigned, numBits);
847+
}]>;
848+
}
849+
850+
let Class = DependentExtIntType in {
851+
def : Property<"isUnsigned", Bool> {
852+
let Read = [{ node->isUnsigned() }];
853+
}
854+
def : Property <"numBitsExpr", ExprRef> {
855+
let Read = [{ node->getNumBitsExpr() }];
856+
}
857+
def : Creator<[{
858+
return ctx.getDependentExtIntType(isUnsigned, numBitsExpr);
859+
}]>;
860+
}

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5947,10 +5947,12 @@ def err_block_return_missing_expr : Error<
59475947
"non-void block should return a value">;
59485948
def err_func_def_incomplete_result : Error<
59495949
"incomplete result type %0 in function definition">;
5950-
def err_atomic_specifier_bad_type : Error<
5951-
"_Atomic cannot be applied to "
5952-
"%select{incomplete |array |function |reference |atomic |qualified |sizeless |}0type "
5953-
"%1 %select{|||||||which is not trivially copyable}0">;
5950+
def err_atomic_specifier_bad_type
5951+
: Error<"_Atomic cannot be applied to "
5952+
"%select{incomplete |array |function |reference |atomic |qualified "
5953+
"|sizeless ||integer |integer }0type "
5954+
"%1 %select{|||||||which is not trivially copyable|with less than "
5955+
"1 byte of precision|with a non power of 2 precision}0">;
59545956

59555957
// Expressions.
59565958
def select_unary_expr_or_type_trait_kind : TextSubstitution<
@@ -10711,4 +10713,8 @@ def warn_sycl_kernel_return_type : Warning<
1071110713
"function template with 'sycl_kernel' attribute must have a 'void' return type">,
1071210714
InGroup<IgnoredAttributes>;
1071310715

10716+
def err_ext_int_bad_size : Error<"%select{signed|unsigned}0 _ExtInt must "
10717+
"have a bit size of at least %select{2|1}0">;
10718+
def err_ext_int_max_size : Error<"%select{signed|unsigned}0 _ExtInt of bit "
10719+
"sizes greater than %1 not supported">;
1071410720
} // end of sema component.

clang/include/clang/Basic/Specifiers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ namespace clang {
6767
TST_char32, // C++11 char32_t
6868
TST_int,
6969
TST_int128,
70+
TST_extint, // Extended Int types.
7071
TST_half, // OpenCL half, ARM NEON __fp16
7172
TST_Float16, // C11 extension ISO/IEC TS 18661-3
7273
TST_Accum, // ISO/IEC JTC1 SC22 WG14 N1169 Extension

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ KEYWORD(goto , KEYALL)
285285
KEYWORD(if , KEYALL)
286286
KEYWORD(inline , KEYC99|KEYCXX|KEYGNU)
287287
KEYWORD(int , KEYALL)
288+
KEYWORD(_ExtInt , KEYALL)
288289
KEYWORD(long , KEYALL)
289290
KEYWORD(register , KEYALL)
290291
KEYWORD(restrict , KEYC99)

0 commit comments

Comments
 (0)