Skip to content

Implement _ExtInt as an extended int type specifier. #1067

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3257,3 +3257,48 @@ Since the size of ``buffer`` can't be known at compile time, Clang will fold
``__builtin_object_size(buffer, 0)`` into ``-1``. However, if this was written
as ``__builtin_dynamic_object_size(buffer, 0)``, Clang will fold it into
``size``, providing some extra runtime safety.

Extended Integer Types
======================

Clang supports a set of extended integer types under the syntax ``_ExtInt(N)``
where ``N`` is an integer that specifies the number of bits that are used to represent
the type, including the sign bit. The keyword ``_ExtInt`` is a type specifier, thus
it can be used in any place a type can, including as a non-type-template-parameter,
as the type of a bitfield, and as the underlying type of an enumeration.

An extended integer can be declared either signed, or unsigned by using the
``signed``/``unsigned`` keywords. If no sign specifier is used or if the ``signed``
keyword is used, the extended integer type is a signed integer and can represent
negative values.

The ``N`` expression is an integer constant expression, which specifies the number
of bits used to represent the type, following normal integer representations for
both signed and unsigned types. Both a signed and unsigned extended integer of the
same ``N`` value will have the same number of bits in its representation. Many
architectures don't have a way of representing non power-of-2 integers, so these
architectures emulate these types using larger integers. In these cases, they are
expected to follow the 'as-if' rule and do math 'as-if' they were done at the
specified number of bits.

In order to be consistent with the C language specification, and make the extended
integer types useful for their intended purpose, extended integers follow the C
standard integer conversion ranks. An extended integer type has a greater rank than
any integer type with less precision. However, they have lower precision than any
of the built in or other integer types (such as __int128). Usual arithmetic conversions
also work the same, where the smaller ranked integer is converted to the larger.

The one exception to the C rules for integers for these types is Integer Promotion.
Unary +, -, and ~ operators typically will promote operands to ``int``. Doing these
promotions would inflate the size of required hardware on some platforms, so extended
integer types aren't subject to the integer promotion rules in these cases.

In languages (such as OpenCL) that define shift by-out-of-range behavior as a mask,
non-power-of-two versions of these types use an unsigned remainder operation to constrain
the value to the proper range, preventing undefined behavior.

Extended integer types are aligned to the next greatest power-of-2 up to 64 bits.
The size of these types for the purposes of layout and ``sizeof`` are the number of
bits aligned to this calculated alignment. This permits the use of these types in
allocated arrays using common ``sizeof(Array)/sizeof(ElementType)`` pattern.

8 changes: 8 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ Non-comprehensive list of changes in this release
the found gcc installation is older than 4.7.0. Add ``-fno-use-init-array`` to
get the old behavior (``.ctors``).

* clang adds support for a set of extended integer types (``_ExtInt(N)``) that
permit non-power of 2 integers, exposing the LLVM integer types. Since a major
motivating use case for these types is to limit 'bit' useage, these types don't
automatically promote to 'int' when operations are done between two ``ExtInt(N)``
types, instead math occurs at the size of the largest ``ExtInt(N)`` type.



New Compiler Flags
------------------

Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::FoldingSet<AtomicType> AtomicTypes;
llvm::FoldingSet<AttributedType> AttributedTypes;
mutable llvm::FoldingSet<PipeType> PipeTypes;
mutable llvm::FoldingSet<ExtIntType> ExtIntTypes;
mutable llvm::FoldingSet<DependentExtIntType> DependentExtIntTypes;

mutable llvm::FoldingSet<QualifiedTemplateName> QualifiedTemplateNames;
mutable llvm::FoldingSet<DependentTemplateName> DependentTemplateNames;
Expand Down Expand Up @@ -1273,6 +1275,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// Return a write_only pipe type for the specified type.
QualType getWritePipeType(QualType T) const;

/// Return an extended integer type with the specified underlying type and bit
/// count.
QualType getExtIntType(bool Unsigned, unsigned NumBits) const;

/// Return a dependent extended integer type with the specified underlying
/// type and bit count.
QualType getDependentExtIntType(bool Unsigned, Expr *BitsExpr) const;

/// Gets the struct used to keep track of the extended descriptor for
/// pointer to blocks.
QualType getBlockDescriptorExtendedType() const;
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,10 @@ DEF_TRAVERSE_TYPE(AtomicType, { TRY_TO(TraverseType(T->getValueType())); })

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

DEF_TRAVERSE_TYPE(ExtIntType, {})
DEF_TRAVERSE_TYPE(DependentExtIntType,
{ TRY_TO(TraverseStmt(T->getNumBitsExpr())); })

#undef DEF_TRAVERSE_TYPE

// ----------------- TypeLoc traversal -----------------
Expand Down Expand Up @@ -1360,6 +1364,11 @@ DEF_TRAVERSE_TYPELOC(AtomicType, { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); })

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

DEF_TRAVERSE_TYPELOC(ExtIntType, {})
DEF_TRAVERSE_TYPELOC(DependentExtIntType, {
TRY_TO(TraverseStmt(TL.getTypePtr()->getNumBitsExpr()));
})

#undef DEF_TRAVERSE_TYPELOC

// ----------------- Decl traversal -----------------
Expand Down
69 changes: 66 additions & 3 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2117,6 +2117,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
bool isOCLExtOpaqueType() const; // Any OpenCL extension type

bool isPipeType() const; // OpenCL pipe type
bool isExtIntType() const; // Extended Int Type
bool isOpenCLSpecificType() const; // Any OpenCL specific type

/// Determines if this type, which must satisfy
Expand Down Expand Up @@ -6162,6 +6163,63 @@ class PipeType : public Type, public llvm::FoldingSetNode {
bool isReadOnly() const { return isRead; }
};

// A fixed int type of a specified bitwidth.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it's possible to define only DependentExtIntType class here. But it will come in cost of extra isInstantiationDependent() in the code.

class ExtIntType : public Type, public llvm::FoldingSetNode {
friend class ASTContext;
bool IsUnsigned;
unsigned NumBits;

protected:
ExtIntType(bool isUnsigned, unsigned NumBits);

public:
bool isUnsigned() const { return IsUnsigned; }
unsigned getNumBits() const { return NumBits; }

bool isSugared() const { return false; }
QualType desugar() const { return QualType(this, 0); }

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, isUnsigned(), getNumBits());
}

static void Profile(llvm::FoldingSetNodeID &ID, bool IsUnsigned,
unsigned NumBits) {
ID.AddBoolean(IsUnsigned);
ID.AddInteger(NumBits);
}

static bool classof(const Type *T) { return T->getTypeClass() == ExtInt; }
};

class DependentExtIntType : public Type, public llvm::FoldingSetNode {
friend class ASTContext;
const ASTContext &Context;
bool IsUnsigned;
Expr *NumBitsExpr;

protected:
DependentExtIntType(const ASTContext &Context, bool IsUnsigned,
Expr *NumBits);

public:
bool isUnsigned() const { return IsUnsigned; }
Expr *getNumBitsExpr() const { return NumBitsExpr; }

bool isSugared() const { return false; }
QualType desugar() const { return QualType(this, 0); }

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, Context, isUnsigned(), getNumBitsExpr());
}
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
bool IsUnsigned, Expr *NumBitsExpr);

static bool classof(const Type *T) {
return T->getTypeClass() == DependentExtInt;
}
};

/// A qualifier set is used to build a set of qualifiers.
class QualifierCollector : public Qualifiers {
public:
Expand Down Expand Up @@ -6681,6 +6739,10 @@ inline bool Type::isPipeType() const {
return isa<PipeType>(CanonicalType);
}

inline bool Type::isExtIntType() const {
return isa<ExtIntType>(CanonicalType);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return isa<ExtIntType>(CanonicalType);
return isa<ExtIntType>(CanonicalType) || isa<DependentExtIntType>(CanonicalType);

}

#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \
inline bool Type::is##Id##Type() const { \
return isSpecificBuiltinType(BuiltinType::Id); \
Expand Down Expand Up @@ -6788,7 +6850,7 @@ inline bool Type::isIntegerType() const {
return IsEnumDeclComplete(ET->getDecl()) &&
!IsEnumDeclScoped(ET->getDecl());
}
return false;
return isExtIntType();
}

inline bool Type::isFixedPointType() const {
Expand Down Expand Up @@ -6845,7 +6907,8 @@ inline bool Type::isScalarType() const {
isa<BlockPointerType>(CanonicalType) ||
isa<MemberPointerType>(CanonicalType) ||
isa<ComplexType>(CanonicalType) ||
isa<ObjCObjectPointerType>(CanonicalType);
isa<ObjCObjectPointerType>(CanonicalType) ||
isExtIntType();
}

inline bool Type::isIntegralOrEnumerationType() const {
Expand All @@ -6858,7 +6921,7 @@ inline bool Type::isIntegralOrEnumerationType() const {
if (const auto *ET = dyn_cast<EnumType>(CanonicalType))
return IsEnumDeclComplete(ET->getDecl());

return false;
return isExtIntType();
}

inline bool Type::isBooleanType() const {
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2314,6 +2314,12 @@ inline T TypeLoc::getAsAdjusted() const {
}
return Cur.getAs<T>();
}
class ExtIntTypeLoc
: public InheritingConcreteTypeLoc<TypeSpecTypeLoc, ExtIntTypeLoc,
ExtIntType> {};
class DependentExtIntTypeLoc
: public InheritingConcreteTypeLoc<TypeSpecTypeLoc, DependentExtIntTypeLoc,
DependentExtIntType> {};

} // namespace clang

Expand Down
25 changes: 25 additions & 0 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,28 @@ let Class = PipeType in {
return ctx.getPipeType(elementType, isReadOnly);
}]>;
}

let Class = ExtIntType in {
def : Property<"isUnsigned", Bool> {
let Read = [{ node->isUnsigned() }];
}
def : Property <"numBits", UInt32> {
let Read = [{ node->getNumBits() }];
}

def : Creator<[{
return ctx.getExtIntType(isUnsigned, numBits);
}]>;
}

let Class = DependentExtIntType in {
def : Property<"isUnsigned", Bool> {
let Read = [{ node->isUnsigned() }];
}
def : Property <"numBitsExpr", ExprRef> {
let Read = [{ node->getNumBitsExpr() }];
}
def : Creator<[{
return ctx.getDependentExtIntType(isUnsigned, numBitsExpr);
}]>;
}
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10383,4 +10383,8 @@ def warn_sycl_kernel_return_type : Warning<
"function template with 'sycl_kernel' attribute must have a 'void' return type">,
InGroup<IgnoredAttributes>;

def err_ext_int_bad_size : Error<"%select{signed|unsigned}0 _ExtInt must "
"have a size of at least %select{2|1}0">;
def err_ext_int_max_size : Error<"%select{signed|unsigned}0 _ExtInt of sizes "
"greater than %1 not supported">;
} // end of sema component.
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Specifiers.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ namespace clang {
TST_char32, // C++11 char32_t
TST_int,
TST_int128,
TST_extint, // Extended Int types.
TST_half, // OpenCL half, ARM NEON __fp16
TST_Float16, // C11 extension ISO/IEC TS 18661-3
TST_Accum, // ISO/IEC JTC1 SC22 WG14 N1169 Extension
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ KEYWORD(goto , KEYALL)
KEYWORD(if , KEYALL)
KEYWORD(inline , KEYC99|KEYCXX|KEYGNU)
KEYWORD(int , KEYALL)
KEYWORD(_ExtInt , KEYALL)
KEYWORD(long , KEYALL)
KEYWORD(register , KEYALL)
KEYWORD(restrict , KEYC99)
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/TypeNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,5 @@ def ObjCInterfaceType : TypeNode<ObjCObjectType>, LeafType;
def ObjCObjectPointerType : TypeNode<Type>;
def PipeType : TypeNode<Type>;
def AtomicType : TypeNode<Type>;
def ExtIntType : TypeNode<Type>;
def DependentExtIntType : TypeNode<Type>, AlwaysDependent;
1 change: 1 addition & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -2680,6 +2680,7 @@ class Parser : public CodeCompletionHandler {
SourceLocation &EllipsisLoc);
void ParseAlignmentSpecifier(ParsedAttributes &Attrs,
SourceLocation *endLoc = nullptr);
ExprResult ParseExtIntegerArgument();

VirtSpecifiers::Specifier isCXX11VirtSpecifier(const Token &Tok) const;
VirtSpecifiers::Specifier isCXX11VirtSpecifier() const {
Expand Down
6 changes: 5 additions & 1 deletion clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ class DeclSpec {
static const TST TST_char32 = clang::TST_char32;
static const TST TST_int = clang::TST_int;
static const TST TST_int128 = clang::TST_int128;
static const TST TST_extint = clang::TST_extint;
static const TST TST_half = clang::TST_half;
static const TST TST_float = clang::TST_float;
static const TST TST_double = clang::TST_double;
Expand Down Expand Up @@ -411,7 +412,7 @@ class DeclSpec {
T == TST_underlyingType || T == TST_atomic);
}
static bool isExprRep(TST T) {
return (T == TST_typeofExpr || T == TST_decltype);
return (T == TST_typeofExpr || T == TST_decltype || T == TST_extint);
}

DeclSpec(const DeclSpec &) = delete;
Expand Down Expand Up @@ -682,6 +683,9 @@ class DeclSpec {
bool SetTypePipe(bool isPipe, SourceLocation Loc,
const char *&PrevSpec, unsigned &DiagID,
const PrintingPolicy &Policy);
bool SetExtIntType(SourceLocation KWLoc, Expr *BitWidth,
const char *&PrevSpec, unsigned &DiagID,
const PrintingPolicy &Policy);
bool SetTypeSpecSat(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
bool SetTypeSpecError();
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,7 @@ class Sema final {
SourceLocation Loc);
QualType BuildWritePipeType(QualType T,
SourceLocation Loc);
QualType BuildExtIntType(bool IsUnsigned, Expr *BitWidth, SourceLocation Loc);

TypeSourceInfo *GetTypeForDeclarator(Declarator &D, Scope *S);
TypeSourceInfo *GetTypeForDeclaratorCast(Declarator &D, QualType FromTy);
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Serialization/TypeBitCodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,7 @@ TYPE_BIT_CODE(DependentSizedExtVector, DEPENDENT_SIZED_EXT_VECTOR, 46)
TYPE_BIT_CODE(DependentAddressSpace, DEPENDENT_ADDRESS_SPACE, 47)
TYPE_BIT_CODE(DependentVector, DEPENDENT_SIZED_VECTOR, 48)
TYPE_BIT_CODE(MacroQualified, MACRO_QUALIFIED, 49)
TYPE_BIT_CODE(ExtInt, EXT_INT, 50)
TYPE_BIT_CODE(DependentExtInt, DEPENDENT_EXT_INT, 51)

#undef TYPE_BIT_CODE
Loading