Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -3311,3 +3311,48 @@ ``__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. + Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -57,6 +57,14 @@ ------------------------------------------------- +* 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 ------------------ Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -223,6 +223,8 @@ mutable llvm::FoldingSet AtomicTypes; llvm::FoldingSet AttributedTypes; mutable llvm::FoldingSet PipeTypes; + mutable llvm::FoldingSet ExtIntTypes; + mutable llvm::FoldingSet DependentExtIntTypes; mutable llvm::FoldingSet QualifiedTemplateNames; mutable llvm::FoldingSet DependentTemplateNames; @@ -1210,6 +1212,14 @@ /// 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; Index: clang/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- clang/include/clang/AST/RecursiveASTVisitor.h +++ clang/include/clang/AST/RecursiveASTVisitor.h @@ -1115,6 +1115,10 @@ 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 ----------------- @@ -1378,6 +1382,11 @@ 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 ----------------- Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -2119,6 +2119,7 @@ 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 @@ -6192,6 +6193,63 @@ bool isReadOnly() const { return isRead; } }; +// A fixed int type of a specified bitwidth. +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: @@ -6711,6 +6769,10 @@ return isa(CanonicalType); } +inline bool Type::isExtIntType() const { + return isa(CanonicalType); +} + #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ inline bool Type::is##Id##Type() const { \ return isSpecificBuiltinType(BuiltinType::Id); \ @@ -6818,7 +6880,7 @@ return IsEnumDeclComplete(ET->getDecl()) && !IsEnumDeclScoped(ET->getDecl()); } - return false; + return isExtIntType(); } inline bool Type::isFixedPointType() const { @@ -6875,7 +6937,8 @@ isa(CanonicalType) || isa(CanonicalType) || isa(CanonicalType) || - isa(CanonicalType); + isa(CanonicalType) || + isExtIntType(); } inline bool Type::isIntegralOrEnumerationType() const { @@ -6888,7 +6951,7 @@ if (const auto *ET = dyn_cast(CanonicalType)) return IsEnumDeclComplete(ET->getDecl()); - return false; + return isExtIntType(); } inline bool Type::isBooleanType() const { Index: clang/include/clang/AST/TypeLoc.h =================================================================== --- clang/include/clang/AST/TypeLoc.h +++ clang/include/clang/AST/TypeLoc.h @@ -2450,6 +2450,12 @@ } return Cur.getAs(); } +class ExtIntTypeLoc + : public InheritingConcreteTypeLoc {}; +class DependentExtIntTypeLoc + : public InheritingConcreteTypeLoc {}; } // namespace clang Index: clang/include/clang/AST/TypeProperties.td =================================================================== --- clang/include/clang/AST/TypeProperties.td +++ clang/include/clang/AST/TypeProperties.td @@ -822,3 +822,28 @@ 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); + }]>; +} Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10374,4 +10374,8 @@ "function template with 'sycl_kernel' attribute must have a 'void' return type">, InGroup; +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. Index: clang/include/clang/Basic/Specifiers.h =================================================================== --- clang/include/clang/Basic/Specifiers.h +++ clang/include/clang/Basic/Specifiers.h @@ -67,6 +67,7 @@ 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 Index: clang/include/clang/Basic/TokenKinds.def =================================================================== --- clang/include/clang/Basic/TokenKinds.def +++ clang/include/clang/Basic/TokenKinds.def @@ -285,6 +285,7 @@ KEYWORD(if , KEYALL) KEYWORD(inline , KEYC99|KEYCXX|KEYGNU) KEYWORD(int , KEYALL) +KEYWORD(_ExtInt , KEYALL) KEYWORD(long , KEYALL) KEYWORD(register , KEYALL) KEYWORD(restrict , KEYC99) Index: clang/include/clang/Basic/TypeNodes.td =================================================================== --- clang/include/clang/Basic/TypeNodes.td +++ clang/include/clang/Basic/TypeNodes.td @@ -104,3 +104,5 @@ def ObjCObjectPointerType : TypeNode; def PipeType : TypeNode; def AtomicType : TypeNode; +def ExtIntType : TypeNode; +def DependentExtIntType : TypeNode, AlwaysDependent; Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -2690,6 +2690,7 @@ SourceLocation &EllipsisLoc); void ParseAlignmentSpecifier(ParsedAttributes &Attrs, SourceLocation *endLoc = nullptr); + ExprResult ParseExtIntegerArgument(); VirtSpecifiers::Specifier isCXX11VirtSpecifier(const Token &Tok) const; VirtSpecifiers::Specifier isCXX11VirtSpecifier() const { Index: clang/include/clang/Sema/DeclSpec.h =================================================================== --- clang/include/clang/Sema/DeclSpec.h +++ clang/include/clang/Sema/DeclSpec.h @@ -278,6 +278,7 @@ 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; @@ -413,7 +414,7 @@ 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); } static bool isTemplateIdRep(TST T) { return (T == TST_auto || T == TST_decltype_auto); @@ -697,6 +698,9 @@ 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(); Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -1625,6 +1625,7 @@ 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); Index: clang/include/clang/Serialization/TypeBitCodes.def =================================================================== --- clang/include/clang/Serialization/TypeBitCodes.def +++ clang/include/clang/Serialization/TypeBitCodes.def @@ -58,5 +58,7 @@ 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 Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -2130,6 +2130,15 @@ Align = toBits(Layout.getAlignment()); break; } + case Type::ExtInt: { + const auto *EIT = cast(T); + Align = + std::min(static_cast(std::max( + getCharWidth(), llvm::PowerOf2Ceil(EIT->getNumBits()))), + Target->getLongLongAlign()); + Width = llvm::alignTo(EIT->getNumBits(), Align); + break; + } case Type::Record: case Type::Enum: { const auto *TT = cast(T); @@ -3326,6 +3335,8 @@ case Type::Auto: case Type::DeducedTemplateSpecialization: case Type::PackExpansion: + case Type::ExtInt: + case Type::DependentExtInt: llvm_unreachable("type should never be variably-modified"); // These types can be variably-modified but should never need to @@ -3998,6 +4009,40 @@ return getPipeType(T, false); } +QualType ASTContext::getExtIntType(bool IsUnsigned, unsigned NumBits) const { + llvm::FoldingSetNodeID ID; + ExtIntType::Profile(ID, IsUnsigned, NumBits); + + void *InsertPos = nullptr; + if (ExtIntType *EIT = ExtIntTypes.FindNodeOrInsertPos(ID, InsertPos)) + return QualType(EIT, 0); + + ExtIntType *New = + new (*this, TypeAlignment) ExtIntType(IsUnsigned, NumBits); + ExtIntTypes.InsertNode(New, InsertPos); + Types.push_back(New); + return QualType(New, 0); +} + +QualType ASTContext::getDependentExtIntType(bool IsUnsigned, + Expr *NumBitsExpr) const { + assert(NumBitsExpr->isInstantiationDependent() && "Only good for dependent"); + llvm::FoldingSetNodeID ID; + DependentExtIntType::Profile(ID, *this, IsUnsigned, NumBitsExpr); + + void *InsertPos = nullptr; + if (DependentExtIntType *Existing = + DependentExtIntTypes.FindNodeOrInsertPos(ID, InsertPos)) + return QualType(Existing, 0); + + DependentExtIntType *New = new (*this, TypeAlignment) + DependentExtIntType(*this, IsUnsigned, NumBitsExpr); + DependentExtIntTypes.InsertNode(New, InsertPos); + + Types.push_back(New); + return QualType(New, 0); +} + #ifndef NDEBUG static bool NeedsInjectedClassNameType(const RecordDecl *D) { if (!isa(D)) return false; @@ -5829,6 +5874,12 @@ unsigned ASTContext::getIntegerRank(const Type *T) const { assert(T->isCanonicalUnqualified() && "T should be canonicalized"); + // Results in this 'losing' to any type of the same size, but winning if + // larger. + if (const auto *EIT = dyn_cast(T)) { + return 0 + (EIT->getNumBits() << 3); + } + switch (cast(T)->getKind()) { default: llvm_unreachable("getIntegerRank(): not a built-in integer"); case BuiltinType::Bool: @@ -7194,6 +7245,7 @@ return; case Type::Pipe: + case Type::ExtInt: #define ABSTRACT_TYPE(KIND, BASE) #define TYPE(KIND, BASE) #define DEPENDENT_TYPE(KIND, BASE) \ @@ -9233,6 +9285,21 @@ assert(LHS != RHS && "Equivalent pipe types should have already been handled!"); return {}; + case Type::ExtInt: { + // Merge two pointer types, while trying to preserve typedef info + bool LHSUnsigned = LHS->getAs()->isUnsigned(); + bool RHSUnsigned = RHS->getAs()->isUnsigned(); + unsigned LHSBits = LHS->getAs()->getNumBits(); + unsigned RHSBits = RHS->getAs()->getNumBits(); + + // Like unsigned/int, shouldn't have a type if they dont match. + if (LHSUnsigned != RHSUnsigned) + return {}; + + if (LHSBits >= RHSBits) + return LHS; + return RHS; + } } llvm_unreachable("Invalid Type::Class!"); @@ -9373,6 +9440,8 @@ T = ET->getDecl()->getIntegerType(); if (T->isBooleanType()) return 1; + if(const auto *EIT = T->getAs()) + return EIT->getNumBits(); // For builtin types, just use the standard type sizing method return (unsigned)getTypeSize(T); } Index: clang/lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- clang/lib/AST/ASTStructuralEquivalence.cpp +++ clang/lib/AST/ASTStructuralEquivalence.cpp @@ -955,6 +955,24 @@ cast(T2)->getElementType())) return false; break; + case Type::ExtInt: { + const auto *Int1 = cast(T1); + const auto *Int2 = cast(T2); + + if (Int1->isUnsigned() != Int2->isUnsigned() || + Int1->getNumBits() != Int2->getNumBits()) + return false; + break; + } + case Type::DependentExtInt: { + const auto *Int1 = cast(T1); + const auto *Int2 = cast(T2); + + if (Int1->isUnsigned() != Int2->isUnsigned() || + !IsStructurallyEquivalent(Context, Int1->getNumBitsExpr(), + Int2->getNumBitsExpr())) + return false; + } } // end switch return true; Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -10204,6 +10204,7 @@ case Type::ObjCInterface: case Type::ObjCObjectPointer: case Type::Pipe: + case Type::ExtInt: // GCC classifies vectors as None. We follow its lead and classify all // other types that don't fit into the regular classification the same way. return GCCTypeClass::None; Index: clang/lib/AST/ItaniumMangle.cpp =================================================================== --- clang/lib/AST/ItaniumMangle.cpp +++ clang/lib/AST/ItaniumMangle.cpp @@ -2002,6 +2002,8 @@ case Type::Atomic: case Type::Pipe: case Type::MacroQualified: + case Type::ExtInt: + case Type::DependentExtInt: llvm_unreachable("type is illegal as a nested name specifier"); case Type::SubstTemplateTypeParmPack: @@ -3460,6 +3462,28 @@ Out << "8ocl_pipe"; } +void CXXNameMangler::mangleType(const ExtIntType *T) { + Out << "U7_ExtInt"; + llvm::APSInt BW(32, true); + BW = T->getNumBits(); + TemplateArgument TA(Context.getASTContext(), BW, getASTContext().IntTy); + mangleTemplateArgs(&TA, 1); + if (T->isUnsigned()) + Out << "j"; + else + Out << "i"; +} + +void CXXNameMangler::mangleType(const DependentExtIntType *T) { + Out << "U7_ExtInt"; + TemplateArgument TA(T->getNumBitsExpr()); + mangleTemplateArgs(&TA, 1); + if (T->isUnsigned()) + Out << "j"; + else + Out << "i"; +} + void CXXNameMangler::mangleIntegerLiteral(QualType T, const llvm::APSInt &Value) { // ::= L E # integer literal Index: clang/lib/AST/MicrosoftMangle.cpp =================================================================== --- clang/lib/AST/MicrosoftMangle.cpp +++ clang/lib/AST/MicrosoftMangle.cpp @@ -2965,6 +2965,30 @@ return Mangler.mangle(D); } +void MicrosoftCXXNameMangler::mangleType(const ExtIntType *T, Qualifiers, + SourceRange Range) { + llvm::SmallString<64> TemplateMangling; + llvm::raw_svector_ostream Stream(TemplateMangling); + MicrosoftCXXNameMangler Extra(Context, Stream); + Stream << "?$"; + if (T->isUnsigned()) + Extra.mangleSourceName("_UExtInt"); + else + Extra.mangleSourceName("_ExtInt"); + Extra.mangleIntegerLiteral(llvm::APSInt::getUnsigned(T->getNumBits()), + /*IsBoolean=*/false); + + mangleArtificialTagType(TTK_Struct, TemplateMangling, {"__clang"}); +} + +void MicrosoftCXXNameMangler::mangleType(const DependentExtIntType *T, + Qualifiers, SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID( + DiagnosticsEngine::Error, "cannot mangle this DependentExtInt type yet"); + Diags.Report(Range.getBegin(), DiagID) << Range; +} + // ::= | | // // ::= A # private near Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -295,6 +295,29 @@ VectorTypeBits.NumElements = nElements; } +ExtIntType::ExtIntType(bool IsUnsigned, unsigned NumBits) + : Type(ExtInt, QualType{}, /*Dependent*/ false, + /*InstantiationDependent*/ false, + /*VariablyModified*/ false, + /*ContainsUnexpandedParameterPack*/ false), + IsUnsigned(IsUnsigned), NumBits(NumBits) {} + +DependentExtIntType::DependentExtIntType(const ASTContext &Context, + bool IsUnsigned, Expr *NumBitsExpr) + : Type(DependentExtInt, QualType{}, + NumBitsExpr->isValueDependent() || NumBitsExpr->isTypeDependent(), + NumBitsExpr->isInstantiationDependent(), + /*VariablyModified*/ false, + NumBitsExpr->containsUnexpandedParameterPack()), + Context(Context), IsUnsigned(IsUnsigned), NumBitsExpr(NumBitsExpr) {} + +void DependentExtIntType::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Context, bool IsUnsigned, + Expr *NumBitsExpr) { + ID.AddBoolean(IsUnsigned); + NumBitsExpr->Profile(ID, Context, true); +} + /// getArrayElementTypeNoTypeQual - If this is an array type, return the /// element type of the array, potentially with type qualifiers missing. /// This method should never be used when type qualifiers are meaningful. @@ -1851,13 +1874,17 @@ if (const auto *ET = dyn_cast(CanonicalType)) return ET->getDecl()->isComplete(); - return false; + return isExtIntType(); } bool Type::isIntegralOrUnscopedEnumerationType() const { if (const auto *BT = dyn_cast(CanonicalType)) return BT->getKind() >= BuiltinType::Bool && BT->getKind() <= BuiltinType::Int128; + + if (isExtIntType()) + return true; + return isUnscopedEnumerationType(); } @@ -1938,6 +1965,9 @@ return ET->getDecl()->getIntegerType()->isSignedIntegerType(); } + if (const ExtIntType *IT = dyn_cast(CanonicalType)) + return !IT->isUnsigned(); + return false; } @@ -1952,6 +1982,10 @@ return ET->getDecl()->getIntegerType()->isSignedIntegerType(); } + if (const ExtIntType *IT = dyn_cast(CanonicalType)) + return !IT->isUnsigned(); + + return false; } @@ -1978,6 +2012,9 @@ return ET->getDecl()->getIntegerType()->isUnsignedIntegerType(); } + if (const ExtIntType *IT = dyn_cast(CanonicalType)) + return IT->isUnsigned(); + return false; } @@ -1992,6 +2029,9 @@ return ET->getDecl()->getIntegerType()->isUnsignedIntegerType(); } + if (const ExtIntType *IT = dyn_cast(CanonicalType)) + return IT->isUnsigned(); + return false; } @@ -2030,7 +2070,7 @@ BT->getKind() <= BuiltinType::Float128; if (const auto *ET = dyn_cast(CanonicalType)) return ET->getDecl()->isComplete() && !ET->getDecl()->isScoped(); - return false; + return isExtIntType(); } bool Type::isArithmeticType() const { @@ -2045,7 +2085,7 @@ // false for scoped enumerations since that will disable any // unwanted implicit conversions. return !ET->getDecl()->isScoped() && ET->getDecl()->isComplete(); - return isa(CanonicalType); + return isa(CanonicalType) || isExtIntType(); } Type::ScalarTypeKind Type::getScalarTypeKind() const { @@ -2074,7 +2114,8 @@ if (CT->getElementType()->isRealFloatingType()) return STK_FloatingComplex; return STK_IntegralComplex; - } + } else if (isExtIntType()) + return STK_Integral; llvm_unreachable("unknown scalar type"); } @@ -2223,6 +2264,7 @@ case Type::MemberPointer: case Type::Vector: case Type::ExtVector: + case Type::ExtInt: return true; case Type::Enum: @@ -3646,6 +3688,7 @@ // here in error recovery. return CachedProperties(ExternalLinkage, false); + case Type::ExtInt: case Type::Builtin: // C++ [basic.link]p8: // A type is said to have linkage if and only if: @@ -3743,6 +3786,7 @@ assert(T->isInstantiationDependentType()); return LinkageInfo::external(); + case Type::ExtInt: case Type::Builtin: return LinkageInfo::external(); @@ -3949,6 +3993,8 @@ case Type::ObjCInterface: case Type::Atomic: case Type::Pipe: + case Type::ExtInt: + case Type::DependentExtInt: return false; } llvm_unreachable("bad type kind!"); Index: clang/lib/AST/TypePrinter.cpp =================================================================== --- clang/lib/AST/TypePrinter.cpp +++ clang/lib/AST/TypePrinter.cpp @@ -227,6 +227,8 @@ case Type::ObjCInterface: case Type::Atomic: case Type::Pipe: + case Type::ExtInt: + case Type::DependentExtInt: CanPrefixQualifiers = true; break; @@ -1112,6 +1114,28 @@ void TypePrinter::printPipeAfter(const PipeType *T, raw_ostream &OS) {} +void TypePrinter::printExtIntBefore(const ExtIntType *T, raw_ostream &OS) { + if (T->isUnsigned()) + OS << "unsigned "; + OS << "_ExtInt(" << T->getNumBits() << ")"; + spaceBeforePlaceHolder(OS); +} + +void TypePrinter::printExtIntAfter(const ExtIntType *T, raw_ostream &OS) {} + +void TypePrinter::printDependentExtIntBefore(const DependentExtIntType *T, + raw_ostream &OS) { + if (T->isUnsigned()) + OS << "unsigned "; + OS << "_ExtInt("; + T->getNumBitsExpr()->printPretty(OS, nullptr, Policy); + OS << ")"; + spaceBeforePlaceHolder(OS); +} + +void TypePrinter::printDependentExtIntAfter(const DependentExtIntType *T, + raw_ostream &OS) {} + /// Appends the given scope to the end of a string. void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) { if (DC->isTranslationUnit()) return; Index: clang/lib/CodeGen/CGDebugInfo.h =================================================================== --- clang/lib/CodeGen/CGDebugInfo.h +++ clang/lib/CodeGen/CGDebugInfo.h @@ -166,6 +166,7 @@ llvm::DIType *CreateType(const BuiltinType *Ty); llvm::DIType *CreateType(const ComplexType *Ty); llvm::DIType *CreateType(const AutoType *Ty); + llvm::DIType *CreateType(const ExtIntType *Ty); llvm::DIType *CreateQualifiedType(QualType Ty, llvm::DIFile *Fg); llvm::DIType *CreateType(const TypedefType *Ty, llvm::DIFile *Fg); llvm::DIType *CreateType(const TemplateSpecializationType *Ty, Index: clang/lib/CodeGen/CGDebugInfo.cpp =================================================================== --- clang/lib/CodeGen/CGDebugInfo.cpp +++ clang/lib/CodeGen/CGDebugInfo.cpp @@ -820,6 +820,17 @@ return DBuilder.createUnspecifiedType("auto"); } +llvm::DIType *CGDebugInfo::CreateType(const ExtIntType *Ty) { + + StringRef Name = Ty->isUnsigned() ? "unsigned _ExtInt" : "_ExtInt"; + llvm::dwarf::TypeKind Encoding = Ty->isUnsigned() + ? llvm::dwarf::DW_ATE_unsigned + : llvm::dwarf::DW_ATE_signed; + + return DBuilder.createBasicType(Name, CGM.getContext().getTypeSize(Ty), + Encoding); +} + llvm::DIType *CGDebugInfo::CreateType(const ComplexType *Ty) { // Bit size and offset of the type. llvm::dwarf::TypeKind Encoding = llvm::dwarf::DW_ATE_complex_float; @@ -3108,6 +3119,8 @@ case Type::Atomic: return CreateType(cast(Ty), Unit); + case Type::ExtInt: + return CreateType(cast(Ty)); case Type::Pipe: return CreateType(cast(Ty), Unit); Index: clang/lib/CodeGen/CGExprScalar.cpp =================================================================== --- clang/lib/CodeGen/CGExprScalar.cpp +++ clang/lib/CodeGen/CGExprScalar.cpp @@ -752,6 +752,11 @@ llvm::Value *Zero,bool isDiv); // Common helper for getting how wide LHS of shift is. static Value *GetWidthMinusOneValue(Value* LHS,Value* RHS); + + // Used for shifting constraints for OpenCL, do mask for powers of 2, URem for + // non powers of two. + Value *ConstrainShiftValue(Value *LHS, Value *RHS, const Twine &Name); + Value *EmitDiv(const BinOpInfo &Ops); Value *EmitRem(const BinOpInfo &Ops); Value *EmitAdd(const BinOpInfo &Ops); @@ -3694,6 +3699,21 @@ return llvm::ConstantInt::get(RHS->getType(), Ty->getBitWidth() - 1); } +Value *ScalarExprEmitter::ConstrainShiftValue(Value *LHS, Value *RHS, + const Twine &Name) { + llvm::IntegerType *Ty; + if (llvm::VectorType *VT = dyn_cast(LHS->getType())) + Ty = cast(VT->getElementType()); + else + Ty = cast(LHS->getType()); + + if (llvm::isPowerOf2_64(Ty->getBitWidth())) + return Builder.CreateAnd(RHS, GetWidthMinusOneValue(LHS, RHS), Name); + + return Builder.CreateURem( + RHS, llvm::ConstantInt::get(RHS->getType(), Ty->getBitWidth()), Name); +} + Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) { // LLVM requires the LHS and RHS to be the same type: promote or truncate the // RHS to the same size as the LHS. @@ -3708,8 +3728,7 @@ bool SanitizeExponent = CGF.SanOpts.has(SanitizerKind::ShiftExponent); // OpenCL 6.3j: shift values are effectively % word size of LHS. if (CGF.getLangOpts().OpenCL) - RHS = - Builder.CreateAnd(RHS, GetWidthMinusOneValue(Ops.LHS, RHS), "shl.mask"); + RHS = ConstrainShiftValue(Ops.LHS, RHS, "shl.mask"); else if ((SanitizeBase || SanitizeExponent) && isa(Ops.LHS->getType())) { CodeGenFunction::SanitizerScope SanScope(&CGF); @@ -3771,8 +3790,7 @@ // OpenCL 6.3j: shift values are effectively % word size of LHS. if (CGF.getLangOpts().OpenCL) - RHS = - Builder.CreateAnd(RHS, GetWidthMinusOneValue(Ops.LHS, RHS), "shr.mask"); + RHS = ConstrainShiftValue(Ops.LHS, RHS, "shr.mask"); else if (CGF.SanOpts.has(SanitizerKind::ShiftExponent) && isa(Ops.LHS->getType())) { CodeGenFunction::SanitizerScope SanScope(&CGF); Index: clang/lib/CodeGen/CGRecordLayoutBuilder.cpp =================================================================== --- clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -385,7 +385,8 @@ Run = FieldEnd; continue; } - llvm::Type *Type = Types.ConvertTypeForMem(Field->getType()); + llvm::Type *Type = + Types.ConvertTypeForMem(Field->getType(), /*ForBitFields=*/true); // If we don't have a run yet, or don't live within the previous run's // allocated storage then we allocate some storage and start a new run. if (Run == FieldEnd || BitOffset >= Tail) { Index: clang/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ clang/lib/CodeGen/CodeGenFunction.cpp @@ -264,6 +264,7 @@ case Type::Enum: case Type::ObjCObjectPointer: case Type::Pipe: + case Type::ExtInt: return TEK_Scalar; // Complexes. @@ -2004,6 +2005,7 @@ case Type::ObjCObject: case Type::ObjCInterface: case Type::ObjCObjectPointer: + case Type::ExtInt: llvm_unreachable("type class is never variably-modified!"); case Type::Adjusted: Index: clang/lib/CodeGen/CodeGenTBAA.cpp =================================================================== --- clang/lib/CodeGen/CodeGenTBAA.cpp +++ clang/lib/CodeGen/CodeGenTBAA.cpp @@ -181,6 +181,15 @@ return createScalarTypeNode(OutName, getChar(), Size); } + if (const auto *EIT = dyn_cast(Ty)) { + SmallString<256> OutName; + llvm::raw_svector_ostream Out(OutName); + // Don't specify signed/unsigned since integer types can alias despite sign + // differences. + Out << "_ExtInt(" << EIT->getNumBits() << ')'; + return createScalarTypeNode(OutName, getChar(), Size); + } + // For now, handle any other kind of type conservatively. return getChar(); } Index: clang/lib/CodeGen/CodeGenTypes.h =================================================================== --- clang/lib/CodeGen/CodeGenTypes.h +++ clang/lib/CodeGen/CodeGenTypes.h @@ -134,7 +134,7 @@ /// ConvertType in that it is used to convert to the memory representation for /// a type. For example, the scalar representation for _Bool is i1, but the /// memory representation is usually i8 or i32, depending on the target. - llvm::Type *ConvertTypeForMem(QualType T); + llvm::Type *ConvertTypeForMem(QualType T, bool ForBitField = false); /// GetFunctionType - Get the LLVM function type for \arg Info. llvm::FunctionType *GetFunctionType(const CGFunctionInfo &Info); Index: clang/lib/CodeGen/CodeGenTypes.cpp =================================================================== --- clang/lib/CodeGen/CodeGenTypes.cpp +++ clang/lib/CodeGen/CodeGenTypes.cpp @@ -83,19 +83,19 @@ /// ConvertType in that it is used to convert to the memory representation for /// a type. For example, the scalar representation for _Bool is i1, but the /// memory representation is usually i8 or i32, depending on the target. -llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T) { +llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T, bool ForBitField) { llvm::Type *R = ConvertType(T); - // If this is a non-bool type, don't map it. - if (!R->isIntegerTy(1)) - return R; + // If this is a bool type, or an ExtIntType in a bitfield representation, + // map this integer to the target-specified size. + if ((ForBitField && T->isExtIntType()) || R->isIntegerTy(1)) + return llvm::IntegerType::get(getLLVMContext(), + (unsigned)Context.getTypeSize(T)); - // Otherwise, return an integer of the target-specified size. - return llvm::IntegerType::get(getLLVMContext(), - (unsigned)Context.getTypeSize(T)); + // Else, don't map it. + return R; } - /// isRecordLayoutComplete - Return true if the specified type is already /// completely laid out. bool CodeGenTypes::isRecordLayoutComplete(const Type *Ty) const { @@ -692,6 +692,11 @@ ResultType = CGM.getOpenCLRuntime().getPipeType(cast(Ty)); break; } + case Type::ExtInt: { + const auto &EIT = cast(Ty); + ResultType = llvm::Type::getIntNTy(getLLVMContext(), EIT->getNumBits()); + break; + } } assert(ResultType && "Didn't convert a type?"); Index: clang/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- clang/lib/CodeGen/ItaniumCXXABI.cpp +++ clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3205,6 +3205,9 @@ case Type::Pipe: llvm_unreachable("Pipe types shouldn't get here"); + case Type::ExtInt: + llvm_unreachable("Ext Int Types shouldn't get here"); + case Type::Builtin: // GCC treats vector and complex types as fundamental types. case Type::Vector: @@ -3461,6 +3464,9 @@ case Type::Pipe: llvm_unreachable("Pipe type shouldn't get here"); + case Type::ExtInt: + llvm_unreachable("Ext Int type shouldn't get here"); + case Type::ConstantArray: case Type::IncompleteArray: case Type::VariableArray: Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -2880,6 +2880,25 @@ ParsedAttr::AS_Keyword, EllipsisLoc); } +ExprResult Parser::ParseExtIntegerArgument() { + assert(Tok.is(tok::kw__ExtInt) && "Not an extended int type"); + ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume()) + return ExprError(); + + ExprResult ER = ParseConstantExpression(); + if (ER.isInvalid()) { + T.skipToEnd(); + return ExprError(); + } + + if(T.consumeClose()) + return ExprError(); + return ER; +} + /// Determine whether we're looking at something that might be a declarator /// in a simple-declaration. If it can't possibly be a declarator, maybe /// diagnose a missing semicolon after a prior tag definition in the decl @@ -3796,6 +3815,14 @@ isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec, DiagID, Policy); break; + case tok::kw__ExtInt: { + ExprResult ER = ParseExtIntegerArgument(); + if (ER.isInvalid()) + continue; + isInvalid = DS.SetExtIntType(Loc, ER.get(), PrevSpec, DiagID, Policy); + ConsumedEnd = PrevTokLocation; + break; + } case tok::kw___int128: isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec, DiagID, Policy); @@ -4878,6 +4905,7 @@ case tok::kw_char16_t: case tok::kw_char32_t: case tok::kw_int: + case tok::kw__ExtInt: case tok::kw_half: case tok::kw_float: case tok::kw_double: @@ -4957,6 +4985,7 @@ case tok::kw_char16_t: case tok::kw_char32_t: case tok::kw_int: + case tok::kw__ExtInt: case tok::kw_half: case tok::kw_float: case tok::kw_double: @@ -5123,6 +5152,7 @@ case tok::kw_char32_t: case tok::kw_int: + case tok::kw__ExtInt: case tok::kw_half: case tok::kw_float: case tok::kw_double: Index: clang/lib/Parse/ParseExpr.cpp =================================================================== --- clang/lib/Parse/ParseExpr.cpp +++ clang/lib/Parse/ParseExpr.cpp @@ -1458,6 +1458,7 @@ case tok::kw_long: case tok::kw___int64: case tok::kw___int128: + case tok::kw__ExtInt: case tok::kw_signed: case tok::kw_unsigned: case tok::kw_half: Index: clang/lib/Parse/ParseExprCXX.cpp =================================================================== --- clang/lib/Parse/ParseExprCXX.cpp +++ clang/lib/Parse/ParseExprCXX.cpp @@ -2149,6 +2149,19 @@ return; } + case tok::kw__ExtInt: { + ExprResult ER = ParseExtIntegerArgument(); + if (ER.isInvalid()) + DS.SetTypeSpecError(); + else + DS.SetExtIntType(Loc, ER.get(), PrevSpec, DiagID, Policy); + + // Do this here because we have already consumed the close paren. + DS.SetRangeEnd(PrevTokLocation); + DS.Finish(Actions, Policy); + return; + } + // builtin types case tok::kw_short: DS.SetTypeSpecWidth(DeclSpec::TSW_short, Loc, PrevSpec, DiagID, Policy); Index: clang/lib/Parse/ParseTentative.cpp =================================================================== --- clang/lib/Parse/ParseTentative.cpp +++ clang/lib/Parse/ParseTentative.cpp @@ -1116,6 +1116,7 @@ case tok::kw_half: case tok::kw_float: case tok::kw_int: + case tok::kw__ExtInt: case tok::kw_long: case tok::kw___int64: case tok::kw___int128: @@ -1742,6 +1743,24 @@ case tok::kw__Atomic: return TPResult::True; + case tok::kw__ExtInt: { + if (NextToken().isNot(tok::l_paren)) + return TPResult::Error; + RevertingTentativeParsingAction PA(*this); + ConsumeToken(); + ConsumeParen(); + + if (!SkipUntil(tok::r_paren, StopAtSemi)) + return TPResult::Error; + + if (Tok.is(tok::l_paren)) + return TPResult::Ambiguous; + + if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) + return BracedCastResult; + + return TPResult::True; + } default: return TPResult::False; } @@ -1774,6 +1793,7 @@ case tok::kw_bool: case tok::kw_short: case tok::kw_int: + case tok::kw__ExtInt: case tok::kw_long: case tok::kw___int64: case tok::kw___int128: Index: clang/lib/Sema/DeclSpec.cpp =================================================================== --- clang/lib/Sema/DeclSpec.cpp +++ clang/lib/Sema/DeclSpec.cpp @@ -351,6 +351,7 @@ case TST_half: case TST_int: case TST_int128: + case TST_extint: case TST_struct: case TST_interface: case TST_union: @@ -529,6 +530,7 @@ case DeclSpec::TST_char32: return "char32_t"; case DeclSpec::TST_int: return "int"; case DeclSpec::TST_int128: return "__int128"; + case DeclSpec::TST_extint: return "_ExtInt"; case DeclSpec::TST_half: return "half"; case DeclSpec::TST_float: return "float"; case DeclSpec::TST_double: return "double"; @@ -904,6 +906,27 @@ return false; } +bool DeclSpec::SetExtIntType(SourceLocation KWLoc, Expr *BitsExpr, + const char *&PrevSpec, unsigned &DiagID, + const PrintingPolicy &Policy) { + assert(BitsExpr && "no expression provided!"); + if (TypeSpecType == TST_error) + return false; + + if (TypeSpecType != TST_unspecified) { + PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy); + DiagID = diag::err_invalid_decl_spec_combination; + return true; + } + + TypeSpecType = TST_extint; + ExprRep = BitsExpr; + TSTLoc = KWLoc; + TSTNameLoc = KWLoc; + TypeSpecOwned = false; + return false; +} + bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, const LangOptions &Lang) { // Duplicates are permitted in C99 onwards, but are not permitted in C89 or @@ -1185,7 +1208,7 @@ TypeSpecType = TST_int; // unsigned -> unsigned int, signed -> signed int. else if (TypeSpecType != TST_int && TypeSpecType != TST_int128 && TypeSpecType != TST_char && TypeSpecType != TST_wchar && - !IsFixedPointType) { + !IsFixedPointType && TypeSpecType != TST_extint) { S.Diag(TSSLoc, diag::err_invalid_sign_spec) << getSpecifierName((TST)TypeSpecType, Policy); // signed double -> double. Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -9659,6 +9659,9 @@ false/*NonNegative*/); } + if (const auto *EIT = dyn_cast(T)) + return IntRange(EIT->getNumBits(), EIT->isUnsigned()); + const BuiltinType *BT = cast(T); assert(BT->isInteger()); @@ -9682,6 +9685,9 @@ if (const EnumType *ET = dyn_cast(T)) T = C.getCanonicalType(ET->getDecl()->getIntegerType()).getTypePtr(); + if (const auto *EIT = dyn_cast(T)) + return IntRange(EIT->getNumBits(), EIT->isUnsigned()); + const BuiltinType *BT = cast(T); assert(BT->isInteger()); Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -14555,12 +14555,16 @@ if (T->isDependentType()) return false; + // this doesn't use 'isIntegralType' despite the error message mentioning + // integral type because isIntegralType would also allow enum types in C. if (const BuiltinType *BT = T->getAs()) if (BT->isInteger()) return false; - Diag(UnderlyingLoc, diag::err_enum_invalid_underlying) << T; - return true; + if (T->isExtIntType()) + return false; + + return Diag(UnderlyingLoc, diag::err_enum_invalid_underlying) << T; } /// Check whether this is a valid redeclaration of a previous enumeration. Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -4187,6 +4187,7 @@ case Type::ObjCObjectPointer: case Type::ObjCTypeParam: case Type::Pipe: + case Type::ExtInt: llvm_unreachable("type class is never variably-modified!"); case Type::Adjusted: T = cast(Ty)->getOriginalType(); @@ -9943,14 +9944,19 @@ << RHS.get()->getSourceRange()); return; } - llvm::APInt LeftBits(Right.getBitWidth(), - S.Context.getTypeSize(LHS.get()->getType())); + + QualType LHSExprType = LHS.get()->getType(); + uint64_t LeftSize = LHSExprType->isExtIntType() + ? S.Context.getIntWidth(LHSExprType) + : S.Context.getTypeSize(LHSExprType); + llvm::APInt LeftBits(Right.getBitWidth(), LeftSize); if (Right.uge(LeftBits)) { S.DiagRuntimeBehavior(Loc, RHS.get(), S.PDiag(diag::warn_shift_gt_typewidth) << RHS.get()->getSourceRange()); return; } + if (Opc != BO_Shl) return; Index: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -2962,6 +2962,7 @@ case Type::Vector: case Type::ExtVector: case Type::Complex: + case Type::ExtInt: break; // Non-deduced auto types only get here for error cases. Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -5904,6 +5904,15 @@ return false; } +bool UnnamedLocalNoLinkageFinder::VisitExtIntType(const ExtIntType *T) { + return false; +} + +bool UnnamedLocalNoLinkageFinder::VisitDependentExtIntType( + const DependentExtIntType *T) { + return false; +} + bool UnnamedLocalNoLinkageFinder::VisitTagDecl(const TagDecl *Tag) { if (Tag->getDeclContext()->isFunctionOrMethod()) { S.Diag(SR.getBegin(), @@ -6826,7 +6835,7 @@ QualType IntegerType = ParamType; if (const EnumType *Enum = IntegerType->getAs()) IntegerType = Enum->getDecl()->getIntegerType(); - Value = Value.extOrTrunc(Context.getTypeSize(IntegerType)); + Value = Value.extOrTrunc(Context.getIntWidth(IntegerType)); Converted = TemplateArgument(Context, Value, Context.getCanonicalType(ParamType)); @@ -6920,7 +6929,7 @@ // Coerce the template argument's value to the value it will have // based on the template parameter's type. - unsigned AllowedBits = Context.getTypeSize(IntegerType); + unsigned AllowedBits = Context.getIntWidth(IntegerType); if (Value.getBitWidth() != AllowedBits) Value = Value.extOrTrunc(AllowedBits); Value.setIsSigned(IntegerType->isSignedIntegerOrEnumerationType()); Index: clang/lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- clang/lib/Sema/SemaTemplateDeduction.cpp +++ clang/lib/Sema/SemaTemplateDeduction.cpp @@ -1515,6 +1515,7 @@ case Type::ObjCObject: case Type::ObjCInterface: case Type::ObjCObjectPointer: + case Type::ExtInt: if (TDF & TDF_SkipNonDependent) return Sema::TDK_Success; @@ -2106,6 +2107,33 @@ return Sema::TDK_NonDeducedMismatch; } + case Type::DependentExtInt: { + const auto *IntParam = cast(Param); + + if (const auto *IntArg = cast(Arg)){ + if (IntParam->isUnsigned() != IntArg->isUnsigned()) + return Sema::TDK_NonDeducedMismatch; + + NonTypeTemplateParmDecl *NTTP = + getDeducedParameterFromExpr(Info, IntParam->getNumBitsExpr()); + if (!NTTP) + return Sema::TDK_Success; + + llvm::APSInt ArgSize(S.Context.getTypeSize(S.Context.IntTy), false); + ArgSize = IntArg->getNumBits(); + + return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, ArgSize, + S.Context.IntTy, true, Info, + Deduced); + } + + if (const auto *IntArg = cast(Arg)) { + if (IntParam->isUnsigned() != IntArg->isUnsigned()) + return Sema::TDK_NonDeducedMismatch; + return Sema::TDK_Success; + } + return Sema::TDK_NonDeducedMismatch; + } case Type::TypeOfExpr: case Type::TypeOf: @@ -5831,6 +5859,11 @@ cast(T)->getDeducedType(), OnlyDeduced, Depth, Used); break; + case Type::DependentExtInt: + MarkUsedTemplateParameters(Ctx, + cast(T)->getNumBitsExpr(), + OnlyDeduced, Depth, Used); + break; // None of these types have any template parameters in them. case Type::Builtin: @@ -5843,6 +5876,7 @@ case Type::ObjCObjectPointer: case Type::UnresolvedUsing: case Type::Pipe: + case Type::ExtInt: #define TYPE(Class, Base) #define ABSTRACT_TYPE(Class, Base) #define DEPENDENT_TYPE(Class, Base) Index: clang/lib/Sema/SemaTemplateVariadic.cpp =================================================================== --- clang/lib/Sema/SemaTemplateVariadic.cpp +++ clang/lib/Sema/SemaTemplateVariadic.cpp @@ -847,6 +847,7 @@ case TST_typeofExpr: case TST_decltype: + case TST_extint: if (DS.getRepAsExpr() && DS.getRepAsExpr()->containsUnexpandedParameterPack()) return true; Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -35,6 +35,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/DerivedTypes.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; @@ -1440,6 +1441,15 @@ } break; } + case DeclSpec::TST_extint: { + Result = S.BuildExtIntType(DS.getTypeSpecSign() == TSS_unsigned, + DS.getRepAsExpr(), DS.getBeginLoc()); + if (Result.isNull()) { + Result = Context.IntTy; + declarator.setInvalidType(true); + } + break; + } case DeclSpec::TST_accum: { switch (DS.getTypeSpecWidth()) { case DeclSpec::TSW_short: @@ -2153,6 +2163,45 @@ return Context.getWritePipeType(T); } +/// Build a extended int type. +/// +/// \param Sign TypeSpecifierSign of this type. +/// +/// \param BitWidth Size of this int type in bits, or an expression representing +/// that. +/// +/// \param Loc Location of the keyword. +QualType Sema::BuildExtIntType(bool IsUnsigned, Expr *BitWidth, + SourceLocation Loc) { + if (BitWidth->isInstantiationDependent()) + return Context.getDependentExtIntType(IsUnsigned, BitWidth); + + llvm::APSInt Bits(32); + ExprResult ICE = VerifyIntegerConstantExpression(BitWidth, &Bits); + + if (ICE.isInvalid()) + return QualType(); + + int64_t NumBits = Bits.getSExtValue(); + if (!IsUnsigned && NumBits < 2) { + Diag(Loc, diag::err_ext_int_bad_size) << 0; + return QualType(); + } + + if (IsUnsigned && NumBits < 1) { + Diag(Loc, diag::err_ext_int_bad_size) << 1; + return QualType(); + } + + if (NumBits > llvm::IntegerType::MAX_INT_BITS) { + Diag(Loc, diag::err_ext_int_max_size) << IsUnsigned + << llvm::IntegerType::MAX_INT_BITS; + return QualType(); + } + + return Context.getExtIntType(IsUnsigned, NumBits); +} + /// Check whether the specified array size makes the array type a VLA. If so, /// return true, if not, return the size of the array in SizeVal. static bool isArraySizeVLA(Sema &S, Expr *ArraySize, llvm::APSInt &SizeVal) { @@ -5757,6 +5806,14 @@ TL.getValueLoc().initializeFullCopy(TInfo->getTypeLoc()); } + void VisitExtIntTypeLoc(ExtIntTypeLoc TL) { + TL.setNameLoc(DS.getTypeSpecTypeLoc()); + } + + void VisitDependentExtIntTypeLoc(DependentExtIntTypeLoc TL) { + TL.setNameLoc(DS.getTypeSpecTypeLoc()); + } + void VisitTypeLoc(TypeLoc TL) { // FIXME: add other typespec types and change this to an assert. TL.initialize(Context, DS.getTypeSpecTypeLoc()); @@ -5883,6 +5940,9 @@ assert(Chunk.Kind == DeclaratorChunk::Pipe); TL.setKWLoc(Chunk.Loc); } + void VisitExtIntTypeLoc(ExtIntTypeLoc TL) { + TL.setNameLoc(Chunk.Loc); + } void VisitMacroQualifiedTypeLoc(MacroQualifiedTypeLoc TL) { TL.setExpansionLoc(Chunk.Loc); } Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -1167,6 +1167,14 @@ QualType RebuildPipeType(QualType ValueType, SourceLocation KWLoc, bool isReadPipe); + /// Build an extended int given its value type. + QualType RebuildExtIntType(bool IsUnsigned, unsigned NumBits, + SourceLocation Loc); + + /// Build a dependent extended int given its value type. + QualType RebuildDependentExtIntType(bool IsUnsigned, Expr *NumBitsExpr, + SourceLocation Loc); + /// Build a new template name given a nested name specifier, a flag /// indicating whether the "template" keyword was provided, and the template /// that the template name refers to. @@ -6039,6 +6047,57 @@ return Result; } +template +QualType TreeTransform::TransformExtIntType(TypeLocBuilder &TLB, + ExtIntTypeLoc TL) { + const ExtIntType *EIT = TL.getTypePtr(); + QualType Result = TL.getType(); + + if (getDerived().AlwaysRebuild()) { + Result = getDerived().RebuildExtIntType(EIT->isUnsigned(), + EIT->getNumBits(), TL.getNameLoc()); + if (Result.isNull()) + return QualType(); + } + + ExtIntTypeLoc NewTL = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; +} + +template +QualType TreeTransform::TransformDependentExtIntType( + TypeLocBuilder &TLB, DependentExtIntTypeLoc TL) { + const DependentExtIntType *EIT = TL.getTypePtr(); + + EnterExpressionEvaluationContext Unevaluated( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); + ExprResult BitsExpr = getDerived().TransformExpr(EIT->getNumBitsExpr()); + BitsExpr = SemaRef.ActOnConstantExpression(BitsExpr); + + if (BitsExpr.isInvalid()) + return QualType(); + + QualType Result = TL.getType(); + + if (getDerived().AlwaysRebuild() || BitsExpr.get() != EIT->getNumBitsExpr()) { + Result = getDerived().RebuildDependentExtIntType( + EIT->isUnsigned(), BitsExpr.get(), TL.getNameLoc()); + + if (Result.isNull()) + return QualType(); + } + + if (isa(Result)) { + DependentExtIntTypeLoc NewTL = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + } else { + ExtIntTypeLoc NewTL = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + } + return Result; +} + /// Simple iterator that traverses the template arguments in a /// container that provides a \c getArgLoc() member function. /// @@ -13468,6 +13527,23 @@ : SemaRef.BuildWritePipeType(ValueType, KWLoc); } +template +QualType TreeTransform::RebuildExtIntType(bool IsUnsigned, + unsigned NumBits, + SourceLocation Loc) { + llvm::APInt NumBitsAP(SemaRef.Context.getIntWidth(SemaRef.Context.IntTy), + NumBits, true); + IntegerLiteral *Bits = IntegerLiteral::Create(SemaRef.Context, NumBitsAP, + SemaRef.Context.IntTy, Loc); + return SemaRef.BuildExtIntType(IsUnsigned, Bits, Loc); +} + +template +QualType TreeTransform::RebuildDependentExtIntType( + bool IsUnsigned, Expr *NumBitsExpr, SourceLocation Loc) { + return SemaRef.BuildExtIntType(IsUnsigned, NumBitsExpr, Loc); +} + template TemplateName TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, Index: clang/lib/Serialization/ASTReader.cpp =================================================================== --- clang/lib/Serialization/ASTReader.cpp +++ clang/lib/Serialization/ASTReader.cpp @@ -6709,6 +6709,15 @@ TL.setKWLoc(readSourceLocation()); } +void TypeLocReader::VisitExtIntTypeLoc(clang::ExtIntTypeLoc TL) { + TL.setNameLoc(readSourceLocation()); +} +void TypeLocReader::VisitDependentExtIntTypeLoc( + clang::DependentExtIntTypeLoc TL) { + TL.setNameLoc(readSourceLocation()); +} + + void ASTRecordReader::readTypeLoc(TypeLoc TL) { TypeLocReader TLR(*this); for (; !TL.isNull(); TL = TL.getNextTypeLoc()) Index: clang/lib/Serialization/ASTWriter.cpp =================================================================== --- clang/lib/Serialization/ASTWriter.cpp +++ clang/lib/Serialization/ASTWriter.cpp @@ -476,6 +476,14 @@ Record.AddSourceLocation(TL.getKWLoc()); } +void TypeLocWriter::VisitExtIntTypeLoc(clang::ExtIntTypeLoc TL) { + Record.AddSourceLocation(TL.getNameLoc()); +} +void TypeLocWriter::VisitDependentExtIntTypeLoc( + clang::DependentExtIntTypeLoc TL) { + Record.AddSourceLocation(TL.getNameLoc()); +} + void ASTWriter::WriteTypeAbbrevs() { using namespace llvm; Index: clang/test/CodeGen/ext-int-sanitizer.cpp =================================================================== --- /dev/null +++ clang/test/CodeGen/ext-int-sanitizer.cpp @@ -0,0 +1,291 @@ +// RUN: %clang_cc1 -triple x86_64-gnu-linux -fsanitize=array-bounds,enum,float-cast-overflow,integer-divide-by-zero,implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change,unsigned-integer-overflow,signed-integer-overflow,shift-base,shift-exponent -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s + + +// CHECK: define void @_Z6BoundsRA10_KiU7_ExtIntILi15EEi +void Bounds(const int (&Array)[10], _ExtInt(15) Index) { + int I1 = Array[Index]; + // CHECK: %[[SEXT:.+]] = sext i15 %{{.+}} to i64 + // CHECK: %[[CMP:.+]] = icmp ult i64 %[[SEXT]], 10 + // CHECK: br i1 %[[CMP]] + // CHECK: call void @__ubsan_handle_out_of_bounds +} + +// CHECK: define void @_Z4Enumv +void Enum() { + enum E1 { e1a = 0, e1b = 127 } + e1; + enum E2 { e2a = -1, e2b = 64 } + e2; + enum E3 { e3a = (1u << 31) - 1 } + e3; + + _ExtInt(34) a = e1; + // CHECK: %[[E1:.+]] = icmp ule i32 %{{.*}}, 127 + // CHECK: br i1 %[[E1]] + // CHECK: call void @__ubsan_handle_load_invalid_value_abort + _ExtInt(34) b = e2; + // CHECK: %[[E2HI:.*]] = icmp sle i32 {{.*}}, 127 + // CHECK: %[[E2LO:.*]] = icmp sge i32 {{.*}}, -128 + // CHECK: %[[E2:.*]] = and i1 %[[E2HI]], %[[E2LO]] + // CHECK: br i1 %[[E2]] + // CHECK: call void @__ubsan_handle_load_invalid_value_abort + _ExtInt(34) c = e3; + // CHECK: %[[E3:.*]] = icmp ule i32 {{.*}}, 2147483647 + // CHECK: br i1 %[[E3]] + // CHECK: call void @__ubsan_handle_load_invalid_value_abort +} + +// CHECK: define void @_Z13FloatOverflowfd +void FloatOverflow(float f, double d) { + _ExtInt(10) E = f; + // CHECK: fcmp ogt float %{{.+}}, -5.130000e+02 + // CHECK: fcmp olt float %{{.+}}, 5.120000e+02 + _ExtInt(10) E2 = d; + // CHECK: fcmp ogt double %{{.+}}, -5.130000e+02 + // CHECK: fcmp olt double %{{.+}}, 5.120000e+02 + _ExtInt(7) E3 = f; + // CHECK: fcmp ogt float %{{.+}}, -6.500000e+01 + // CHECK: fcmp olt float %{{.+}}, 6.400000e+01 + _ExtInt(7) E4 = d; + // CHECK: fcmp ogt double %{{.+}}, -6.500000e+01 + // CHECK: fcmp olt double %{{.+}}, 6.400000e+01 +} + +// CHECK: define void @_Z14UIntTruncationU7_ExtIntILi35EEjjy +void UIntTruncation(unsigned _ExtInt(35) E, unsigned int i, unsigned long long ll) { + + i = E; + // CHECK: %[[LOADE:.+]] = load i35 + // CHECK: %[[CONV:.+]] = trunc i35 %[[LOADE]] to i32 + // CHECK: %[[EXT:.+]] = zext i32 %[[CONV]] to i35 + // CHECK: %[[CHECK:.+]] = icmp eq i35 %[[EXT]], %[[LOADE]] + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_implicit_conversion_abort + + E = ll; + // CHECK: %[[LOADLL:.+]] = load i64 + // CHECK: %[[CONV:.+]] = trunc i64 %[[LOADLL]] to i35 + // CHECK: %[[EXT:.+]] = zext i35 %[[CONV]] to i64 + // CHECK: %[[CHECK:.+]] = icmp eq i64 %[[EXT]], %[[LOADLL]] + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_implicit_conversion_abort +} + +// CHECK: define void @_Z13IntTruncationU7_ExtIntILi35EEiU7_ExtIntILi42EEjij +void IntTruncation(_ExtInt(35) E, unsigned _ExtInt(42) UE, int i, unsigned j) { + + j = E; + // CHECK: %[[LOADE:.+]] = load i35 + // CHECK: %[[CONV:.+]] = trunc i35 %[[LOADE]] to i32 + // CHECK: %[[EXT:.+]] = zext i32 %[[CONV]] to i35 + // CHECK: %[[CHECK:.+]] = icmp eq i35 %[[EXT]], %[[LOADE]] + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_implicit_conversion_abort + + j = UE; + // CHECK: %[[LOADUE:.+]] = load i42 + // CHECK: %[[CONV:.+]] = trunc i42 %[[LOADUE]] to i32 + // CHECK: %[[EXT:.+]] = zext i32 %[[CONV]] to i42 + // CHECK: %[[CHECK:.+]] = icmp eq i42 %[[EXT]], %[[LOADUE]] + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_implicit_conversion_abort + + // Note: also triggers sign change check. + i = UE; + // CHECK: %[[LOADUE:.+]] = load i42 + // CHECK: %[[CONV:.+]] = trunc i42 %[[LOADUE]] to i32 + // CHECK: %[[NEG:.+]] = icmp slt i32 %[[CONV]], 0 + // CHECK: %[[SIGNCHECK:.+]] = icmp eq i1 false, %[[NEG]] + // CHECK: %[[EXT:.+]] = sext i32 %[[CONV]] to i42 + // CHECK: %[[CHECK:.+]] = icmp eq i42 %[[EXT]], %[[LOADUE]] + // CHECK: %[[CHECKBOTH:.+]] = and i1 %[[SIGNCHECK]], %[[CHECK]] + // CHECK: br i1 %[[CHECKBOTH]] + // CHECK: call void @__ubsan_handle_implicit_conversion_abort + + // Note: also triggers sign change check. + E = UE; + // CHECK: %[[LOADUE:.+]] = load i42 + // CHECK: %[[CONV:.+]] = trunc i42 %[[LOADUE]] to i35 + // CHECK: %[[NEG:.+]] = icmp slt i35 %[[CONV]], 0 + // CHECK: %[[SIGNCHECK:.+]] = icmp eq i1 false, %[[NEG]] + // CHECK: %[[EXT:.+]] = sext i35 %[[CONV]] to i42 + // CHECK: %[[CHECK:.+]] = icmp eq i42 %[[EXT]], %[[LOADUE]] + // CHECK: %[[CHECKBOTH:.+]] = and i1 %[[SIGNCHECK]], %[[CHECK]] + // CHECK: br i1 %[[CHECKBOTH]] + // CHECK: call void @__ubsan_handle_implicit_conversion_abort +} + +// CHECK: define void @_Z15SignChangeCheckU7_ExtIntILi39EEjU7_ExtIntILi39EEi +void SignChangeCheck(unsigned _ExtInt(39) UE, _ExtInt(39) E) { + UE = E; + // CHECK: %[[LOADE:.+]] = load i39 + // CHECK: %[[NEG:.+]] = icmp slt i39 %[[LOADE]], 0 + // CHECK: %[[SIGNCHECK:.+]] = icmp eq i1 %[[NEG]], false + // CHECK: br i1 %[[SIGNCHECK]] + // CHECK: call void @__ubsan_handle_implicit_conversion_abort + + + E = UE; + // CHECK: %[[LOADUE:.+]] = load i39 + // CHECK: %[[NEG:.+]] = icmp slt i39 %[[LOADUE]], 0 + // CHECK: %[[SIGNCHECK:.+]] = icmp eq i1 false, %[[NEG]] + // CHECK: br i1 %[[SIGNCHECK]] + // CHECK: call void @__ubsan_handle_implicit_conversion_abort +} + +// CHECK: define void @_Z9DivByZeroU7_ExtIntILi11EEii +void DivByZero(_ExtInt(11) E, int i) { + + // Also triggers signed integer overflow. + E / i; + // CHECK: %[[E:.+]] = load i11, i11* + // CHECK: %[[CONV:.+]] = sext i11 %[[E]] to i32 + // CHECK: %[[I:.+]] = load i32, i32* + // CHECK: %[[NEZERO:.+]] = icmp ne i32 %[[I]], 0 + // CHECK: %[[NEMIN:.+]] = icmp ne i32 %[[CONV]], -2147483648 + // CHECK: %[[NENEG1:.+]] = icmp ne i32 %[[I]], -1 + // CHECK: %[[OR:.+]] = or i1 %[[NEMIN]], %[[NENEG1]] + // CHECK: %[[AND:.+]] = and i1 %[[NEZERO]], %[[OR]] + // CHECK: br i1 %[[AND]] + // CHECK: call void @__ubsan_handle_divrem_overflow_abort + + // Also triggers signed integer overflow. + i / E; + // CHECK: %[[I:.+]] = load i32, i32* + // CHECK: %[[E:.+]] = load i11, i11* + // CHECK: %[[EXT:.+]] = sext i11 %[[E]] to i32 + // CHECK: %[[NEZERO:.+]] = icmp ne i32 %[[EXT]], 0 + // CHECK: %[[NEMIN:.+]] = icmp ne i32 %[[I]], -2147483648 + // CHECK: %[[NENEG1:.+]] = icmp ne i32 %[[EXT]], -1 + // CHECK: %[[OR:.+]] = or i1 %[[NEMIN]], %[[NENEG1]] + // CHECK: %[[AND:.+]] = and i1 %[[NEZERO]], %[[OR]] + // CHECK: br i1 %[[AND]] + // CHECK: call void @__ubsan_handle_divrem_overflow_abort + + // Also triggers signed integer overflow. + E / E; + // CHECK: %[[E:.+]] = load i11, i11* + // CHECK: %[[E2:.+]] = load i11, i11* + // CHECK: %[[NEZERO:.+]] = icmp ne i11 %[[E2]], 0 + // CHECK: %[[NEMIN:.+]] = icmp ne i11 %[[E]], -1024 + // CHECK: %[[NENEG1:.+]] = icmp ne i11 %[[E2]], -1 + // CHECK: %[[OR:.+]] = or i1 %[[NEMIN]], %[[NENEG1]] + // CHECK: %[[AND:.+]] = and i1 %[[NEZERO]], %[[OR]] + // CHECK: br i1 %[[AND]] + // CHECK: call void @__ubsan_handle_divrem_overflow_abort +} + +// TODO: +//-fsanitize=shift: (shift-base, shift-exponent) Shift operators where the amount shifted is greater or equal to the promoted bit-width of the left hand side or less than zero, or where the left hand side is negative. For a signed left shift, also checks for signed overflow in C, and for unsigned overflow in C++. You can use -fsanitize=shift-base or -fsanitize=shift-exponent to check only left-hand side or right-hand side of shift operation, respectively. +// CHECK: define void @_Z6ShiftsU7_ExtIntILi9EEi +void Shifts(_ExtInt(9) E) { + E >> E; + // CHECK: %[[LHSE:.+]] = load i9, i9* + // CHECK: %[[RHSE:.+]] = load i9, i9* + // CHECK: %[[CMP:.+]] = icmp ule i9 %[[RHSE]], 8 + // CHECK: br i1 %[[CMP]] + // CHECK: call void @__ubsan_handle_shift_out_of_bounds_abort + + E << E; + // CHECK: %[[LHSE:.+]] = load i9, i9* + // CHECK: %[[RHSE:.+]] = load i9, i9* + // CHECK: %[[CMP:.+]] = icmp ule i9 %[[RHSE]], 8 + // CHECK: br i1 %[[CMP]] + // CHECK: %[[ZEROS:.+]] = sub nuw nsw i9 8, %[[RHSE]] + // CHECK: %[[CHECK:.+]] = lshr i9 %[[LHSE]], %[[ZEROS]] + // CHECK: %[[SKIPSIGN:.+]] = lshr i9 %[[CHECK]], 1 + // CHECK: %[[CHECK:.+]] = icmp eq i9 %[[SKIPSIGN]] + // CHECK: %[[PHI:.+]] = phi i1 [ true, %{{.+}} ], [ %[[CHECK]], %{{.+}} ] + // CHECK: and i1 %[[CMP]], %[[PHI]] + // CHECK: call void @__ubsan_handle_shift_out_of_bounds_abort +} + +// CHECK: define void @_Z21SignedIntegerOverflowU7_ExtIntILi93EEiU7_ExtIntILi4EEiU7_ExtIntILi31EEi +void SignedIntegerOverflow(_ExtInt(93) BiggestE, + _ExtInt(4) SmallestE, + _ExtInt(31) JustRightE) { + BiggestE + BiggestE; + // CHECK: %[[LOAD1:.+]] = load i93, i93* + // CHECK: %[[LOAD2:.+]] = load i93, i93* + // CHECK: %[[OFCALL:.+]] = call { i93, i1 } @llvm.sadd.with.overflow.i93(i93 %[[LOAD1]], i93 %[[LOAD2]]) + // CHECK: %[[EXRESULT:.+]] = extractvalue { i93, i1 } %[[OFCALL]], 0 + // CHECK: %[[OFRESULT:.+]] = extractvalue { i93, i1 } %[[OFCALL]], 1 + // CHECK: %[[CHECK:.+]] = xor i1 %[[OFRESULT]], true + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_add_overflow_abort + + SmallestE - SmallestE; + // CHECK: %[[LOAD1:.+]] = load i4, i4* + // CHECK: %[[LOAD2:.+]] = load i4, i4* + // CHECK: %[[OFCALL:.+]] = call { i4, i1 } @llvm.ssub.with.overflow.i4(i4 %[[LOAD1]], i4 %[[LOAD2]]) + // CHECK: %[[EXRESULT:.+]] = extractvalue { i4, i1 } %[[OFCALL]], 0 + // CHECK: %[[OFRESULT:.+]] = extractvalue { i4, i1 } %[[OFCALL]], 1 + // CHECK: %[[CHECK:.+]] = xor i1 %[[OFRESULT]], true + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_sub_overflow_abort + + JustRightE * JustRightE; + // CHECK: %[[LOAD1:.+]] = load i31, i31* + // CHECK: %[[LOAD2:.+]] = load i31, i31* + // CHECK: %[[OFCALL:.+]] = call { i31, i1 } @llvm.smul.with.overflow.i31(i31 %[[LOAD1]], i31 %[[LOAD2]]) + // CHECK: %[[EXRESULT:.+]] = extractvalue { i31, i1 } %[[OFCALL]], 0 + // CHECK: %[[OFRESULT:.+]] = extractvalue { i31, i1 } %[[OFCALL]], 1 + // CHECK: %[[CHECK:.+]] = xor i1 %[[OFRESULT]], true + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_mul_overflow_abort +} + +// CHECK: define void @_Z23UnsignedIntegerOverflowjU7_ExtIntILi23EEjU7_ExtIntILi35EEj +void UnsignedIntegerOverflow(unsigned u, + unsigned _ExtInt(23) SmallE, + unsigned _ExtInt(35) BigE) { + u = SmallE + SmallE; + // CHECK: %[[LOADE1:.+]] = load i23, i23* + // CHECK: %[[LOADE2:.+]] = load i23, i23* + // CHECK: %[[OFCALL:.+]] = call { i23, i1 } @llvm.uadd.with.overflow.i23(i23 %[[LOADE1]], i23 %[[LOADE2]]) + // CHECK: %[[EXRESULT:.+]] = extractvalue { i23, i1 } %[[OFCALL]], 0 + // CHECK: %[[OFRESULT:.+]] = extractvalue { i23, i1 } %[[OFCALL]], 1 + // CHECK: %[[CHECK:.+]] = xor i1 %[[OFRESULT]], true + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_add_overflow_abort + + SmallE = u + u; + // CHECK: %[[LOADU1:.+]] = load i32, i32* + // CHECK: %[[LOADU2:.+]] = load i32, i32* + // CHECK: %[[OFCALL:.+]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %[[LOADU1]], i32 %[[LOADU2]]) + // CHECK: %[[EXRESULT:.+]] = extractvalue { i32, i1 } %[[OFCALL]], 0 + // CHECK: %[[OFRESULT:.+]] = extractvalue { i32, i1 } %[[OFCALL]], 1 + // CHECK: %[[CHECK:.+]] = xor i1 %[[OFRESULT]], true + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_add_overflow_abort + + SmallE = SmallE + SmallE; + // CHECK: %[[LOADE1:.+]] = load i23, i23* + // CHECK: %[[LOADE2:.+]] = load i23, i23* + // CHECK: %[[OFCALL:.+]] = call { i23, i1 } @llvm.uadd.with.overflow.i23(i23 %[[LOADE1]], i23 %[[LOADE2]]) + // CHECK: %[[EXRESULT:.+]] = extractvalue { i23, i1 } %[[OFCALL]], 0 + // CHECK: %[[OFRESULT:.+]] = extractvalue { i23, i1 } %[[OFCALL]], 1 + // CHECK: %[[CHECK:.+]] = xor i1 %[[OFRESULT]], true + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_add_overflow_abort + + SmallE = BigE + BigE; + // CHECK: %[[LOADE1:.+]] = load i35, i35* + // CHECK: %[[LOADE2:.+]] = load i35, i35* + // CHECK: %[[OFCALL:.+]] = call { i35, i1 } @llvm.uadd.with.overflow.i35(i35 %[[LOADE1]], i35 %[[LOADE2]]) + // CHECK: %[[EXRESULT:.+]] = extractvalue { i35, i1 } %[[OFCALL]], 0 + // CHECK: %[[OFRESULT:.+]] = extractvalue { i35, i1 } %[[OFCALL]], 1 + // CHECK: %[[CHECK:.+]] = xor i1 %[[OFRESULT]], true + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_add_overflow_abort + + BigE = BigE + BigE; + // CHECK: %[[LOADE1:.+]] = load i35, i35* + // CHECK: %[[LOADE2:.+]] = load i35, i35* + // CHECK: %[[OFCALL:.+]] = call { i35, i1 } @llvm.uadd.with.overflow.i35(i35 %[[LOADE1]], i35 %[[LOADE2]]) + // CHECK: %[[EXRESULT:.+]] = extractvalue { i35, i1 } %[[OFCALL]], 0 + // CHECK: %[[OFRESULT:.+]] = extractvalue { i35, i1 } %[[OFCALL]], 1 + // CHECK: %[[CHECK:.+]] = xor i1 %[[OFRESULT]], true + // CHECK: br i1 %[[CHECK]] + // CHECK: call void @__ubsan_handle_add_overflow_abort +} Index: clang/test/CodeGen/ext-int.cpp =================================================================== --- /dev/null +++ clang/test/CodeGen/ext-int.cpp @@ -0,0 +1,249 @@ +// RUN: %clang_cc1 -triple x86_64-gnu-linux -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,LIN,NoNewStructPathTBAA +// RUN: %clang_cc1 -triple x86_64-gnu-linux -O3 -disable-llvm-passes -new-struct-path-tbaa -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,LIN,NewStructPathTBAA + +// RUN: %clang_cc1 -triple x86_64-windows-pc -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WIN,NoNewStructPathTBAA +// RUN: %clang_cc1 -triple x86_64-windows-pc -O3 -disable-llvm-passes -new-struct-path-tbaa -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WIN,NewStructPathTBAA + + +// Ensure that the layout for these structs is the same as the normal bitfield +// layouts. +struct BitFieldsByte { + _ExtInt(7) A : 3; + _ExtInt(7) B : 3; + _ExtInt(7) C : 2; +}; +// CHECK: %struct.BitFieldsByte = type { i8 } + +struct BitFieldsShort { + _ExtInt(15) A : 3; + _ExtInt(15) B : 3; + _ExtInt(15) C : 2; +}; +// LIN: %struct.BitFieldsShort = type { i8, i8 } +// WIN: %struct.BitFieldsShort = type { i16 } + +struct BitFieldsInt { + _ExtInt(31) A : 3; + _ExtInt(31) B : 3; + _ExtInt(31) C : 2; +}; +// LIN: %struct.BitFieldsInt = type { i8, [3 x i8] } +// WIN: %struct.BitFieldsInt = type { i32 } + +struct BitFieldsLong { + _ExtInt(63) A : 3; + _ExtInt(63) B : 3; + _ExtInt(63) C : 2; +}; +// LIN: %struct.BitFieldsLong = type { i8, [7 x i8] } +// WIN: %struct.BitFieldsLong = type { i64 } + +struct HasExtIntFirst { + _ExtInt(35) A; + int B; +}; +// CHECK: %struct.HasExtIntFirst = type { i35, i32 } + +struct HasExtIntLast { + int A; + _ExtInt(35) B; +}; +// CHECK: %struct.HasExtIntLast = type { i32, i35 } + +struct HasExtIntMiddle { + int A; + _ExtInt(35) B; + int C; +}; +// CHECK: %struct.HasExtIntMiddle = type { i32, i35, i32 } + +// Force emitting of the above structs. +void StructEmit() { + BitFieldsByte A; + BitFieldsShort B; + BitFieldsInt C; + BitFieldsLong D; + + HasExtIntFirst E; + HasExtIntLast F; + HasExtIntMiddle G; +} + +void BitfieldAssignment() { + // LIN: define void @_Z18BitfieldAssignmentv + // WIN: define dso_local void @"?BitfieldAssignment@@YAXXZ" + BitFieldsByte B; + B.A = 3; + B.B = 2; + B.C = 1; + // First one is used for the lifetime start, skip that. + // CHECK: bitcast %struct.BitFieldsByte* + // CHECK: %[[BFType:.+]] = bitcast %struct.BitFieldsByte* + // CHECK: %[[LOADA:.+]] = load i8, i8* %[[BFType]] + // CHECK: %[[CLEARA:.+]] = and i8 %[[LOADA]], -8 + // CHECK: %[[SETA:.+]] = or i8 %[[CLEARA]], 3 + // CHECK: %[[BFType:.+]] = bitcast %struct.BitFieldsByte* + // CHECK: %[[LOADB:.+]] = load i8, i8* %[[BFType]] + // CHECK: %[[CLEARB:.+]] = and i8 %[[LOADB]], -57 + // CHECK: %[[SETB:.+]] = or i8 %[[CLEARB]], 16 + // CHECK: %[[BFType:.+]] = bitcast %struct.BitFieldsByte* + // CHECK: %[[LOADC:.+]] = load i8, i8* %[[BFType]] + // CHECK: %[[CLEARC:.+]] = and i8 %[[LOADC]], 63 + // CHECK: %[[SETC:.+]] = or i8 %[[CLEARC]], 64 +} + +enum AsEnumUnderlyingType : _ExtInt(9) { + A,B,C +}; + +void UnderlyingTypeUseage(AsEnumUnderlyingType Param) { + // LIN: define void @_Z20UnderlyingTypeUseage20AsEnumUnderlyingType(i9 % + // WIN: define dso_local void @"?UnderlyingTypeUseage@@YAXW4AsEnumUnderlyingType@@@Z"(i9 % + AsEnumUnderlyingType Var; + // CHECK: alloca i9, align 2 + // CHECK: store i9 %{{.*}}, align 2 +} + +unsigned _ExtInt(33) ManglingTestRetParam(unsigned _ExtInt(33) Param) { +// LIN: define i33 @_Z20ManglingTestRetParamU7_ExtIntILi33EEj(i33 % +// WIN: define dso_local i33 @"?ManglingTestRetParam@@YAU?$_UExtInt@$0CB@@__clang@@U12@@Z"(i33 + return 0; +} + +_ExtInt(33) ManglingTestRetParam(_ExtInt(33) Param) { +// LIN: define i33 @_Z20ManglingTestRetParamU7_ExtIntILi33EEi(i33 % +// WIN: define dso_local i33 @"?ManglingTestRetParam@@YAU?$_ExtInt@$0CB@@__clang@@U12@@Z"(i33 + return 0; +} + +template +void ManglingTestTemplateParam(T&); +template<_ExtInt(99) T> +void ManglingTestNTTP(); + +void ManglingInstantiator() { + // LIN: define void @_Z20ManglingInstantiatorv() + // WIN: define dso_local void @"?ManglingInstantiator@@YAXXZ"() + _ExtInt(93) A; + ManglingTestTemplateParam(A); +// LIN: call void @_Z25ManglingTestTemplateParamIU7_ExtIntILi93EEiEvRT_(i93* +// WIN: call void @"??$ManglingTestTemplateParam@U?$_ExtInt@$0FN@@__clang@@@@YAXAEAU?$_ExtInt@$0FN@@__clang@@@Z"(i93* + constexpr _ExtInt(93) B = 993; + ManglingTestNTTP<38>(); +// LIN: call void @_Z16ManglingTestNTTPILU7_ExtIntILi99EEi38EEvv() +// WIN: call void @"??$ManglingTestNTTP@$0CG@@@YAXXZ"() + ManglingTestNTTP(); +// LIN: call void @_Z16ManglingTestNTTPILU7_ExtIntILi99EEi993EEvv() +// WIN: call void @"??$ManglingTestNTTP@$0DOB@@@YAXXZ"() +} + +// TODO: UBSan, and in particular -fsanitize=signed-integer-overflow (this will break; UBSan's static data representation assumes that integer types have power-of-two sizes) + +void ShiftExtIntByConstant(_ExtInt(28) Ext) { +// LIN: define void @_Z21ShiftExtIntByConstantU7_ExtIntILi28EEi +// WIN: define dso_local void @"?ShiftExtIntByConstant@@YAXU?$_ExtInt@$0BM@@__clang@@@Z" + Ext << 7; + // CHECK: shl i28 %{{.+}}, 7 + Ext >> 7; + // CHECK: ashr i28 %{{.+}}, 7 + Ext << -7; + // CHECK: shl i28 %{{.+}}, -7 + Ext >> -7; + // CHECK: ashr i28 %{{.+}}, -7 + + // UB in C/C++, Defined in OpenCL. + Ext << 29; + // CHECK: shl i28 %{{.+}}, 29 + Ext >> 29; + // CHECK: ashr i28 %{{.+}}, 29 +} + +void ConstantShiftByExtInt(_ExtInt(28) Ext, _ExtInt(65) LargeExt) { + // LIN: define void @_Z21ConstantShiftByExtIntU7_ExtIntILi28EEiU7_ExtIntILi65EEi + // WIN: define dso_local void @"?ConstantShiftByExtInt@@YAXU?$_ExtInt@$0BM@@__clang@@U?$_ExtInt@$0EB@@2@@Z" + 10 << Ext; + // CHECK: %[[PROMO:.+]] = zext i28 %{{.+}} to i32 + // CHECK: shl i32 10, %[[PROMO]] + 10 >> Ext; + // CHECK: %[[PROMO:.+]] = zext i28 %{{.+}} to i32 + // CHECK: ashr i32 10, %[[PROMO]] + 10 << LargeExt; + // CHECK: %[[PROMO:.+]] = trunc i65 %{{.+}} to i32 + // CHECK: shl i32 10, %[[PROMO]] + 10 >> LargeExt; + // CHECK: %[[PROMO:.+]] = trunc i65 %{{.+}} to i32 + // CHECK: ashr i32 10, %[[PROMO]] +} + +void Shift(_ExtInt(28) Ext, _ExtInt(65) LargeExt, int i) { + // LIN: define void @_Z5ShiftU7_ExtIntILi28EEiU7_ExtIntILi65EEii + // WIN: define dso_local void @"?Shift@@YAXU?$_ExtInt@$0BM@@__clang@@U?$_ExtInt@$0EB@@2@H@Z" + i << Ext; + // CHECK: %[[PROMO:.+]] = zext i28 %{{.+}} to i32 + // CHECK: shl i32 {{.+}}, %[[PROMO]] + i >> Ext; + // CHECK: %[[PROMO:.+]] = zext i28 %{{.+}} to i32 + // CHECK: ashr i32 {{.+}}, %[[PROMO]] + + i << LargeExt; + // CHECK: %[[PROMO:.+]] = trunc i65 %{{.+}} to i32 + // CHECK: shl i32 {{.+}}, %[[PROMO]] + i >> LargeExt; + // CHECK: %[[PROMO:.+]] = trunc i65 %{{.+}} to i32 + // CHECK: ashr i32 {{.+}}, %[[PROMO]] + + Ext << i; + // CHECK: %[[PROMO:.+]] = trunc i32 %{{.+}} to i28 + // CHECK: shl i28 {{.+}}, %[[PROMO]] + Ext >> i; + // CHECK: %[[PROMO:.+]] = trunc i32 %{{.+}} to i28 + // CHECK: ashr i28 {{.+}}, %[[PROMO]] + + LargeExt << i; + // CHECK: %[[PROMO:.+]] = zext i32 %{{.+}} to i65 + // CHECK: shl i65 {{.+}}, %[[PROMO]] + LargeExt >> i; + // CHECK: %[[PROMO:.+]] = zext i32 %{{.+}} to i65 + // CHECK: ashr i65 {{.+}}, %[[PROMO]] + + Ext << LargeExt; + // CHECK: %[[PROMO:.+]] = trunc i65 %{{.+}} to i28 + // CHECK: shl i28 {{.+}}, %[[PROMO]] + Ext >> LargeExt; + // CHECK: %[[PROMO:.+]] = trunc i65 %{{.+}} to i28 + // CHECK: ashr i28 {{.+}}, %[[PROMO]] + + LargeExt << Ext; + // CHECK: %[[PROMO:.+]] = zext i28 %{{.+}} to i65 + // CHECK: shl i65 {{.+}}, %[[PROMO]] + LargeExt >> Ext; + // CHECK: %[[PROMO:.+]] = zext i28 %{{.+}} to i65 + // CHECK: ashr i65 {{.+}}, %[[PROMO]] +} + +// Ensure that these types don't alias the normal int types. +void TBAATest(_ExtInt(sizeof(int) * 8) ExtInt, + unsigned _ExtInt(sizeof(int) * 8) ExtUInt, + _ExtInt(6) Other) { + // CHECK-DAG: store i32 %{{.+}}, i32* %{{.+}}, align 4, !tbaa ![[EXTINT_TBAA:.+]] + // CHECK-DAG: store i32 %{{.+}}, i32* %{{.+}}, align 4, !tbaa ![[EXTINT_TBAA]] + // CHECK-DAG: store i6 %{{.+}}, i6* %{{.+}}, align 1, !tbaa ![[EXTINT6_TBAA:.+]] + ExtInt = 5; + ExtUInt = 5; + Other = 5; +} + + +// NoNewStructPathTBAA-DAG: ![[CHAR_TBAA_ROOT:.+]] = !{!"omnipotent char", ![[TBAA_ROOT:.+]], i64 0} +// NoNewStructPathTBAA-DAG: ![[TBAA_ROOT]] = !{!"Simple C++ TBAA"} +// NoNewStructPathTBAA-DAG: ![[EXTINT_TBAA]] = !{![[EXTINT_TBAA_ROOT:.+]], ![[EXTINT_TBAA_ROOT]], i64 0} +// NoNewStructPathTBAA-DAG: ![[EXTINT_TBAA_ROOT]] = !{!"_ExtInt(32)", ![[CHAR_TBAA_ROOT]], i64 0} +// NoNewStructPathTBAA-DAG: ![[EXTINT6_TBAA]] = !{![[EXTINT6_TBAA_ROOT:.+]], ![[EXTINT6_TBAA_ROOT]], i64 0} +// NoNewStructPathTBAA-DAG: ![[EXTINT6_TBAA_ROOT]] = !{!"_ExtInt(6)", ![[CHAR_TBAA_ROOT]], i64 0} + +// NewStructPathTBAA-DAG: ![[CHAR_TBAA_ROOT:.+]] = !{![[TBAA_ROOT:.+]], i64 1, !"omnipotent char"} +// NewStructPathTBAA-DAG: ![[TBAA_ROOT]] = !{!"Simple C++ TBAA"} +// NewStructPathTBAA-DAG: ![[EXTINT_TBAA]] = !{![[EXTINT_TBAA_ROOT:.+]], ![[EXTINT_TBAA_ROOT]], i64 0, i64 4} +// NewStructPathTBAA-DAG: ![[EXTINT_TBAA_ROOT]] = !{![[CHAR_TBAA_ROOT]], i64 4, !"_ExtInt(32)"} +// NewStructPathTBAA-DAG: ![[EXTINT6_TBAA]] = !{![[EXTINT6_TBAA_ROOT:.+]], ![[EXTINT6_TBAA_ROOT]], i64 0, i64 1} +// NewStructPathTBAA-DAG: ![[EXTINT6_TBAA_ROOT]] = !{![[CHAR_TBAA_ROOT]], i64 1, !"_ExtInt(6)"} Index: clang/test/CodeGenCXX/debug-info-template-partial-specialization.cpp =================================================================== --- clang/test/CodeGenCXX/debug-info-template-partial-specialization.cpp +++ clang/test/CodeGenCXX/debug-info-template-partial-specialization.cpp @@ -17,7 +17,7 @@ // Test that the bool template type parameter is emitted. // // CHECK: ![[PARAMS]] = !{!{{.*}}, !{{.*}}, ![[FALSE:[0-9]+]]} - // CHECK: ![[FALSE]] = !DITemplateValueParameter(type: !{{[0-9]+}}, value: i8 0) + // CHECK: ![[FALSE]] = !DITemplateValueParameter(type: !{{[0-9]+}}, value: i1 false) typedef typename __pointer_type_imp::__pointer_type1<_Tp, _Dp, false>::type type; }; template struct default_delete {}; Index: clang/test/CodeGenOpenCL/ext-int-shift.cl =================================================================== --- /dev/null +++ clang/test/CodeGenOpenCL/ext-int-shift.cl @@ -0,0 +1,21 @@ +// RUN: %clang -cc1 -triple x86_64-linux-pc -O3 -disable-llvm-passes %s -emit-llvm -o - | FileCheck %s + +void Shifts(_ExtInt(12) E, int i) { + E << 99; + // CHECK: shl i12 %{{.+}}, 3 + + 77 << E; + // CHECK: %[[PROM:.+]] = zext i12 %{{.+}} to i32 + // CHECK: %[[MASK:.+]] = and i32 %[[PROM]], 31 + // CHECK: shl i32 77, %[[MASK]] + + E << i; + // CHECK: %[[PROM:.+]] = trunc i32 %{{.+}} to i12 + // CHECK: %[[MASK:.+]] = urem i12 %[[PROM]], 12 + // CHECK: shl i12 %{{.+}}, %[[MASK]] + + i << E; + // CHECK: %[[PROM:.+]] = zext i12 %{{.+}} to i32 + // CHECK: %[[MASK:.+]] = and i32 %[[PROM]], 31 + // CHECK: shl i32 %{{.+}}, %[[MASK]] +} Index: clang/test/SemaCXX/ext-int.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/ext-int.cpp @@ -0,0 +1,236 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +template +struct HasExtInt { + _ExtInt(Bounds) b; + unsigned _ExtInt(Bounds) b2; +}; + +// Delcaring variables: +_ExtInt(33) Declarations(_ExtInt(48) &Param) { // Useable in params and returns. + short _ExtInt(43) a; // expected-error {{'short _ExtInt' is invalid}} + _ExtInt(43) long b; // expected-error {{'long _ExtInt' is invalid}} + + // These should all be fine: + const _ExtInt(5) c = 3; + const unsigned _ExtInt(5) d; // expected-error {{default initialization of an object of const type 'const unsigned _ExtInt(5)'}} + unsigned _ExtInt(5) e = 5; + _ExtInt(5) unsigned f; + + _ExtInt(-3) g; // expected-error{{signed _ExtInt must have a size of at least 2}} + _ExtInt(0) h; // expected-error{{signed _ExtInt must have a size of at least 2}} + _ExtInt(1) i; // expected-error{{signed _ExtInt must have a size of at least 2}} + _ExtInt(2) j;; + unsigned _ExtInt(0) k;// expected-error{{unsigned _ExtInt must have a size of at least 1}} + unsigned _ExtInt(1) l; + signed _ExtInt(1) m; // expected-error{{signed _ExtInt must have a size of at least 2}} + + constexpr _ExtInt(6) n = 33; // expected-warning{{implicit conversion from 'int' to 'const _ExtInt(6)' changes value from 33 to -31}} + constexpr _ExtInt(7) o = 33; + + // Check LLVM imposed max size. + _ExtInt(0xFFFFFFFFFF) p; // expected-error {{signed _ExtInt of sizes greater than 16777215 not supported}} + unsigned _ExtInt(0xFFFFFFFFFF) q; // expected-error {{unsigned _ExtInt of sizes greater than 16777215 not supported}} + +// Ensure template params are instantiated correctly. + // expected-error@5{{signed _ExtInt must have a size of at least 2}} + // expected-error@6{{unsigned _ExtInt must have a size of at least 1}} + // expected-note@+1{{in instantiation of template class }} + HasExtInt<-1> r; + // expected-error@5{{signed _ExtInt must have a size of at least 2}} + // expected-error@6{{unsigned _ExtInt must have a size of at least 1}} + // expected-note@+1{{in instantiation of template class }} + HasExtInt<0> s; + // expected-error@5{{signed _ExtInt must have a size of at least 2}} + // expected-note@+1{{in instantiation of template class }} + HasExtInt<1> t; + HasExtInt<2> u; + + _ExtInt(-3.0) v; // expected-error {{integral constant expression must have integral or unscoped enumeration type, not 'double'}} + _ExtInt(3.0) x; // expected-error {{integral constant expression must have integral or unscoped enumeration type, not 'double'}} + + return 0; +} + +template <_ExtInt(5) I> +struct ExtIntTemplParam { + static constexpr _ExtInt(5) Var = I; +}; + +template +void deduced_whole_type(T){} +template +void deduced_bound(_ExtInt(I)){} + +// Ensure ext-int can be used in template places. +void Templates() { + ExtIntTemplParam<13> a; + constexpr _ExtInt(3) b = 1; + ExtIntTemplParam c; + constexpr _ExtInt(9) d = 1; + ExtIntTemplParam e; + + deduced_whole_type(b); + deduced_bound(b); +} + +template +struct is_same { + static constexpr bool value = false; +}; +template +struct is_same { + static constexpr bool value = true; +}; + +// Reject vector types: +// expected-error@+1{{invalid vector element type '_ExtInt(32)'}} +typedef _ExtInt(32) __attribute__((vector_size(16))) VecTy; + +// Reject _Complex: +// expected-error@+1{{'_Complex _ExtInt' is invalid}} +_Complex _ExtInt(3) Cmplx; + +// Test result types of Unary/Bitwise/Binary Operations: +void Ops() { + _ExtInt(43) x43_s = 1, y43_s = 1; + _ExtInt(sizeof(int) * 8) x32_s = 1, y32_s = 1; + unsigned _ExtInt(sizeof(unsigned) * 8) x32_u = 1, y32_u = 1; + _ExtInt(4) x4_s = 1, y4_s = 1; + unsigned _ExtInt(43) x43_u = 1, y43_u = 1; + unsigned _ExtInt(4) x4_u = 1, y4_u = 1; + int x_int = 1, y_int = 1; + unsigned x_uint = 1, y_uint = 1; + bool b; + + // Same size/sign ops don't change type. + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + + // Same size/diff sign goes to unsigned. + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + + // Different size prefers largest. + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + + // When mixed with standard types, largest prefered, then standard types + // > ext_int. + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + + static_assert(is_same::value,""); + static_assert(is_same::value,""); + + // bool was promoted to int, so rules apply same as above. + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + + // More spot checking with bitwise ops. + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same> 1), _ExtInt(4)>::value,""); + static_assert(is_same::value,""); + + // Unary ops shouldn't go through integer promotions. + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + static_assert(is_same::value,""); + // expected-warning@+1{{expression with side effects has no effect in an unevaluated context}} + static_assert(is_same::value,""); + // expected-warning@+1{{expression with side effects has no effect in an unevaluated context}} + static_assert(is_same::value,""); + // expected-warning@+1{{expression with side effects has no effect in an unevaluated context}} + static_assert(is_same::value,""); + // expected-warning@+1{{expression with side effects has no effect in an unevaluated context}} + static_assert(is_same::value,""); + + static_assert(sizeof(x43_s) == 8, ""); + static_assert(sizeof(x4_s) == 1, ""); + + static_assert(sizeof(_ExtInt(3340)) == 424, ""); // 424 * 8 == 3392. + static_assert(sizeof(_ExtInt(1049)) == 136, ""); // 136 * 8 == 1088. + + // compassign works too. + // expected-warning@+1{{expression with side effects has no effect in an unevaluated context}} + static_assert(is_same::value, ""); + + // Comparisons work the same as ints. + static_assert(is_same 33), bool>::value, ""); + static_assert(is_same 33), bool>::value, ""); +} + +// Useable as an underlying type. +enum AsEnumUnderlyingType : _ExtInt(33) { +}; + +void overloaded(int); +void overloaded(_ExtInt(32)); +void overloaded(_ExtInt(33)); +void overloaded(short); +//expected-note@+1{{candidate function}} +void overloaded2(_ExtInt(32)); +//expected-note@+1{{candidate function}} +void overloaded2(_ExtInt(33)); +//expected-note@+1{{candidate function}} +void overloaded2(short); + +void overload_use() { + int i; + _ExtInt(32) i32; + _ExtInt(33) i33; + short s; + + // All of these get their corresponding exact matches. + overloaded(i); + overloaded(i32); + overloaded(i33); + overloaded(s); + + overloaded2(i); // expected-error{{call to 'overloaded2' is ambiguous}} + + overloaded2(i32); + + overloaded2(s); +} + +// no errors expected, this should 'just work'. +struct UsedAsBitField { + _ExtInt(3) F : 3; + _ExtInt(3) G : 3; + _ExtInt(3) H : 3; +}; + +// Note, the extra closing brackets confuses the parser, so this test should likely be last. +// expected-error@+5{{expected ')'}} +// expected-note@+4{{to match this '('}} +// expected-error@+3{{expected unqualified-id}} +// expected-error@+2{{extraneous closing brace}} +// expected-error@+1{{C++ requires a type specifier for all declarations}} +_ExtInt(32} a; +// expected-error@+2{{expected expression}} +// expected-error@+1{{C++ requires a type specifier for all declarations}} +_ExtInt(32* ) b; +// expected-error@+3{{expected '('}} +// expected-error@+2{{expected unqualified-id}} +// expected-error@+1{{C++ requires a type specifier for all declarations}} +_ExtInt{32} c; + Index: clang/tools/libclang/CIndex.cpp =================================================================== --- clang/tools/libclang/CIndex.cpp +++ clang/tools/libclang/CIndex.cpp @@ -1804,6 +1804,8 @@ DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParm, Type) DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParmPack, Type) DEFAULT_TYPELOC_IMPL(Auto, Type) +DEFAULT_TYPELOC_IMPL(ExtInt, Type) +DEFAULT_TYPELOC_IMPL(DependentExtInt, Type) bool CursorVisitor::VisitCXXRecordDecl(CXXRecordDecl *D) { // Visit the nested-name-specifier, if present.