diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3461,3 +3461,56 @@ ``__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 rank 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. + +Extended integer types work with the C _Atomic type modifier, however only precisions +that are powers-of-2 greater than 8 bit are accepted. + +Extended integer types align with existing calling conventions. They have the same size +and alignment as the smallest basic type that can contain them. Types that are larger +than 64 bits are handled in the same way as _int128 is handled; they are conceptually +treated as struct of register size chunks. They number of chunks are the smallest +number that can contain the types which does not necessarily mean a power-of-2 size. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -62,6 +62,14 @@ in the Arm C Language Extensions. +* 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' usage, 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 ------------------ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -224,6 +224,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; @@ -1203,6 +1205,14 @@ /// Return a write_only pipe type for the specified type. QualType getWritePipeType(QualType T) const; + /// Return an extended integer type with the specified signedness and bit + /// count. + QualType getExtIntType(bool Unsigned, unsigned NumBits) const; + + /// Return a dependent extended integer type with the specified signedness 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; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/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 ----------------- @@ -1385,6 +1389,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 ----------------- diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2101,6 +2101,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 @@ -6127,6 +6128,64 @@ bool isReadOnly() const { return isRead; } }; +/// A fixed int type of a specified bitwidth. +class ExtIntType final : public Type, public llvm::FoldingSetNode { + friend class ASTContext; + unsigned IsUnsigned : 1; + unsigned NumBits : 24; + +protected: + ExtIntType(bool isUnsigned, unsigned NumBits); + +public: + bool isUnsigned() const { return IsUnsigned; } + bool isSigned() 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 final : public Type, public llvm::FoldingSetNode { + friend class ASTContext; + const ASTContext &Context; + llvm::PointerIntPair ExprAndUnsigned; + +protected: + DependentExtIntType(const ASTContext &Context, bool IsUnsigned, + Expr *NumBits); + +public: + bool isUnsigned() const; + bool isSigned() const { return !isUnsigned(); } + Expr *getNumBitsExpr() const; + + 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: @@ -6646,6 +6705,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); \ @@ -6741,7 +6804,7 @@ return IsEnumDeclComplete(ET->getDecl()) && !IsEnumDeclScoped(ET->getDecl()); } - return false; + return isExtIntType(); } inline bool Type::isFixedPointType() const { @@ -6798,7 +6861,8 @@ isa(CanonicalType) || isa(CanonicalType) || isa(CanonicalType) || - isa(CanonicalType); + isa(CanonicalType) || + isExtIntType(); } inline bool Type::isIntegralOrEnumerationType() const { @@ -6811,7 +6875,7 @@ if (const auto *ET = dyn_cast(CanonicalType)) return IsEnumDeclComplete(ET->getDecl()); - return false; + return isExtIntType(); } inline bool Type::isBooleanType() const { diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -2450,6 +2450,12 @@ } return Cur.getAs(); } +class ExtIntTypeLoc final + : public InheritingConcreteTypeLoc {}; +class DependentExtIntTypeLoc final + : public InheritingConcreteTypeLoc {}; } // namespace clang diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -833,3 +833,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); + }]>; +} diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5947,10 +5947,12 @@ "non-void block should return a value">; def err_func_def_incomplete_result : Error< "incomplete result type %0 in function definition">; -def err_atomic_specifier_bad_type : Error< - "_Atomic cannot be applied to " - "%select{incomplete |array |function |reference |atomic |qualified |sizeless |}0type " - "%1 %select{|||||||which is not trivially copyable}0">; +def err_atomic_specifier_bad_type + : Error<"_Atomic cannot be applied to " + "%select{incomplete |array |function |reference |atomic |qualified " + "|sizeless ||integer |integer }0type " + "%1 %select{|||||||which is not trivially copyable|with less than " + "1 byte of precision|with a non power of 2 precision}0">; // Expressions. def select_unary_expr_or_type_trait_kind : TextSubstitution< @@ -10711,4 +10713,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 bit size of at least %select{2|1}0">; +def err_ext_int_max_size : Error<"%select{signed|unsigned}0 _ExtInt of bit " + "sizes greater than %1 not supported">; } // end of sema component. diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h --- a/clang/include/clang/Basic/Specifiers.h +++ b/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 diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/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) diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td --- a/clang/include/clang/Basic/TypeNodes.td +++ b/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; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2721,6 +2721,7 @@ SourceLocation &EllipsisLoc); void ParseAlignmentSpecifier(ParsedAttributes &Attrs, SourceLocation *endLoc = nullptr); + ExprResult ParseExtIntegerArgument(); VirtSpecifiers::Specifier isCXX11VirtSpecifier(const Token &Tok) const; VirtSpecifiers::Specifier isCXX11VirtSpecifier() const { diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/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); @@ -704,6 +705,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(); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1678,6 +1678,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); diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/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 diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2180,6 +2180,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); @@ -3376,6 +3385,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 @@ -4070,6 +4081,39 @@ 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); + + auto *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); + + auto *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; @@ -5905,6 +5949,11 @@ 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: @@ -7288,6 +7337,7 @@ return; case Type::Pipe: + case Type::ExtInt: #define ABSTRACT_TYPE(KIND, BASE) #define TYPE(KIND, BASE) #define DEPENDENT_TYPE(KIND, BASE) \ @@ -9381,6 +9431,21 @@ assert(LHS != RHS && "Equivalent pipe types should have already been handled!"); return {}; + case Type::ExtInt: { + // Merge two ext-int types, while trying to preserve typedef info. + bool LHSUnsigned = LHS->castAs()->isUnsigned(); + bool RHSUnsigned = RHS->castAs()->isUnsigned(); + unsigned LHSBits = LHS->castAs()->getNumBits(); + unsigned RHSBits = RHS->castAs()->getNumBits(); + + // Like unsigned/int, shouldn't have a type if they dont match. + if (LHSUnsigned != RHSUnsigned) + return {}; + + if (LHSBits != RHSBits) + return {}; + return LHS; + } } llvm_unreachable("Invalid Type::Class!"); @@ -9521,6 +9586,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); } diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -949,6 +949,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; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -10354,6 +10354,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; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2093,6 +2093,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: @@ -3551,6 +3553,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 diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -2953,6 +2953,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 diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -293,6 +293,39 @@ VectorTypeBits.NumElements = nElements; } +ExtIntType::ExtIntType(bool IsUnsigned, unsigned NumBits) + : Type(ExtInt, QualType{}, TypeDependence::None), IsUnsigned(IsUnsigned), + NumBits(NumBits) {} + +DependentExtIntType::DependentExtIntType(const ASTContext &Context, + bool IsUnsigned, Expr *NumBitsExpr) + : Type(DependentExtInt, QualType{}, + ((NumBitsExpr->isValueDependent() || NumBitsExpr->isTypeDependent()) + ? TypeDependence::Dependent + : TypeDependence::None) | + (NumBitsExpr->isInstantiationDependent() + ? TypeDependence::Instantiation + : TypeDependence::None) | + (NumBitsExpr->containsUnexpandedParameterPack() + ? TypeDependence::VariablyModified + : TypeDependence::None)), + Context(Context), ExprAndUnsigned(NumBitsExpr, IsUnsigned) {} + +bool DependentExtIntType::isUnsigned() const { + return ExprAndUnsigned.getInt(); +} + +clang::Expr *DependentExtIntType::getNumBitsExpr() const { + return ExprAndUnsigned.getPointer(); +} + +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. @@ -1836,13 +1869,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(); } @@ -1923,6 +1960,9 @@ return ET->getDecl()->getIntegerType()->isSignedIntegerType(); } + if (const ExtIntType *IT = dyn_cast(CanonicalType)) + return IT->isSigned(); + return false; } @@ -1937,6 +1977,10 @@ return ET->getDecl()->getIntegerType()->isSignedIntegerType(); } + if (const ExtIntType *IT = dyn_cast(CanonicalType)) + return IT->isSigned(); + + return false; } @@ -1963,6 +2007,9 @@ return ET->getDecl()->getIntegerType()->isUnsignedIntegerType(); } + if (const ExtIntType *IT = dyn_cast(CanonicalType)) + return IT->isUnsigned(); + return false; } @@ -1977,6 +2024,9 @@ return ET->getDecl()->getIntegerType()->isUnsignedIntegerType(); } + if (const ExtIntType *IT = dyn_cast(CanonicalType)) + return IT->isUnsigned(); + return false; } @@ -2015,7 +2065,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 { @@ -2030,7 +2080,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 { @@ -2059,6 +2109,8 @@ if (CT->getElementType()->isRealFloatingType()) return STK_FloatingComplex; return STK_IntegralComplex; + } else if (isExtIntType()) { + return STK_Integral; } llvm_unreachable("unknown scalar type"); @@ -2224,6 +2276,7 @@ case Type::MemberPointer: case Type::Vector: case Type::ExtVector: + case Type::ExtInt: return true; case Type::Enum: @@ -3643,6 +3696,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: @@ -3740,6 +3794,7 @@ assert(T->isInstantiationDependentType()); return LinkageInfo::external(); + case Type::ExtInt: case Type::Builtin: return LinkageInfo::external(); @@ -3948,6 +4003,8 @@ case Type::ObjCInterface: case Type::Atomic: case Type::Pipe: + case Type::ExtInt: + case Type::DependentExtInt: return false; } llvm_unreachable("bad type kind!"); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/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; @@ -1114,6 +1116,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; diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -168,6 +168,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, diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -826,6 +826,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; @@ -3159,6 +3170,8 @@ case Type::Atomic: return CreateType(cast(Ty), Unit); + case Type::ExtInt: + return CreateType(cast(Ty)); case Type::Pipe: return CreateType(cast(Ty), Unit); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -760,6 +760,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); @@ -3770,6 +3775,21 @@ return llvm::ConstantInt::get(RHS->getType(), Ty->getBitWidth() - 1); } +Value *ScalarExprEmitter::ConstrainShiftValue(Value *LHS, Value *RHS, + const Twine &Name) { + llvm::IntegerType *Ty; + if (auto *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. @@ -3784,8 +3804,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); @@ -3847,8 +3866,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); diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/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) { diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -257,6 +257,7 @@ case Type::Enum: case Type::ObjCObjectPointer: case Type::Pipe: + case Type::ExtInt: return TEK_Scalar; // Complexes. @@ -2010,6 +2011,7 @@ case Type::ObjCObject: case Type::ObjCInterface: case Type::ObjCObjectPointer: + case Type::ExtInt: llvm_unreachable("type class is never variably-modified!"); case Type::Adjusted: diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp --- a/clang/lib/CodeGen/CodeGenTBAA.cpp +++ b/clang/lib/CodeGen/CodeGenTBAA.cpp @@ -209,6 +209,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(); } diff --git a/clang/lib/CodeGen/CodeGenTypes.h b/clang/lib/CodeGen/CodeGenTypes.h --- a/clang/lib/CodeGen/CodeGenTypes.h +++ b/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); diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/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 { @@ -731,6 +731,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?"); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3219,6 +3219,7 @@ llvm_unreachable("Pipe types shouldn't get here"); case Type::Builtin: + case Type::ExtInt: // GCC treats vector and complex types as fundamental types. case Type::Vector: case Type::ExtVector: @@ -3472,7 +3473,10 @@ llvm_unreachable("Undeduced type shouldn't get here"); case Type::Pipe: - llvm_unreachable("Pipe type shouldn't get here"); + break; + + case Type::ExtInt: + break; case Type::ConstantArray: case Type::IncompleteArray: diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/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 @@ -3807,6 +3826,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); @@ -4890,6 +4917,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: @@ -4969,6 +4997,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: @@ -5135,6 +5164,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: diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1492,6 +1492,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: diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2156,6 +2156,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); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1141,6 +1141,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: @@ -1778,6 +1779,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; } @@ -1810,6 +1829,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: diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -360,6 +360,7 @@ case TST_half: case TST_int: case TST_int128: + case TST_extint: case TST_struct: case TST_interface: case TST_union: @@ -538,6 +539,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"; @@ -913,6 +915,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 @@ -1194,7 +1217,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. @@ -1241,7 +1264,8 @@ S.getLocForEndOfToken(getTypeSpecComplexLoc()), " double"); TypeSpecType = TST_double; // _Complex -> _Complex double. - } else if (TypeSpecType == TST_int || TypeSpecType == TST_char) { + } else if (TypeSpecType == TST_int || TypeSpecType == TST_char || + TypeSpecType == TST_extint) { // Note that this intentionally doesn't include _Complex _Bool. if (!S.getLangOpts().CPlusPlus) S.Diag(TSTLoc, diag::ext_integer_complex); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -9793,6 +9793,9 @@ false/*NonNegative*/); } + if (const auto *EIT = dyn_cast(T)) + return IntRange(EIT->getNumBits(), EIT->isUnsigned()); + const BuiltinType *BT = cast(T); assert(BT->isInteger()); @@ -9816,6 +9819,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()); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14811,12 +14811,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. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4087,8 +4087,9 @@ Diag(AttrLoc, diag::err_enum_mode_vector_type) << Name << CI.getRange(); return; } - bool IntegralOrAnyEnumType = - OldElemTy->isIntegralOrEnumerationType() || OldElemTy->getAs(); + bool IntegralOrAnyEnumType = (OldElemTy->isIntegralOrEnumerationType() && + !OldElemTy->isExtIntType()) || + OldElemTy->getAs(); if (!OldElemTy->getAs() && !OldElemTy->isComplexType() && !IntegralOrAnyEnumType) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1482,6 +1482,11 @@ if (LHSType == RHSType) return LHSType; + // ExtInt types aren't subject to conversions between them or normal integers, + // so this fails. + if(LHSType->isExtIntType() || RHSType->isExtIntType()) + return QualType(); + // At this point, we have two different arithmetic types. // Diagnose attempts to convert between __float128 and long double where @@ -4261,6 +4266,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(); @@ -10431,14 +10437,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; diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -2967,6 +2967,7 @@ case Type::Vector: case Type::ExtVector: case Type::Complex: + case Type::ExtInt: break; // Non-deduced auto types only get here for error cases. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5998,6 +5998,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(), @@ -6891,7 +6900,9 @@ QualType IntegerType = ParamType; if (const EnumType *Enum = IntegerType->getAs()) IntegerType = Enum->getDecl()->getIntegerType(); - Value = Value.extOrTrunc(Context.getTypeSize(IntegerType)); + Value = Value.extOrTrunc(IntegerType->isExtIntType() + ? Context.getIntWidth(IntegerType) + : Context.getTypeSize(IntegerType)); Converted = TemplateArgument(Context, Value, Context.getCanonicalType(ParamType)); @@ -6985,7 +6996,9 @@ // 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 = IntegerType->isExtIntType() + ? Context.getIntWidth(IntegerType) + : Context.getTypeSize(IntegerType); if (Value.getBitWidth() != AllowedBits) Value = Value.extOrTrunc(AllowedBits); Value.setIsSigned(IntegerType->isSignedIntegerOrEnumerationType()); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/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 = dyn_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 = dyn_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: @@ -5850,6 +5878,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: @@ -5862,6 +5895,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) diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/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; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/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; @@ -1441,6 +1442,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: @@ -2160,6 +2170,45 @@ return Context.getWritePipeType(T); } +/// Build a extended int type. +/// +/// \param IsUnsigned Boolean representing the signedness of the 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) { @@ -5774,6 +5823,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()); @@ -5900,6 +5957,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); } @@ -8631,6 +8691,12 @@ else if (!T.isTriviallyCopyableType(Context)) // Some other non-trivially-copyable type (probably a C++ class) DisallowedKind = 7; + else if (auto *ExtTy = T->getAs()) { + if (ExtTy->getNumBits() < 8) + DisallowedKind = 8; + else if (!llvm::isPowerOf2_32(ExtTy->getNumBits())) + DisallowedKind = 9; + } if (DisallowedKind != -1) { Diag(Loc, diag::err_atomic_specifier_bad_type) << DisallowedKind << T; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1183,6 +1183,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. @@ -6120,6 +6128,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. /// @@ -13782,6 +13841,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, diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6719,6 +6719,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()) diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/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; diff --git a/clang/test/CodeGen/ext-int-sanitizer.cpp b/clang/test/CodeGen/ext-int-sanitizer.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/ext-int-sanitizer.cpp @@ -0,0 +1,265 @@ +// 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 / 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 +} diff --git a/clang/test/CodeGen/ext-int.c b/clang/test/CodeGen/ext-int.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/ext-int.c @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -triple x86_64-gnu-linux -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK +// RUN: %clang_cc1 -triple x86_64-windows-pc -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK + + +void GenericTest(_ExtInt(3) a, unsigned _ExtInt(3) b, _ExtInt(4) c) { + // CHECK: define {{.*}}void @GenericTest + int which = _Generic(a, _ExtInt(3): 1, unsigned _ExtInt(3) : 2, _ExtInt(4) : 3); + // CHECK: store i32 1 + int which2 = _Generic(b, _ExtInt(3): 1, unsigned _ExtInt(3) : 2, _ExtInt(4) : 3); + // CHECK: store i32 2 + int which3 = _Generic(c, _ExtInt(3): 1, unsigned _ExtInt(3) : 2, _ExtInt(4) : 3); + // CHECK: store i32 3 +} + +void VLATest(_ExtInt(3) A, _ExtInt(99) B, _ExtInt(123456) C) { + // CHECK: define {{.*}}void @VLATest + int AR1[A]; + // CHECK: %[[A:.+]] = zext i3 %{{.+}} to i64 + // CHECK: %[[VLA1:.+]] = alloca i32, i64 %[[A]] + int AR2[B]; + // CHECK: %[[B:.+]] = trunc i99 %{{.+}} to i64 + // CHECK: %[[VLA2:.+]] = alloca i32, i64 %[[B]] + int AR3[C]; + // CHECK: %[[C:.+]] = trunc i123456 %{{.+}} to i64 + // CHECK: %[[VLA3:.+]] = alloca i32, i64 %[[C]] +} + +struct S { + _ExtInt(17) A; + _ExtInt(16777200) B; + _ExtInt(17) C; +}; + +void OffsetOfTest() { + // CHECK: define {{.*}}void @OffsetOfTest + int A = __builtin_offsetof(struct S,A); + // CHECK: store i32 0, i32* %{{.+}} + int B = __builtin_offsetof(struct S,B); + // CHECK: store i32 8, i32* %{{.+}} + int C = __builtin_offsetof(struct S,C); + // CHECK: store i32 2097160, i32* %{{.+}} +} + + diff --git a/clang/test/CodeGenCXX/ext-int.cpp b/clang/test/CodeGenCXX/ext-int.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/ext-int.cpp @@ -0,0 +1,432 @@ +// RUN: %clang_cc1 -triple x86_64-gnu-linux -O3 -disable-llvm-passes -I%S -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,LIN,NoNewStructPathTBAA +// RUN: %clang_cc1 -triple x86_64-gnu-linux -O3 -disable-llvm-passes -I%S -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 -I%S -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WIN,NoNewStructPathTBAA +// RUN: %clang_cc1 -triple x86_64-windows-pc -O3 -disable-llvm-passes -I%S -new-struct-path-tbaa -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WIN,NewStructPathTBAA + +#include + +// 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 UnderlyingTypeUsage(AsEnumUnderlyingType Param) { + // LIN: define void @_Z19UnderlyingTypeUsage20AsEnumUnderlyingType(i9 % + // WIN: define dso_local void @"?UnderlyingTypeUsage@@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"() +} + +void TakesVarargs(int i, ...) { + // LIN: define void @_Z12TakesVarargsiz(i32 %i, ...) + // WIN: define dso_local void @"?TakesVarargs@@YAXHZZ"(i32 %i, ...) + + __builtin_va_list args; + // LIN: %[[ARGS:.+]] = alloca [1 x %struct.__va_list_tag] + // WIN: %[[ARGS:.+]] = alloca i8* + __builtin_va_start(args, i); + // LIN: %[[STARTAD:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %[[ARGS]] + // LIN: %[[STARTAD1:.+]] = bitcast %struct.__va_list_tag* %[[STARTAD]] to i8* + // LIN: call void @llvm.va_start(i8* %[[STARTAD1]]) + // WIN: %[[ARGSLLIFETIMESTART:.+]] = bitcast i8** %[[ARGS]] to i8* + // WIN: %[[ARGSSTART:.+]] = bitcast i8** %[[ARGS]] to i8* + // WIN: call void @llvm.va_start(i8* %[[ARGSSTART]]) + + _ExtInt(92) A = __builtin_va_arg(args, _ExtInt(92)); + // LIN: %[[AD1:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %[[ARGS]] + // LIN: %[[OFA_P1:.+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %[[AD1]], i32 0, i32 2 + // LIN: %[[OFA1:.+]] = load i8*, i8** %[[OFA_P1]] + // LIN: %[[BC1:.+]] = bitcast i8* %[[OFA1]] to i92* + // LIN: %[[OFANEXT1:.+]] = getelementptr i8, i8* %[[OFA1]], i32 16 + // LIN: store i8* %[[OFANEXT1]], i8** %[[OFA_P1]] + // LIN: %[[LOAD1:.+]] = load i92, i92* %[[BC1]] + // LIN: store i92 %[[LOAD1]], i92* + // WIN: %[[CUR1:.+]] = load i8*, i8** %[[ARGS]] + // WIN: %[[NEXT1:.+]] = getelementptr inbounds i8, i8* %[[CUR1]], i64 16 + // WIN: store i8* %[[NEXT1]], i8** %[[ARGS]] + // WIN: %[[BC1:.+]] = bitcast i8* %[[CUR1]] to i92* + // WIN: %[[LOADV1:.+]] = load i92, i92* %[[BC1]] + // WIN: store i92 %[[LOADV1]], i92* + + _ExtInt(31) B = __builtin_va_arg(args, _ExtInt(31)); + // LIN: %[[AD2:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %[[ARGS]] + // LIN: %[[OFA_P2:.+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %[[AD2]], i32 0, i32 2 + // LIN: %[[OFA2:.+]] = load i8*, i8** %[[OFA_P2]] + // LIN: %[[BC2:.+]] = bitcast i8* %[[OFA2]] to i31* + // LIN: %[[OFANEXT2:.+]] = getelementptr i8, i8* %[[OFA2]], i32 8 + // LIN: store i8* %[[OFANEXT2]], i8** %[[OFA_P2]] + // LIN: %[[LOAD2:.+]] = load i31, i31* %[[BC2]] + // LIN: store i31 %[[LOAD2]], i31* + // WIN: %[[CUR2:.+]] = load i8*, i8** %[[ARGS]] + // WIN: %[[NEXT2:.+]] = getelementptr inbounds i8, i8* %[[CUR2]], i64 8 + // WIN: store i8* %[[NEXT2]], i8** %[[ARGS]] + // WIN: %[[BC2:.+]] = bitcast i8* %[[CUR2]] to i31* + // WIN: %[[LOADV2:.+]] = load i31, i31* %[[BC2]] + // WIN: store i31 %[[LOADV2]], i31* + + _ExtInt(16) C = __builtin_va_arg(args, _ExtInt(16)); + // LIN: %[[AD3:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %[[ARGS]] + // LIN: %[[OFA_P3:.+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %[[AD3]], i32 0, i32 2 + // LIN: %[[OFA3:.+]] = load i8*, i8** %[[OFA_P3]] + // LIN: %[[BC3:.+]] = bitcast i8* %[[OFA3]] to i16* + // LIN: %[[OFANEXT3:.+]] = getelementptr i8, i8* %[[OFA3]], i32 8 + // LIN: store i8* %[[OFANEXT3]], i8** %[[OFA_P3]] + // LIN: %[[LOAD3:.+]] = load i16, i16* %[[BC3]] + // LIN: store i16 %[[LOAD3]], i16* + // WIN: %[[CUR3:.+]] = load i8*, i8** %[[ARGS]] + // WIN: %[[NEXT3:.+]] = getelementptr inbounds i8, i8* %[[CUR3]], i64 8 + // WIN: store i8* %[[NEXT3]], i8** %[[ARGS]] + // WIN: %[[BC3:.+]] = bitcast i8* %[[CUR3]] to i16* + // WIN: %[[LOADV3:.+]] = load i16, i16* %[[BC3]] + // WIN: store i16 %[[LOADV3]], i16* + + _ExtInt(129) D = __builtin_va_arg(args, _ExtInt(129)); + // LIN: %[[AD4:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %[[ARGS]] + // LIN: %[[OFA_P4:.+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %[[AD4]], i32 0, i32 2 + // LIN: %[[OFA4:.+]] = load i8*, i8** %[[OFA_P4]] + // LIN: %[[BC4:.+]] = bitcast i8* %[[OFA4]] to i129* + // LIN: %[[OFANEXT4:.+]] = getelementptr i8, i8* %[[OFA4]], i32 24 + // LIN: store i8* %[[OFANEXT4]], i8** %[[OFA_P4]] + // LIN: %[[LOAD4:.+]] = load i129, i129* %[[BC4]] + // LIN: store i129 %[[LOAD4]], i129* + // WIN: %[[CUR4:.+]] = load i8*, i8** %[[ARGS]] + // WIN: %[[NEXT4:.+]] = getelementptr inbounds i8, i8* %[[CUR4]], i64 24 + // WIN: store i8* %[[NEXT4]], i8** %[[ARGS]] + // WIN: %[[BC4:.+]] = bitcast i8* %[[CUR4]] to i129* + // WIN: %[[LOADV4:.+]] = load i129, i129* %[[BC4]] + // WIN: store i129 %[[LOADV4]], i129* + + _ExtInt(16777200) E = __builtin_va_arg(args, _ExtInt(16777200)); + // LIN: %[[AD5:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %[[ARGS]] + // LIN: %[[OFA_P5:.+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %[[AD5]], i32 0, i32 2 + // LIN: %[[OFA5:.+]] = load i8*, i8** %[[OFA_P5]] + // LIN: %[[BC5:.+]] = bitcast i8* %[[OFA5]] to i16777200* + // LIN: %[[OFANEXT5:.+]] = getelementptr i8, i8* %[[OFA5]], i32 2097152 + // LIN: store i8* %[[OFANEXT5]], i8** %[[OFA_P5]] + // LIN: %[[LOAD5:.+]] = load i16777200, i16777200* %[[BC5]] + // LIN: store i16777200 %[[LOAD5]], i16777200* + // WIN: %[[CUR5:.+]] = load i8*, i8** %[[ARGS]] + // WIN: %[[NEXT5:.+]] = getelementptr inbounds i8, i8* %[[CUR5]], i64 2097152 + // WIN: store i8* %[[NEXT5]], i8** %[[ARGS]] + // WIN: %[[BC5:.+]] = bitcast i8* %[[CUR5]] to i16777200* + // WIN: %[[LOADV5:.+]] = load i16777200, i16777200* %[[BC5]] + // WIN: store i16777200 %[[LOADV5]], i16777200* + + __builtin_va_end(args); + // LIN: %[[ENDAD:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %[[ARGS]] + // LIN: %[[ENDAD1:.+]] = bitcast %struct.__va_list_tag* %[[ENDAD]] to i8* + // LIN: call void @llvm.va_end(i8* %[[ENDAD1]]) + // WIN: %[[ARGSEND:.+]] = bitcast i8** %[[ARGS]] to i8* + // WIN: call void @llvm.va_end(i8* %[[ARGSEND]]) +} +void typeid_tests() { + // LIN: define void @_Z12typeid_testsv() + // WIN: define dso_local void @"?typeid_tests@@YAXXZ"() + unsigned _ExtInt(33) U33_1, U33_2; + _ExtInt(33) S33_1, S33_2; + _ExtInt(32) S32_1, S32_2; + + auto A = typeid(U33_1); + // LIN: call void @_ZNSt9type_infoC1ERKS_(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast ({ i8*, i8* }* @_ZTIU7_ExtIntILi33EEj to %"class.std::type_info"*)) + // WIN: call %"class.std::type_info"* @"??0type_info@std@@QEAA@AEBV01@@Z"(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast (%rtti.TypeDescriptor28* @"??_R0U?$_UExtInt@$0CB@@__clang@@@8" to %"class.std::type_info"*)) + auto B = typeid(U33_2); + // LIN: call void @_ZNSt9type_infoC1ERKS_(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast ({ i8*, i8* }* @_ZTIU7_ExtIntILi33EEj to %"class.std::type_info"*)) + // WIN: call %"class.std::type_info"* @"??0type_info@std@@QEAA@AEBV01@@Z"(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast (%rtti.TypeDescriptor28* @"??_R0U?$_UExtInt@$0CB@@__clang@@@8" to %"class.std::type_info"*)) + auto C = typeid(S33_1); + // LIN: call void @_ZNSt9type_infoC1ERKS_(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast ({ i8*, i8* }* @_ZTIU7_ExtIntILi33EEi to %"class.std::type_info"*)) + // WIN: call %"class.std::type_info"* @"??0type_info@std@@QEAA@AEBV01@@Z"(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast (%rtti.TypeDescriptor27* @"??_R0U?$_ExtInt@$0CB@@__clang@@@8" to %"class.std::type_info"*)) + auto D = typeid(S33_2); + // LIN: call void @_ZNSt9type_infoC1ERKS_(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast ({ i8*, i8* }* @_ZTIU7_ExtIntILi33EEi to %"class.std::type_info"*)) + // WIN: call %"class.std::type_info"* @"??0type_info@std@@QEAA@AEBV01@@Z"(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast (%rtti.TypeDescriptor27* @"??_R0U?$_ExtInt@$0CB@@__clang@@@8" to %"class.std::type_info"*)) + auto E = typeid(S32_1); + // LIN: call void @_ZNSt9type_infoC1ERKS_(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast ({ i8*, i8* }* @_ZTIU7_ExtIntILi32EEi to %"class.std::type_info"*)) + // WIN: call %"class.std::type_info"* @"??0type_info@std@@QEAA@AEBV01@@Z"(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast (%rtti.TypeDescriptor27* @"??_R0U?$_ExtInt@$0CA@@__clang@@@8" to %"class.std::type_info"*)) + auto F = typeid(S32_2); + // LIN: call void @_ZNSt9type_infoC1ERKS_(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast ({ i8*, i8* }* @_ZTIU7_ExtIntILi32EEi to %"class.std::type_info"*)) + // WIN: call %"class.std::type_info"* @"??0type_info@std@@QEAA@AEBV01@@Z"(%"class.std::type_info"* %{{.+}}, %"class.std::type_info"* dereferenceable(16) bitcast (%rtti.TypeDescriptor27* @"??_R0U?$_ExtInt@$0CA@@__clang@@@8" to %"class.std::type_info"*)) +} + +void ExplicitCasts() { + // LIN: define void @_Z13ExplicitCastsv() + // WIN: define dso_local void @"?ExplicitCasts@@YAXXZ"() + + _ExtInt(33) a; + _ExtInt(31) b; + int i; + + a = i; + // CHECK: %[[CONV:.+]] = sext i32 %{{.+}} to i33 + b = i; + // CHECK: %[[CONV:.+]] = trunc i32 %{{.+}} to i31 + i = a; + // CHECK: %[[CONV:.+]] = trunc i33 %{{.+}} to i32 + i = b; + // CHECK: %[[CONV:.+]] = sext i31 %{{.+}} to i32 +} + +struct S { + _ExtInt(17) A; + _ExtInt(16777200) B; + _ExtInt(17) C; +}; + +void OffsetOfTest() { + // LIN: define void @_Z12OffsetOfTestv() + // WIN: define dso_local void @"?OffsetOfTest@@YAXXZ"() + + auto A = __builtin_offsetof(S,A); + // CHECK: store i64 0, i64* %{{.+}} + auto B = __builtin_offsetof(S,B); + // CHECK: store i64 8, i64* %{{.+}} + auto C = __builtin_offsetof(S,C); + // CHECK: store i64 2097160, i64* %{{.+}} +} + + +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]] +} + +void ComplexTest(_Complex _ExtInt(12) first, + _Complex _ExtInt(33) second) { + // LIN: define void @_Z11ComplexTestCU7_ExtIntILi12EEiCU7_ExtIntILi33EEi + // WIN: define dso_local void @"?ComplexTest@@YAXU?$_Complex@U?$_ExtInt@$0M@@__clang@@@__clang@@U?$_Complex@U?$_ExtInt@$0CB@@__clang@@@2@@Z" + first + second; + // CHECK: %[[FIRST_REALP:.+]] = getelementptr inbounds { i12, i12 }, { i12, i12 }* %{{.+}}, i32 0, i32 0 + // CHECK: %[[FIRST_REAL:.+]] = load i12, i12* %[[FIRST_REALP]] + // CHECK: %[[FIRST_IMAGP:.+]] = getelementptr inbounds { i12, i12 }, { i12, i12 }* %{{.+}}, i32 0, i32 1 + // CHECK: %[[FIRST_IMAG:.+]] = load i12, i12* %[[FIRST_IMAGP]] + // CHECK: %[[FIRST_REAL_CONV:.+]] = sext i12 %[[FIRST_REAL]] + // CHECK: %[[FIRST_IMAG_CONV:.+]] = sext i12 %[[FIRST_IMAG]] + // CHECK: %[[SECOND_REALP:.+]] = getelementptr inbounds { i33, i33 }, { i33, i33 }* %{{.+}}, i32 0, i32 0 + // CHECK: %[[SECOND_REAL:.+]] = load i33, i33* %[[SECOND_REALP]] + // CHECK: %[[SECOND_IMAGP:.+]] = getelementptr inbounds { i33, i33 }, { i33, i33 }* %{{.+}}, i32 0, i32 1 + // CHECK: %[[SECOND_IMAG:.+]] = load i33, i33* %[[SECOND_IMAGP]] + // CHECK: %[[REAL:.+]] = add i33 %[[FIRST_REAL_CONV]], %[[SECOND_REAL]] + // CHECK: %[[IMAG:.+]] = add i33 %[[FIRST_IMAG_CONV]], %[[SECOND_IMAG]] +} + +// 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)"} diff --git a/clang/test/CodeGenOpenCL/ext-int-shift.cl b/clang/test/CodeGenOpenCL/ext-int-shift.cl new file mode 100644 --- /dev/null +++ b/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]] +} diff --git a/clang/test/Parser/ext-int.cpp b/clang/test/Parser/ext-int.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/ext-int.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// 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; diff --git a/clang/test/SemaCXX/ext-int.cpp b/clang/test/SemaCXX/ext-int.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/ext-int.cpp @@ -0,0 +1,278 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wimplicit-int-conversion + +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 bit size of at least 2}} + _ExtInt(0) h; // expected-error{{signed _ExtInt must have a bit size of at least 2}} + _ExtInt(1) i; // expected-error{{signed _ExtInt must have a bit size of at least 2}} + _ExtInt(2) j;; + unsigned _ExtInt(0) k;// expected-error{{unsigned _ExtInt must have a bit size of at least 1}} + unsigned _ExtInt(1) l; + signed _ExtInt(1) m; // expected-error{{signed _ExtInt must have a bit 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 bit sizes greater than 16777215 not supported}} + unsigned _ExtInt(0xFFFFFFFFFF) q; // expected-error {{unsigned _ExtInt of bit sizes greater than 16777215 not supported}} + +// Ensure template params are instantiated correctly. + // expected-error@5{{signed _ExtInt must have a bit size of at least 2}} + // expected-error@6{{unsigned _ExtInt must have a bit size of at least 1}} + // expected-note@+1{{in instantiation of template class }} + HasExtInt<-1> r; + // expected-error@5{{signed _ExtInt must have a bit size of at least 2}} + // expected-error@6{{unsigned _ExtInt must have a bit size of at least 1}} + // expected-note@+1{{in instantiation of template class }} + HasExtInt<0> s; + // expected-error@5{{signed _ExtInt must have a bit 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; + +// Allow _Complex: +_Complex _ExtInt(3) Cmplx; + +// Reject cases of _Atomic: +// expected-error@+1{{_Atomic cannot be applied to integer type '_ExtInt(4)' with less than 1 byte of precision}} +_Atomic _ExtInt(4) TooSmallAtomic; +// expected-error@+1{{_Atomic cannot be applied to integer type '_ExtInt(9)' with a non power of 2 precision}} +_Atomic _ExtInt(9) NotPow2Atomic; +_Atomic _ExtInt(128) JustRightAtomic; + +// 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; + + // Disabling mixed conversions: + // Signed/unsigned mixed. + // expected-error@+1{{invalid operands to binary expression}} + x43_u + y43_s; + // expected-error@+1{{invalid operands to binary expression}} + x4_s - y4_u; + // expected-error@+1{{invalid operands to binary expression}} + x43_s * y43_u; + // expected-error@+1{{invalid operands to binary expression}} + x4_u / y4_s; + + // Different Sizes. + // expected-error@+1{{invalid operands to binary expression}} + x43_s + y4_s; + // expected-error@+1{{invalid operands to binary expression}} + x43_s - y4_u; + // expected-error@+1{{invalid operands to binary expression}} + x43_u * y4_u; + // expected-error@+1{{invalid operands to binary expression}} + x4_u / y43_u; + + // Mixed with standard types. + // expected-error@+1{{invalid operands to binary expression}} + x43_s + x_int; + // expected-error@+1{{invalid operands to binary expression}} + x43_u - x_int; + // expected-error@+1{{invalid operands to binary expression}} + x32_s * x_int; + // expected-error@+1{{invalid operands to binary expression}} + x32_u / x_int; + // expected-error@+1{{invalid operands to binary expression}} + x32_s * x_uint; + // expected-error@+1{{invalid operands to binary expression}} + x32_u / x_uint; + // expected-error@+1{{invalid operands to binary expression}} + x4_s + x_int; + // expected-error@+1{{invalid operands to binary expression}} + x4_u - x_int; + // expected-error@+1{{invalid operands to binary expression}} + x4_s + b; + // expected-error@+1{{invalid operands to binary expression}} + x4_u - b; + // expected-error@+1{{invalid operands to binary expression}} + x43_s + b; + // expected-error@+1{{invalid operands to binary expression}} + x43_u - b; + + // Bitwise checks. + // expected-error@+1{{invalid operands to binary expression}} + x43_s % y4_u; + // expected-error@+1{{invalid operands to binary expression}} + x43_u % y4_s; + // expected-error@+1{{invalid operands to binary expression}} + x4_s | y43_u; + // expected-error@+1{{invalid operands to binary expression}} + x4_u | y43_s; + + // compassign. + // expected-error@+1{{invalid operands to binary expression}} + x43_s += 33; + + // Comparisons. + // expected-error@+1{{invalid operands to binary expression}} + x43_s > 33; + // expected-error@+1{{invalid operands to binary expression}} + x4_s > 33; + + // 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,""); + + // 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(is_same> 1), _ExtInt(4)>::value,""); + 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. + + static_assert(alignof(decltype(x43_s)) == 8, ""); + static_assert(alignof(decltype(x4_s)) == 1, ""); + + static_assert(alignof(_ExtInt(3340)) == 8, ""); + static_assert(alignof(_ExtInt(1049)) == 8, ""); +} + +constexpr int func() { return 42;} + +void ConstexprBitsize() { + _ExtInt(func()) F; + static_assert(is_same::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; +}; + +// expected-error@+1{{mode attribute only supported for integer and floating-point types}} +typedef _ExtInt(33) IllegalMode __attribute__((mode(DI))); + +void ImplicitCasts(_ExtInt(31) s31, _ExtInt(33) s33, int i) { + // expected-warning@+1{{implicit conversion loses integer precision}} + s31 = i; + // expected-warning@+1{{implicit conversion loses integer precision}} + s31 = s33; + s33 = i; + s33 = s31; + i = s31; + // expected-warning@+1{{implicit conversion loses integer precision}} + i = s33; +} + diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1793,6 +1793,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.