From d0ce9b4969da028ed2a4f7651e6749ba3c46fa77 Mon Sep 17 00:00:00 2001 From: liuxinyi Date: Thu, 9 Jan 2025 16:37:41 +0800 Subject: [PATCH 1/2] [FatPtr]support fat ptr for bsc compiler and add std library bsc_fat_ptr.hbs 1. add enable-fat-ptr compilation flags to control desugar fat ptr 2. add fat type qualifier and &fat operator 3. desugar fat ptr in FunctionDecl, VarDecl and RecordDecl by TreeTransform framework 4. add type-checking for fat ptr 5. desugar fat ptr in FunctionDecl, VarDecl and RecordDecl by TreeTransform framework. --- clang/include/clang/AST/BSC/WalkerBSC.h | 3 +- clang/include/clang/AST/CanonicalType.h | 4 +- clang/include/clang/AST/OperationKinds.def | 2 + clang/include/clang/AST/StmtVisitor.h | 2 + clang/include/clang/AST/Type.h | 184 +- clang/include/clang/Basic/BSC/BSCAttr.td | 6 + .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 25 +- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Basic/TokenKinds.def | 2 + clang/include/clang/Driver/BSC/BSCOptions.td | 2 + clang/include/clang/Sema/DeclSpec.h | 34 +- clang/include/clang/Sema/Sema.h | 51 +- clang/lib/AST/ASTContext.cpp | 6 +- clang/lib/AST/ASTImporter.cpp | 2 +- clang/lib/AST/BSC/TypeBSC.cpp | 111 ++ clang/lib/AST/Expr.cpp | 1 + clang/lib/AST/ExprConstant.cpp | 16 + clang/lib/AST/StmtPrinter.cpp | 12 +- clang/lib/AST/TypePrinter.cpp | 6 + clang/lib/Analysis/BSCBorrowCheck.cpp | 1 - clang/lib/Analysis/ThreadSafetyCommon.cpp | 3 +- clang/lib/CodeGen/CGCall.cpp | 7 + clang/lib/CodeGen/CGExprAgg.cpp | 5 + clang/lib/CodeGen/CGExprScalar.cpp | 6 + clang/lib/Driver/ToolChains/Clang.cpp | 2 + clang/lib/Headers/CMakeLists.txt | 1 + clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 90 + clang/lib/Lex/Lexer.cpp | 14 + clang/lib/Parse/ParseDecl.cpp | 39 +- clang/lib/Parse/ParseExpr.cpp | 1 + clang/lib/Sema/BSC/SemaBSCCoroutine.cpp | 12 +- clang/lib/Sema/BSC/SemaBSCDestructor.cpp | 2 +- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 1547 +++++++++++++++++ clang/lib/Sema/BSC/SemaBSCOwnership.cpp | 2 +- clang/lib/Sema/BSC/SemaBSCSafeZone.cpp | 2 +- clang/lib/Sema/BSC/SemaDeclBSC.cpp | 5 +- clang/lib/Sema/CMakeLists.txt | 1 + clang/lib/Sema/DeclSpec.cpp | 13 +- clang/lib/Sema/Sema.cpp | 29 +- clang/lib/Sema/SemaCast.cpp | 63 +- clang/lib/Sema/SemaDecl.cpp | 35 +- clang/lib/Sema/SemaDeclAttr.cpp | 16 + clang/lib/Sema/SemaExpr.cpp | 121 +- clang/lib/Sema/SemaExprMember.cpp | 28 +- clang/lib/Sema/SemaOverload.cpp | 9 +- clang/lib/Sema/SemaType.cpp | 7 +- clang/lib/Serialization/ASTWriterStmt.cpp | 3 +- clang/test/BSC/Negative/FatPtr/addr_fat.cbs | 58 + clang/test/BSC/Negative/FatPtr/fat_cast.cbs | 72 + .../FatPtr/fat_qulifier_only_for_pointer.cbs | 24 + .../global_array/array_out_of_bounds1.cbs | 12 + .../global_array/array_out_of_bounds2.cbs | 14 + .../heap/array_out_of_bounds1.cbs | 11 + .../heap/array_out_of_bounds2.cbs | 11 + .../heap/array_out_of_bounds3.cbs | 12 + .../heap/array_out_of_bounds4.cbs | 13 + .../heap/array_out_of_bounds5.cbs | 13 + .../heap/array_out_of_bounds6.cbs | 11 + .../heap/buffer_overflow.cbs | 21 + .../heap/double_free1.cbs | 13 + .../heap/double_free2.cbs | 14 + .../heap/double_free3.cbs | 16 + .../heap/double_free4.cbs | 18 + .../heap/use_after_free1.cbs | 14 + .../heap/use_after_free2.cbs | 15 + .../heap/use_after_free3.cbs | 17 + .../heap/use_after_free4.cbs | 15 + .../heap/use_after_free5.cbs | 29 + .../heap/use_after_free6.cbs | 14 + .../stack/array_out_of_bounds1.cbs | 12 + .../stack/array_out_of_bounds2.cbs | 13 + .../stack/buffer_overflow.cbs | 22 + .../stack/return_local_address.cbs | 17 + .../test/BSC/Positive/FatPtr/global_array.cbs | 23 + clang/test/BSC/Positive/FatPtr/heap.cbs | 94 + clang/test/BSC/Positive/FatPtr/stack.cbs | 24 + 76 files changed, 2972 insertions(+), 204 deletions(-) create mode 100644 clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs create mode 100644 clang/lib/Sema/BSC/SemaBSCFatPtr.cpp create mode 100644 clang/test/BSC/Negative/FatPtr/addr_fat.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/fat_cast.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/global_array.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/heap.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/stack.cbs diff --git a/clang/include/clang/AST/BSC/WalkerBSC.h b/clang/include/clang/AST/BSC/WalkerBSC.h index 5f8435f9c8d0..5db08af81cee 100644 --- a/clang/include/clang/AST/BSC/WalkerBSC.h +++ b/clang/include/clang/AST/BSC/WalkerBSC.h @@ -88,7 +88,8 @@ public: bool VisitQualType(QualType QT) { if (QT.isOwnedQualified() || QT.isBorrowQualified() || - QT->hasBorrowFields() || QT->hasOwnedFields()) { + QT->hasBorrowFields() || QT->hasOwnedFields() || QT.isFatQualified() || + QT->hasFatFields()) { return true; } if (IsDesugaredFromTraitType(QT)) { diff --git a/clang/include/clang/AST/CanonicalType.h b/clang/include/clang/AST/CanonicalType.h index 9b7a1d4efe96..89258bedde76 100644 --- a/clang/include/clang/AST/CanonicalType.h +++ b/clang/include/clang/AST/CanonicalType.h @@ -146,7 +146,9 @@ public: bool isBorrowQualified() const { return Stored.isLocalBorrowQualified(); } - #endif + + bool isFatQualified() const { return Stored.isLocalFatQualified(); } +#endif bool isVolatileQualified() const { return Stored.isLocalVolatileQualified(); diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def index 0c48c0a51353..b744276b1b46 100644 --- a/clang/include/clang/AST/OperationKinds.def +++ b/clang/include/clang/AST/OperationKinds.def @@ -445,6 +445,8 @@ UNARY_OPERATION(AddrMut, "&mut") UNARY_OPERATION(AddrMutDeref, "&mut *") UNARY_OPERATION(AddrConst, "&const") UNARY_OPERATION(AddrConstDeref, "&const *") +// [BSC Fat pointer] fat pointer operator +UNARY_OPERATION(AddrFat, "&fat") #endif #undef CAST_OPERATION diff --git a/clang/include/clang/AST/StmtVisitor.h b/clang/include/clang/AST/StmtVisitor.h index da654e5a616b..b013581c178b 100644 --- a/clang/include/clang/AST/StmtVisitor.h +++ b/clang/include/clang/AST/StmtVisitor.h @@ -106,6 +106,7 @@ public: case UO_AddrConst: DISPATCH(UnaryAddrConst, UnaryOperator); case UO_AddrMutDeref: DISPATCH(UnaryAddrMutDeref, UnaryOperator); case UO_AddrConstDeref: DISPATCH(UnaryAddrConstDeref, UnaryOperator); + case UO_AddrFat: DISPATCH(UnaryAddrFat, UnaryOperator); #endif } } @@ -177,6 +178,7 @@ public: #if ENABLE_BSC UNARYOP_FALLBACK(AddrMut) UNARYOP_FALLBACK(AddrMutDeref) UNARYOP_FALLBACK(AddrConst) UNARYOP_FALLBACK(AddrConstDeref) + UNARYOP_FALLBACK(AddrFat) #endif #undef UNARYOP_FALLBACK diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index cdaca83b0f61..22f7e986a5b0 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -66,11 +66,11 @@ class TemplateParameterList; class Type; enum { - #if ENABLE_BSC - TypeAlignmentInBits = 6, - #else +#if ENABLE_BSC + TypeAlignmentInBits = 7, +#else TypeAlignmentInBits = 4, - #endif +#endif TypeAlignment = 1 << TypeAlignmentInBits }; @@ -154,16 +154,17 @@ using CanQualType = CanQual; class Qualifiers { public: enum TQ { // NOTE: These flags must be kept in sync with DeclSpec::TQ. - Const = 0x1, + Const = 0x1, Restrict = 0x2, Volatile = 0x4, - #if ENABLE_BSC - Owned = 0x8, - Borrow = 0x10, - CVRMask = Const | Volatile | Restrict | Owned | Borrow - #else +#if ENABLE_BSC + Owned = 0x8, + Borrow = 0x10, + Fat = 0x20, + CVRMask = Const | Volatile | Restrict | Owned | Borrow | Fat +#else CVRMask = Const | Volatile | Restrict - #endif +#endif }; enum GC { @@ -195,20 +196,20 @@ public: }; enum { - /// The maximum supported address space number. - /// 21 bits should be enough for anyone. - #if ENABLE_BSC - MaxAddressSpace = 0x1fffffu, - #else +/// The maximum supported address space number. +/// 20 bits should be enough for anyone. +#if ENABLE_BSC + MaxAddressSpace = 0xfffffu, +#else MaxAddressSpace = 0x7fffffu, - #endif +#endif - /// The width of the "fast" qualifier mask. - #if ENABLE_BSC - FastWidth = 5, - #else +/// The width of the "fast" qualifier mask. +#if ENABLE_BSC + FastWidth = 6, +#else FastWidth = 3, - #endif +#endif /// The fast qualifier mask. FastMask = (1 << FastWidth) - 1 @@ -312,7 +313,17 @@ public: Qs.addBorrow(); return Qs; } - #endif + + bool hasFat() const { return Mask & Fat; } + bool hasOnlyFat() const { return Mask == Fat; } + void removeFat() { Mask &= ~Fat; } + void addFat() { Mask |= Fat; } + Qualifiers withFat() const { + Qualifiers Qs = *this; + Qs.addFat(); + return Qs; + } +#endif bool hasVolatile() const { return Mask & Volatile; } bool hasOnlyVolatile() const { return Mask == Volatile; } @@ -652,32 +663,32 @@ public: } private: - // bits: |0 1 2 3 4|5|6 .. 7|8 .. 10|11 ... 31| - // |C R V O B|U|GCAttr|Lifetime|AddressSpace| + // bits: |0 1 2 3 4 5|6|7 .. 8|9 .. 11|12 ... 31| + // |C R V O B F|U|GCAttr|Lifetime|AddressSpace| uint32_t Mask = 0; #if ENABLE_BSC - static const uint32_t UMask = 0x20; - static const uint32_t UShift = 5; - static const uint32_t GCAttrMask = 0xC0; - static const uint32_t GCAttrShift = 6; - static const uint32_t LifetimeMask = 0x700; - static const uint32_t LifetimeShift = 8; - #else + static const uint32_t UMask = 0x40; + static const uint32_t UShift = 6; + static const uint32_t GCAttrMask = 0x180; + static const uint32_t GCAttrShift = 7; + static const uint32_t LifetimeMask = 0xe00; + static const uint32_t LifetimeShift = 9; +#else static const uint32_t UMask = 0x8; static const uint32_t UShift = 3; static const uint32_t GCAttrMask = 0x30; static const uint32_t GCAttrShift = 4; static const uint32_t LifetimeMask = 0x1C0; static const uint32_t LifetimeShift = 6; - #endif +#endif static const uint32_t AddressSpaceMask = ~(CVRMask | UMask | GCAttrMask | LifetimeMask); #if ENABLE_BSC - static const uint32_t AddressSpaceShift = 11; - #else + static const uint32_t AddressSpaceShift = 12; +#else static const uint32_t AddressSpaceShift = 9; - #endif +#endif }; class QualifiersAndAtomic { @@ -696,7 +707,8 @@ public: #if ENABLE_BSC bool hasOwned() const { return Quals.hasOwned(); } bool hasBorrow() const { return Quals.hasBorrow(); } - #endif + bool hasFat() const { return Quals.hasFat(); } +#endif bool hasRestrict() const { return Quals.hasRestrict(); } bool hasAtomic() const { return HasAtomic; } @@ -705,7 +717,8 @@ public: #if ENABLE_BSC void addOwned() { Quals.addOwned(); } void addBorrow() { Quals.addBorrow(); } - #endif + void addFat() { Quals.addFat(); } +#endif void addRestrict() { Quals.addRestrict(); } void addAtomic() { HasAtomic = true; } @@ -714,7 +727,8 @@ public: #if ENABLE_BSC void removeOwned() { Quals.removeOwned(); } void removeBorrow() { Quals.removeBorrow(); } - #endif + void removeFat() { Quals.removeFat(); } +#endif void removeRestrict() { Quals.removeRestrict(); } void removeAtomic() { HasAtomic = false; } @@ -725,7 +739,8 @@ public: #if ENABLE_BSC QualifiersAndAtomic withOwned() { return {Quals.withOwned(), HasAtomic}; } QualifiersAndAtomic withBorrow() { return {Quals.withBorrow(), HasAtomic}; } - #endif + QualifiersAndAtomic withFat() { return {Quals.withFat(), HasAtomic}; } +#endif QualifiersAndAtomic withRestrict() { return {Quals.withRestrict(), HasAtomic}; } @@ -805,7 +820,8 @@ class QualType { // Thankfully, these are efficiently composable. llvm::PointerIntPair, - Qualifiers::FastWidth> Value; // For BSC, FastWidth = 5 + Qualifiers::FastWidth> + Value; // For BSC, FastWidth = 6 const ExtQuals *getExtQualsUnsafe() const { return Value.getPointer().get(); @@ -912,7 +928,18 @@ public: /// Determine whether this type is borrow-qualified. bool isBorrowQualified() const; - #endif + + /// Determine whether this particular QualType instance has the + /// "fat" qualifier set, without looking through typedefs that may have + /// added "fat" at a different level. + bool isLocalFatQualified() const { + return (getLocalFastQualifiers() & Qualifiers::Fat); + } + + /// Determine whether this type is fat-qualified. + bool isFatQualified() const; + bool isFatPtrType() const; +#endif /// Determine whether this particular QualType instance has the /// "restrict" qualifier set, without looking through typedefs that may have @@ -1034,7 +1061,12 @@ public: QualType addConstBorrow(const ASTContext &Context); QualType removeConstForBorrow(const ASTContext &Context); bool hasBorrow() const; - #endif + + /// Add the `fat` type qualifier to this QualType. + void addFat() { addFastQualifiers(Qualifiers::Fat); } + QualType withFat() const { return withFastQualifiers(Qualifiers::Fat); } + bool hasFat() const; +#endif /// Add the `volatile` type qualifier to this QualType. void addVolatile() { @@ -1066,7 +1098,8 @@ public: #if ENABLE_BSC void removeLocalOwned(); void removeLocalBorrow(); - #endif + void removeLocalFat(); +#endif void removeLocalVolatile(); void removeLocalRestrict(); void removeLocalCVRQualifiers(unsigned Mask); @@ -1799,9 +1832,9 @@ protected: /// C++ 8.3.5p4: The return type, the parameter type list and the /// cv-qualifier-seq, [...], are part of the function type. /// - /// After add 'owned, borrow' qualifiers, the FastWidth is now 5(from 3 to 5), - /// then bitNumberOf(FunctionTypeBitFields) is now 65(63 to 65, greater than 8 bytes), - /// then sizeof(Type) is now increased by one byte. + /// After add 'owned, borrow, fat' qualifiers, the FastWidth is now 6(from 3 + /// to 6), then bitNumberOf(FunctionTypeBitFields) is now 66(63 to 66, + /// greater than 8 bytes), then sizeof(Type) is now increased by one byte. unsigned FastTypeQuals : Qualifiers::FastWidth; /// Whether this function has extended Qualifiers. unsigned HasExtQuals : 1; @@ -2039,7 +2072,7 @@ protected: Type(TypeClass tc, QualType canon, TypeDependence Dependence) : ExtQualsTypeCommonBase(this, canon.isNull() ? QualType(this_(), 0) : canon) { - // Add owned, borrow, sizeof(FunctionTypeBitfields) > 8 now. + // Add owned, borrow, fat, sizeof(FunctionTypeBitfields) > 8 now. // sizeof(Type) is larger too. static_assert(sizeof(*this) <= 16 + sizeof(ExtQualsTypeCommonBase), "changing bitfields changed sizeof(Type)!"); @@ -2175,7 +2208,9 @@ public: bool hasOwnedFields() const; bool hasBorrowFields() const; - #endif + + bool hasFatFields() const; +#endif /// Test for a particular builtin type. bool isSpecificBuiltinType(unsigned K) const; @@ -2266,6 +2301,8 @@ public: bool isTraitType() const; bool isTraitPointerType() const; bool hasTraitType() const; + bool isFatPtrRecordType() const; + QualType getFatPtrPointeeType() const; bool isBSCCalculatedTypeInCompileTime() const; bool checkFunctionProtoType(SafeZoneSpecifier SZS) const; bool isOwnedStructureType() const; @@ -2909,7 +2946,9 @@ public: bool hasOwnedFields() const; bool hasBorrowFields() const; - #endif + + bool hasFatFields() const; +#endif }; /// Represents a type which was implicitly adjusted by the semantic @@ -4069,18 +4108,20 @@ public: ExtInfo getExtInfo() const { return ExtInfo(FunctionTypeBits.ExtInfo); } static_assert((~Qualifiers::FastMask & Qualifiers::CVRMask) == 0, - #if ENABLE_BSC - "Const, volatile, restrict, owned and borrow are assumed to be a subset of " - #else +#if ENABLE_BSC + "Const, volatile, restrict, owned, borrow and fat are assumed " + "to be a subset of " +#else "Const, volatile and restrict are assumed to be a subset of " - #endif +#endif "the fast qualifiers."); bool isConst() const { return getFastTypeQuals().hasConst(); } #if ENABLE_BSC bool isOwned() const { return getFastTypeQuals().hasOwned(); } bool isBorrow() const { return getFastTypeQuals().hasBorrow(); } - #endif + bool isFat() const { return getFastTypeQuals().hasFat(); } +#endif bool isVolatile() const { return getFastTypeQuals().hasVolatile(); } bool isRestrict() const { return getFastTypeQuals().hasRestrict(); } @@ -4410,7 +4451,9 @@ public: bool hasOwnedRetOrParams() const; // return true if any 'borrow' here bool hasBorrowRetOrParams() const; - #endif + // return true if any 'fat' here + bool hasFatRetOrParams() const; +#endif /// Return all the available information about this type's exception spec. ExceptionSpecInfo getExceptionSpecInfo() const { @@ -4931,8 +4974,11 @@ protected: withBorrow, withoutBorrow }; + + enum fatStatus{unInitFat, withFat, withoutFat}; mutable ownedStatus hasOwn = ownedStatus::unInitOwned; mutable borrowStatus hasBorrow = borrowStatus::unInitBorrow; + mutable fatStatus hasFat = fatStatus::unInitFat; #endif public: @@ -4952,7 +4998,11 @@ public: bool hasBorrowFields() const; void initBorrowStatus() const; - #endif + + bool hasFatFields() const; + + void initFatStatus() const; +#endif bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } @@ -6986,6 +7036,11 @@ inline bool QualType::isBorrowQualified() const { return isLocalBorrowQualified() || getCommonPtr()->CanonicalType.isLocalBorrowQualified(); } + +inline bool QualType::isFatQualified() const { + return isLocalFatQualified() || + getCommonPtr()->CanonicalType.isLocalFatQualified(); +} #endif inline bool QualType::isRestrictQualified() const { @@ -7009,14 +7064,17 @@ inline QualType QualType::getUnqualifiedType() const { int addOwned = getCanonicalType().isOwnedQualified() ? Qualifiers::Owned : 0; int addBorrow = getCanonicalType().isBorrowQualified() ? Qualifiers::Borrow : 0; - #else + int addFat = getCanonicalType().isFatQualified() ? Qualifiers::Fat : 0; +#else int addOwned = 0; int addBorrow = 0; - #endif + int addFat = 0; +#endif if (!getTypePtr()->getCanonicalTypeInternal().hasLocalQualifiers()) - return QualType(getTypePtr(), addOwned | addBorrow); + return QualType(getTypePtr(), addOwned | addBorrow | addFat); - return QualType(getSplitUnqualifiedTypeImpl(*this).Ty, addOwned | addBorrow); + return QualType(getSplitUnqualifiedTypeImpl(*this).Ty, + addOwned | addBorrow | addFat); } inline SplitQualType QualType::getSplitUnqualifiedType() const { @@ -7039,6 +7097,10 @@ inline void QualType::removeLocalOwned() { inline void QualType::removeLocalBorrow() { removeLocalFastQualifiers(Qualifiers::Borrow); } + +inline void QualType::removeLocalFat() { + removeLocalFastQualifiers(Qualifiers::Fat); +} #endif inline void QualType::removeLocalRestrict() { diff --git a/clang/include/clang/Basic/BSC/BSCAttr.td b/clang/include/clang/Basic/BSC/BSCAttr.td index 1bc3ca7c902d..6cc7de54bf6a 100644 --- a/clang/include/clang/Basic/BSC/BSCAttr.td +++ b/clang/include/clang/Basic/BSC/BSCAttr.td @@ -89,3 +89,9 @@ def Operator : InheritableAttr { let Subjects = SubjectList<[Function]>; let Documentation = [Undocumented]; } + +def FatPtrChecked : InheritableAttr { + let Spellings = [GNU<"fat_ptr_checked">]; + let Subjects = SubjectList<[Var]>; + let Documentation = [Undocumented]; +} \ No newline at end of file diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index da4060a40f0e..da40644d24cd 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -63,7 +63,7 @@ def err_typecheck_invalid_owned_arrsub : Error< "owned pointer type (%0) do not support ArraySubscript operate">; def err_owned_temporary_memLeak : Error< "memory leak because temporary variable '%0' is owned or indirect owned type, please fix it">; -def err_funcPtr_incompatible : Error< +def err_borrow_funcPtr_incompatible : Error< "incompatible borrow function pointer types, cannot cast %0 to %1">; def err_borrow_on_borrow : Error<"%0 on a 'borrow' quialified type is not allowed">; def err_mut_expr_unmodifiable : Error<"the expression after '&mut' must be modifiable">; @@ -78,7 +78,6 @@ def err_borrow_qualcheck_compare : Error< def err_typecheck_borrow_func : Error<"no borrow qualified type found in the function parameters, the return type is not allowed to be borrow qualified">; def err_typecheck_borrow_subscript : Error<"subscript of borrow pointer is not allowed">; - // BSC trait warnings and errors. def err_variables_not_trait_pointer : Error<"only trait pointer type is allowed to be declared">; def err_trait_impl : Error<"function %0 in %1 is not implemented for %2">; @@ -214,4 +213,24 @@ def err_nullable_cast_nonnull : Error< def err_nullable_pointer_access_member : Error< "cannot access member througn nullable pointer">; def err_nonnull_assigned_by_nullable : Error< - "nonnull pointer cannot be assigned by nullable pointer">; \ No newline at end of file + "nonnull pointer cannot be assigned by nullable pointer">; + +// BSC Fat Pointer errors. +def err_fat_ptr_checked_attr_global_array_var : Error< + "fat_ptr_checked attribute can only used for global or static array variables">; +def err_typecheck_invalid_fat_not_pointer : Error< + "only pointer type can be qualified by fat">; +def err_fat_qualcheck_incompatible : Error< + "incompatible fat types, cannot cast %0 to %1">; +def err_fat_funcPtr_incompatible : Error< + "incompatible fat function pointer types, cannot cast %0 to %1">; +def err_addr_fat_on_no_fat_pointer : Error< + "'&fat' on member of no-fat pointer is not allowed">; +def err_addr_fat_on_global_var_without_checked : Error< + "'&fat' on global variables without fat_ptr_checked attribute is not allowed">; +def err_fat_ptr_type_not_found : Error< + "fat ptr type not found, you need to include %0 before using the 'fat' keyword">; +def err_fat_ptr_func_not_found : Error< + "fat ptr related function not found, you need to include %0">; +def err_fat_ptr_missing_specialization : Error< + "missing definition of template specialization for fat ptr %0">; \ No newline at end of file diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 6530a91d7f78..01b65d0b44a6 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -260,6 +260,7 @@ LANGOPT(RenderScript , 1, 0, "RenderScript") LANGOPT(HLSL, 1, 0, "HLSL") #if ENABLE_BSC LANGOPT(BSC, 1, 0, "BSC") +LANGOPT(EnableFatPtr, 1, 0, "enable fat ptr") LANGOPT(DisableOwnershipCheck, 1, 0, "disable ownership check") ENUM_LANGOPT(NullabilityCheck, NullCheckZone, 2, NC_SAFE, "nullability check") #endif diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 0def6903efe1..e2f7b89cff7c 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -251,6 +251,7 @@ PUNCTUATOR(caretcaret, "^^") #if ENABLE_BSC PUNCTUATOR(ampmut, "&mut") PUNCTUATOR(ampconst, "&const") +PUNCTUATOR(ampfat, "&fat") #endif // C99 6.4.1: Keywords. These turn into kw_* tokens. @@ -477,6 +478,7 @@ KEYWORD(async , KEYBSC) KEYWORD(await , KEYBSC) KEYWORD(owned , KEYBSC) KEYWORD(borrow , KEYBSC) +KEYWORD(fat , KEYBSC) KEYWORD(trait , KEYBSC) KEYWORD(safe , KEYBSC) KEYWORD(unsafe , KEYBSC) diff --git a/clang/include/clang/Driver/BSC/BSCOptions.td b/clang/include/clang/Driver/BSC/BSCOptions.td index 1b7d0328d08c..6c782e581767 100644 --- a/clang/include/clang/Driver/BSC/BSCOptions.td +++ b/clang/include/clang/Driver/BSC/BSCOptions.td @@ -4,6 +4,8 @@ def rewrite_bsc : Flag<["-"], "rewrite-bsc">, Flags<[CC1Option]>, Group; def rewrite_bsc_line : Flag<["-"], "line">, Flags<[CC1Option]>, HelpText<"Insert line info when rewrite BSC">; +def enable_fat_ptr : Flag<["-"], "enable-fat-ptr">, Flags<[CC1Option]>, + HelpText<"Enable fat ptr">, MarshallingInfoFlag>; def disable_ownership_check : Flag<["-"], "disable-ownership-check">, Flags<[CC1Option]>, HelpText<"Disable ownership check">, MarshallingInfoFlag>; def nullability_check : Joined<["-"], "nullability-check=">, Flags<[CC1Option]>, diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index f16d5abd3318..e0bc8603e4f6 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -337,19 +337,20 @@ public: // type-qualifiers enum TQ { // NOTE: These flags must be kept in sync with Qualifiers::TQ. TQ_unspecified = 0, - TQ_const = 1, - TQ_restrict = 2, - TQ_volatile = 4, + TQ_const = 1, + TQ_restrict = 2, + TQ_volatile = 4, #if ENABLE_BSC - TQ_owned = 8, - TQ_borrow = 16, - TQ_unaligned = 32, + TQ_owned = 8, + TQ_borrow = 16, + TQ_fat = 32, + TQ_unaligned = 64, // This has no corresponding Qualifiers::TQ value, because it's not treated // as a qualifier in our type system. - TQ_atomic = 64 + TQ_atomic = 128 #else - TQ_unaligned = 8, - TQ_atomic = 16 + TQ_unaligned = 8, + TQ_atomic = 16 #endif }; @@ -385,7 +386,7 @@ private: // type-qualifiers #if ENABLE_BSC - unsigned TypeQualifiers : 7; // Bitwise OR of TQ. + unsigned TypeQualifiers : 8; // Bitwise OR of TQ. #else unsigned TypeQualifiers : 5; #endif @@ -456,7 +457,7 @@ private: SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; SourceLocation TQ_pipeLoc; #if ENABLE_BSC - SourceLocation TQ_ownedLoc, TQ_borrowLoc; + SourceLocation TQ_ownedLoc, TQ_borrowLoc, TQ_fatLoc; SourceLocation FS_asyncLoc, FS_safe_zone_loc; bool IsImplTrait = false; // if parsing impl trait decl #endif @@ -631,6 +632,7 @@ public: #if ENABLE_BSC SourceLocation getOwnedSpecLoc() const { return TQ_ownedLoc; } SourceLocation getBorrowSpecLoc() const { return TQ_borrowLoc; } + SourceLocation getFatSpecLoc() const { return TQ_fatLoc; } void setImplTrait() { IsImplTrait = true; } bool getImplTrait() { return IsImplTrait; } llvm::Optional getConditionalCondResult() const { return ConditionalCondResult; } @@ -646,6 +648,7 @@ public: #if ENABLE_BSC TQ_ownedLoc = SourceLocation(); TQ_borrowLoc = SourceLocation(); + TQ_fatLoc = SourceLocation(); #endif TQ_restrictLoc = SourceLocation(); TQ_volatileLoc = SourceLocation(); @@ -1338,7 +1341,7 @@ struct DeclaratorChunk { struct PointerTypeInfo { /// The type qualifiers: const/volatile/restrict/owned/unaligned/atomic. #if ENABLE_BSC - unsigned TypeQuals : 7; + unsigned TypeQuals : 8; #else unsigned TypeQuals : 5; #endif @@ -1352,6 +1355,9 @@ struct DeclaratorChunk { /// The location of the borrow-qualifier, if any. SourceLocation BorrowQualLoc; + + /// The location of the fat-qualifier, if any. + SourceLocation FatQualLoc; #endif /// The location of the volatile-qualifier, if any. @@ -1383,7 +1389,7 @@ struct DeclaratorChunk { /// The type qualifiers for the array: /// const/volatile/restrict/owned/__unaligned/_Atomic. #if ENABLE_BSC - unsigned TypeQuals : 7; + unsigned TypeQuals : 8; #else unsigned TypeQuals : 5; #endif @@ -1687,7 +1693,7 @@ struct DeclaratorChunk { struct MemberPointerTypeInfo { /// The type qualifiers: const/volatile/restrict/owned/__unaligned/_Atomic. #if ENABLE_BSC - unsigned TypeQuals : 7; + unsigned TypeQuals : 8; #else unsigned TypeQuals : 5; #endif diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b02939c4439f..0babd61ee984 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1953,11 +1953,7 @@ public: PoppedFunctionScopePtr PopFunctionScopeInfo(const sema::AnalysisBasedWarnings::Policy *WP = nullptr, const Decl *D = nullptr, - QualType BlockType = QualType() - #if ENABLE_BSC - , bool isBSCCoroutine = false - #endif - ); + QualType BlockType = QualType()); sema::FunctionScopeInfo *getCurFunction() const { return FunctionScopes.empty() ? nullptr : FunctionScopes.back(); @@ -3218,6 +3214,19 @@ public: bool IsBSCCompatibleFutureType(QualType Ty); + // BSC Fat Ptr related. + bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, + SourceLocation RLoc); + bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType); + bool CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr); + bool CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, + SourceLocation RLoc); + bool CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr); + void DesugarFunctionDeclWithFatPtr(FunctionDecl *FD); + void DesugarRecordDeclWithFatPtr(RecordDecl *RD); + void DesugarGlobalVarDeclWithFatPtr(VarDecl *VD); + std::pair + BuildAllocationUnitForGlobalArrayVar(VarDecl *VD); // BSC Destructor related. BSCMethodDecl *getOrInsertBSCDestructor(RecordDecl *RD); void HandleBSCDestructorBody(RecordDecl *RD, BSCMethodDecl *Destructor, @@ -12307,20 +12316,25 @@ public: /// object with __weak qualifier. IncompatibleObjCWeakRef, - #if ENABLE_BSC - /// IncompatibleOwnedPointer - The assignment is between a owned qualified pointer - /// type with a unOwned qualified pointer type or two owned qualified pointer type - /// with different base types +#if ENABLE_BSC + /// IncompatibleOwnedPointer - The assignment is between a owned qualified + /// pointer type with a unOwned qualified pointer type or two owned + /// qualified pointer type with different base types IncompatibleOwnedPointer, - /// IncompatibleBorrowPointer - The assignment is between a borrow qualified pointer - /// type with a unBorrow qualified pointer type or two borrow qualified pointer type - /// with different base types + /// IncompatibleBorrowPointer - The assignment is between a borrow qualified + /// pointer type with a unBorrow qualified pointer type or two borrow + /// qualified pointer type with different base types IncompatibleBorrowPointer, + /// IncompatibleFatPointer - The assignment is between a fat qualified + /// pointer type with a unFat qualified pointer type or two fat qualified + /// pointer type with different base types + IncompatibleFatPointer, + /// IncompatibleBSCSafeZone - unsafe convert in the bsc safe zone. IncompatibleBSCSafeZone, - #endif +#endif /// Incompatible - We reject this conversion outright, it is invalid to /// represent it in the AST. @@ -12398,7 +12412,7 @@ public: void CheckOwnedOrIndirectOwnedType(SourceLocation ErrLoc, QualType T, StringRef Env); bool CheckOwnedDecl(SourceLocation ErrLoc, QualType T); bool CheckTemporaryVarMemoryLeak(Expr* E); - void BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck = true, + void BSCDataflowAnalysis(const FunctionDecl *FD, bool EnableOwnershipCheck = true, bool EnableNullabilityCheck = true); bool IsInSafeZone(); bool IsSafeBuiltinTypeConversion(BuiltinType::Kind SourceType, @@ -12433,11 +12447,10 @@ public: bool CheckBorrowQualTypeCompare(QualType LHSType, QualType RHSType); void CheckBorrowOrIndirectBorrowType(SourceLocation ErrLoc, QualType T, StringRef Env); - QualType GetBorrowAddressOperandQualType(QualType resultType, - ExprResult &Input, - const Expr *InputExpr, - UnaryOperatorKind &Opc, - SourceLocation OpLoc); + QualType GetBSCAddressOperandQualType(QualType resultType, ExprResult &Input, + const Expr *InputExpr, + UnaryOperatorKind &Opc, + SourceLocation OpLoc); bool CheckOperatorDeclNeedAddToContext(Declarator &D); bool CheckOperatorFunReturnTypeIsLegal(FunctionDecl *FnDecl); bool FindSafeFeatures(const FunctionDecl* FnDecl); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 11d3138573fd..0d34dfc80377 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -10150,7 +10150,11 @@ bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS, bool CompareUnqualified) { if (getLangOpts().CPlusPlus) return hasSameType(LHS, RHS); - +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + LHS->isFatPtrRecordType() && RHS->isFatPtrRecordType()) + return true; +#endif return !mergeTypes(LHS, RHS, false, CompareUnqualified).isNull(); } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 196d193d5b24..38e841e5c02d 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1508,7 +1508,7 @@ ExpectedType ASTNodeImporter::VisitInjectedClassNameType( // return Importer.getToContext().getInjectedClassNameType(D, InjType); enum { #if ENABLE_BSC - TypeAlignmentInBits = 6, + TypeAlignmentInBits = 7, #else TypeAlignmentInBits = 4, #endif diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index bba9b782c748..f7e1cbf52688 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -66,6 +66,52 @@ bool Type::hasBorrowFields() const { return false; } +bool PointerType::hasFatFields() const { + QualType R = getPointeeType(); + if (R.isFatQualified()) { + return true; + } + if (R.getTypePtr()->hasFatFields()) { + return true; + } + return false; +} + +bool Type::hasFatFields() const { + if (const auto *RecTy = dyn_cast(CanonicalType)) { + return RecTy->hasFatFields(); + } else if (const auto *PointerTy = dyn_cast(CanonicalType)) { + return PointerTy->hasFatFields(); + } + return false; +} + +bool Type::isFatPtrRecordType() const { + if (this->isRecordType()) + if (RecordDecl *RD = this->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + return false; +} + +QualType Type::getFatPtrPointeeType() const { + if (this->isFatPtrRecordType()) + if (auto TST = dyn_cast(this)) + if (TST->getNumArgs() == 1) + return TST->begin()->getAsType(); + if (auto PT = dyn_cast(this)) + return PT->getPointeeType(); + return QualType(); +} + +bool QualType::isFatPtrType() const { + if (isFatQualified()) + return true; + if (const Type *ty = getTypePtr()) + return ty->isFatPtrRecordType(); + return false; +} + bool FunctionProtoType::hasOwnedRetOrParams() const { if (getReturnType().isOwnedQualified()) { return true; @@ -90,6 +136,18 @@ bool FunctionProtoType::hasBorrowRetOrParams() const { return false; } +bool FunctionProtoType::hasFatRetOrParams() const { + if (getReturnType().isFatQualified()) { + return true; + } + for (auto ParamType : getParamTypes()) { + if (ParamType.isFatQualified()) { + return true; + } + } + return false; +} + bool Type::checkFunctionProtoType(SafeZoneSpecifier SZS) const { const FunctionProtoType *FPT = nullptr; if (isFunctionType()) { @@ -254,6 +312,53 @@ bool RecordType::hasBorrowFields() const { return false; } +void RecordType::initFatStatus() const { + if (hasFat != fatStatus::unInitFat) + return; + std::vector RecordTypeList; + RecordTypeList.push_back(this); + unsigned NextToCheckIndex = 0; + + while (RecordTypeList.size() > NextToCheckIndex) { + for (FieldDecl *FD : + RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { + QualType FieldTy = FD->getType(); + if (FieldTy.isFatQualified()) { + hasFat = fatStatus::withFat; + return; + } + QualType tempQT = FieldTy; + const Type *tempT = tempQT.getTypePtr(); + while (tempT->isPointerType()) { + tempQT = tempT->getPointeeType(); + if (tempQT.isFatQualified()) { + hasFat = fatStatus::withFat; + return; + } else { + tempQT = tempQT.getCanonicalType(); + tempT = tempQT.getTypePtr(); + } + } + FieldTy = tempQT.getCanonicalType(); + if (const auto *FieldRecTy = FieldTy->getAs()) { + if (llvm::find(RecordTypeList, FieldRecTy) == RecordTypeList.end()) + RecordTypeList.push_back(FieldRecTy); + } + } + ++NextToCheckIndex; + } + hasFat = fatStatus::withoutFat; + return; +} + +bool RecordType::hasFatFields() const { + if (hasFat == fatStatus::unInitFat) + initFatStatus(); + if (hasFat == fatStatus::withFat) + return true; + return false; +} + bool Type::isBSCFutureType() const { if (const auto *RT = getAs()) { RecordDecl *RD = RT->getAsRecordDecl(); @@ -292,6 +397,12 @@ bool QualType::hasBorrow() const { return getTypePtr()->hasBorrowFields(); } +bool QualType::hasFat() const { + if (isFatQualified()) + return true; + return getTypePtr()->hasFatFields(); +} + bool QualType::isConstBorrow() const { QualType QT = QualType(getTypePtr(), getLocalFastQualifiers()); if (QT.isLocalBorrowQualified()) { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index c55477431d52..025a70a754fa 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2535,6 +2535,7 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case UO_AddrConst: case UO_AddrMutDeref: case UO_AddrConstDeref: + case UO_AddrFat: #endif case UO_Not: case UO_LNot: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 0d7aa2caeab1..894692ab2d5c 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8037,8 +8037,18 @@ public: QualType BaseTy; bool EvalOK; if (E->isArrow()) { +#if ENABLE_BSC + if (this->Info.getLangOpts().BSC && this->Info.getLangOpts().EnableFatPtr && + E->getBase()->getType()->isFatPtrRecordType()) { + EvalOK = true; + BaseTy = E->getBase()->getType()->getFatPtrPointeeType(); + } else { +#endif EvalOK = evaluatePointer(E->getBase(), Result); BaseTy = E->getBase()->getType()->castAs()->getPointeeType(); +#if ENABLE_BSC + } +#endif } else if (E->getBase()->isPRValue()) { assert(E->getBase()->getType()->isRecordType()); EvalOK = EvaluateTemporary(E->getBase(), Result, this->Info); @@ -8484,6 +8494,11 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { } bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) { +#if ENABLE_BSC + if (Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && + E->getSubExpr()->getType()->isFatPtrRecordType()) + return true; +#endif return evaluatePointer(E->getSubExpr(), Result); } @@ -15533,6 +15548,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case UO_AddrConst: case UO_AddrMutDeref: case UO_AddrConstDeref: + case UO_AddrFat: #endif case UO_Deref: case UO_Coawait: diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 1aea4e166576..fdafb8620f7c 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1158,10 +1158,12 @@ void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) { #if ENABLE_BSC if (Policy.RewriteBSC) { if (VarDecl *VD = dyn_cast(Node->getDecl())) { - APValue *Res = VD->getEvaluatedValue(); - if (VD->isConstexpr() && Res && Res->isInt()) { - OS << Res->getInt(); - return; + if (VD->isConstexpr()) { + APValue *Res = VD->evaluateValue(); + if (Res && Res->isInt()) { + OS << Res->getInt(); + return; + } } } if (auto *BD = dyn_cast(Node->getFoundDecl())) { @@ -1479,7 +1481,7 @@ void StmtPrinter::VisitUnaryOperator(UnaryOperator *Node) { #if ENABLE_BSC if (Policy.RewriteBSC) { if (Node->getOpcode() == UO_AddrConst || - Node->getOpcode() == UO_AddrMut) { + Node->getOpcode() == UO_AddrMut || Node->getOpcode() == UO_AddrFat) { OS << UnaryOperator::getOpcodeStr(UO_AddrOf); } else if (Node->getOpcode() == UO_AddrConstDeref || Node->getOpcode() == UO_AddrMutDeref) { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 0cc42b5cf96f..8279e434ad67 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -200,6 +200,12 @@ static void AppendTypeQualList(raw_ostream &OS, unsigned TypeQuals, OS << "borrow"; appendSpace = true; } + if (TypeQuals & Qualifiers::Fat && !IsRewriteBSC) { + if (appendSpace) + OS << ' '; + OS << "fat"; + appendSpace = true; + } #endif if (TypeQuals & Qualifiers::Volatile) { if (appendSpace) OS << ' '; diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSCBorrowCheck.cpp index bc9243999a9c..7b4d8d3d47f8 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -18,7 +18,6 @@ #include "clang/Analysis/CFG.h" #include #include -#include #include using namespace clang; using namespace std; diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index e6ab441058d0..89b71e7a37e4 100644 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -465,7 +465,8 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, #if ENABLE_BSC case UO_AddrMut: case UO_AddrConst: - #endif + case UO_AddrFat: +#endif if (CapabilityExprMode) { // interpret &Graph::mu_ as an existential. if (const auto *DRE = dyn_cast(UO->getSubExpr())) { diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index dfa78bf59c65..4ff156690c82 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4185,6 +4185,13 @@ void CodeGenFunction::EmitCallArgs( CallExpr::const_arg_iterator Arg = ArgRange.begin(); for (QualType Ty : ArgTypes) { assert(Arg != ArgRange.end() && "Running over edge of argument list!"); + if (Ty.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + Ty.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") { + ++Arg; + continue; + } assert( (isGenericMethod || Ty->isVariablyModifiedType() || Ty.getNonReferenceType()->isObjCRetainableType() || diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 73b05690537d..f5d307974bd6 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1166,6 +1166,11 @@ static bool isBlockVarRef(const Expr *E) { void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { // For an assignment to work, the value on the right has // to be compatible with the value on the left. +#if ENABLE_BSC + if (!(CGF.getLangOpts().BSC && CGF.getLangOpts().EnableFatPtr && + E->getLHS()->getType()->isFatPtrRecordType() && + E->getRHS()->getType()->isFatPtrRecordType())) +#endif assert(CGF.getContext().hasSameUnqualifiedType(E->getLHS()->getType(), E->getRHS()->getType()) && "Invalid assignment"); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index c689fa310858..9a0fba739fe0 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -637,6 +637,12 @@ public: Value *VisitUnaryAddrConstDeref(const UnaryOperator *E) { return Visit(E->getSubExpr()); } + Value *VisitUnaryAddrFat(const UnaryOperator *E) { + if (isa(E->getType())) // never sugared + return CGF.CGM.getMemberPointerConstant(E); + + return EmitLValue(E->getSubExpr()).getPointer(CGF); + } #endif Value *VisitUnaryDeref(const UnaryOperator *E) { if (E->getType()->isVoidType()) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index f32da6d90e65..543964111f2c 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4818,6 +4818,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, #if ENABLE_BSC if (Args.getLastArg(options::OPT_opt_string)) Args.AddLastArg(CmdArgs, options::OPT_opt_string); + if (Args.hasArg(options::OPT_enable_fat_ptr)) + CmdArgs.push_back("-enable-fat-ptr"); if (Args.hasArg(options::OPT_disable_ownership_check)) CmdArgs.push_back("-disable-ownership-check"); if (Args.hasArg(options::OPT_nullability_check)) { diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt index 158dfb759f75..81faf5ebd67c 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -265,6 +265,7 @@ set(openmp_wrapper_files if (ENABLE_BSC) set(bsc_include_files + bsc_include/bsc_fat_ptr.hbs bsc_include/bsc_type_traits.hbs bsc_include/bsc_conditional.hbs bsc_include/future.hbs diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs new file mode 100644 index 000000000000..e0558ee48b36 --- /dev/null +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -0,0 +1,90 @@ +/*===---------- bsc_fat_ptr.hbs - Standard header for fat ptr ----------===*\ + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * +\*===----------------------------------------------------------------------===*/ + +// Define BSC fat ptr type and related functions +#ifndef BSC_FAT_PTR_HBS +#define BSC_FAT_PTR_HBS + +#include +#include +#include +#include +#include + +struct _FatPtr { + T* raw_ptr; + uint32_t key_version; + int32_t offset; +}; + +struct _AllocationUnit { + uint32_t lock_version; + uint32_t size; + uint8_t payload[]; +}; + +void _report_error(const char* msg) { + fprintf(stderr, "%s", msg); + void *callstack[10]; + int num_frames = backtrace(callstack, 10); + char **symbols = backtrace_symbols(callstack, num_frames); + + fprintf(stderr, "Call stack:\n"); + for (int i = 0; i < num_frames; i++) { + fprintf(stderr, "%s\n", symbols[i]); + } + free(symbols); + exit(EXIT_FAILURE); +} + +uint32_t _new_version_number() { + static _Atomic(uint32_t) uuid = 10000; + atomic_fetch_add(&uuid, 1); + return atomic_load(&uuid); +} + +_FatPtr checked_malloc(size_t payload_bytes) { + if (payload_bytes == 0) + _report_error("allocated size is ZERO!\n"); + size_t all_size = payload_bytes + 8; + void *raw = malloc(all_size); + uint32_t *p = (uint32_t*)raw; + uint32_t ver = _new_version_number(); + *p = ver; + *(p + 1) = payload_bytes; + _FatPtr ptr = { .raw_ptr = (void*)(p + 2), .key_version = ver, .offset = 0 }; + return ptr; +} + +void checked_free(_FatPtr ptr) { + uint8_t* head = (uint8_t*)ptr.raw_ptr - ptr.offset - 8; + struct _AllocationUnit* phead = (struct _AllocationUnit *)head; + if (ptr.key_version != phead->lock_version) + _report_error("version number error when free!\n"); + if (ptr.offset != 0) + _report_error("free a pointer which is not point to the head of an allocation!\n"); + memset(phead, 0, phead->size + 8); + free(phead); +} + +void _check_version(_FatPtr ptr) { + const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + if (ptr.key_version != 0 && ptr.key_version != phead->lock_version) + _report_error("version number error!\n"); +} + +void _check_offset(_FatPtr ptr, int32_t bytes) { + const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + int32_t new_offset = ptr.offset + bytes; + if (phead->size > 0 && new_offset > phead->size) + _report_error("pointer offset exceed the allocation size!\n"); +} + +#endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 27cdfc68c13e..c38f31d4324a 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -3914,6 +3914,20 @@ LexNextToken: ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result), SizeTmp2, Result), SizeTmp3, Result), SizeTmp4, Result), SizeTmp5, Result); } + } else if (LangOpts.BSC && Char == 'f' && + getCharAndSize(CurPtr + SizeTmp, SizeTmp2) == 'a' && + getCharAndSize(CurPtr + SizeTmp + SizeTmp2, SizeTmp3) == 't') { + char After = + getCharAndSize(CurPtr + SizeTmp + SizeTmp2 + SizeTmp3, SizeTmp4); + if ((After >= '0' && After <= '9') || (After >= 'a' && After <= 'z') || + (After >= 'A' && After <= 'Z')) { + Kind = tok::amp; + } else { + Kind = tok::ampfat; + CurPtr = ConsumeChar( + ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result), SizeTmp2, Result), + SizeTmp3, Result); + } } #endif else { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 3faa4ceba9ca..d314128f5ff7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2233,12 +2233,26 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, DeclsInGroup.push_back(FirstDecl); #if ENABLE_BSC - if (FirstDecl) { - FunctionDecl *FD = dyn_cast_or_null(FirstDecl); - if (getLangOpts().BSC && FD && FD->isAsyncSpecified()) { - SmallVector Decls = Actions.ActOnAsyncFunctionDeclaration(FD); - for (auto &D : Decls) { - DeclsInGroup.push_back(D); + if (getLangOpts().BSC && FirstDecl) { + if (FunctionDecl *FD = dyn_cast_or_null(FirstDecl)) { + if (FD->isAsyncSpecified()) { + SmallVector Decls = Actions.ActOnAsyncFunctionDeclaration(FD); + for (auto &D : Decls) { + DeclsInGroup.push_back(D); + } + } + } else if (VarDecl *VD = dyn_cast_or_null(FirstDecl)) { + if (getLangOpts().EnableFatPtr && VD->hasGlobalStorage()) { + QualType QT = VD->getType().getCanonicalType(); + if (VD->hasAttr() && + QT->isConstantArrayType()) { + std::pair Decls = + Actions.BuildAllocationUnitForGlobalArrayVar(VD); + DeclsInGroup.push_back(Decls.first); + DeclsInGroup.push_back(Decls.second); + } else if (QT.isFatQualified() || QT->hasFatFields()) { + Actions.DesugarGlobalVarDeclWithFatPtr(VD); + } } } } @@ -4519,6 +4533,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, isInvalid = DS.SetTypeQual(DeclSpec::TQ_borrow, Loc, PrevSpec, DiagID, getLangOpts()); break; + + // fat-qualifier: + case tok::kw_fat: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_fat, Loc, PrevSpec, DiagID, + getLangOpts()); + break; #endif // C++ typename-specifier: @@ -5610,6 +5630,7 @@ bool Parser::isTypeSpecifierQualifier() { #if ENABLE_BSC case tok::kw_owned: case tok::kw_borrow: + case tok::kw_fat: #endif // Debugger support. @@ -5785,6 +5806,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { #if ENABLE_BSC case tok::kw_owned: case tok::kw_borrow: + case tok::kw_fat: #endif case tok::kw__Sat: @@ -6108,6 +6130,11 @@ void Parser::ParseTypeQualifierListOpt( isInvalid = DS.SetTypeQual(DeclSpec::TQ_borrow, Loc, PrevSpec, DiagID, getLangOpts()); break; + // fat-qualifier: + case tok::kw_fat: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_fat, Loc, PrevSpec, DiagID, + getLangOpts()); + break; #endif case tok::kw__Atomic: if (!AtomicAllowed) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index f92348520b7c..fa64e3805832 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1427,6 +1427,7 @@ ExprResult Parser::ParseCastExpression( #if ENABLE_BSC case tok::ampmut: case tok::ampconst: + case tok::ampfat: #endif case tok::amp: { // unary-expression: '&' cast-expression if (NotPrimaryExpression) diff --git a/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp b/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp index a67c1c8dfea5..18aa747e27fa 100644 --- a/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp +++ b/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp @@ -917,7 +917,7 @@ static FunctionDecl *buildFutureInitFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD; } @@ -1094,7 +1094,7 @@ buildFutureStructInitFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD; } @@ -2414,7 +2414,7 @@ static BSCMethodDecl *buildFreeFunctionDeclaration(Sema &S, RecordDecl *RD, S.PushFunctionScope(); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD; } @@ -2645,7 +2645,7 @@ static BSCMethodDecl *buildFreeFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD; } @@ -2706,7 +2706,7 @@ static BSCMethodDecl *buildPollFunctionDeclaration(Sema &S, RecordDecl *RD, NewFD->setType(FuncType); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD->isInvalidDecl() ? nullptr : NewFD; } @@ -2869,7 +2869,7 @@ static BSCMethodDecl *buildPollFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD->isInvalidDecl() ? nullptr : NewFD; } diff --git a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp index f44821d2a515..a3593c7c9c8d 100644 --- a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp +++ b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp @@ -2,10 +2,10 @@ #include -#include "TreeTransform.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/SourceManager.h" #include "clang/Sema/Scope.h" #include "clang/Sema/Template.h" diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp new file mode 100644 index 000000000000..41a5c1ae7b21 --- /dev/null +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -0,0 +1,1547 @@ +//===--- SemaBSCFatPtr.cpp - Semantic Analysis for BSC FatPtr -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for BSC FatPtr. +// +//===----------------------------------------------------------------------===// + +#if ENABLE_BSC +#include "TreeTransform.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/DiagnosticSema.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include + +using namespace clang; +using namespace sema; + +enum AddrFatExprBaseKind { + // `&fat p->a;` p is fat ptr + // `&fat p->arr[1];` p is fat ptr + // `&fat p[1];` p is fat ptr + // `&fat p[1].a;` p is fat ptr + // `&fat s.p[1].a;` s.p is fat ptr + FatPtr, + + // `&fat a;` a is local no-array variable + // `&fat s.a;` s is local no-array variable whose type has no array field + LocalNoArrayVar, + + // `&fat arr[5];` arr is local array variable + // `&fat s.arr[5];` s is local variable whose type has array field + // `&fat s.a;` s is local variable whose type has array field + LocalArrayVar, + + // `&fat garr[5];` garr is global array variable + // `&fat g.arr[5];` g is global variable whose type has array field + // `&fat g.a;` g is global variable whose type has array field + GlobalArrayVar, + + OtherKind, +}; + +// Collect local vars whose address is taken by &fat in current function, +// also classify &fat expr by its base kind. +class AddressFatTakenFinder : public StmtVisitor { + bool IsVisitingAddrFatExpr = false; + Expr *CurrentAddrFatExpr = nullptr; + +public: + bool LocalNoArrayVarAddressIsFatTaken = false; + llvm::SmallSet LocalArrayVarsSet; + + // Map of AddrFatExpr and its base expr and base kind: + // key is AddrFatExpr, value is its base expr and kind. + // For example: + // 1. &fat p->a` where p is fat ptr + // 2. &fat arr[5]` where arr is local array variable + // 3. &fat g.arr[5]` where g is global variable whose type has array field + // the map will be: + // &fat p->a : (FatPtr, p) + // &fat arr[5] : (LocalArrayVar, arr) + // &fat g.arr[5] : (GlobalArrayVar, g) + llvm::DenseMap> + AddrFatExprAndBaseMap; + + bool hasConstantArray(QualType QT) { + QT = QT.getCanonicalType(); + if (QT->isConstantArrayType()) + return true; + else if (auto RT = dyn_cast(QT)) { + if (auto RD = dyn_cast(RT->getDecl())) + for (auto *FD : RD->fields()) + if (hasConstantArray(FD->getType())) + return true; + } + return false; + } + + void VisitStmt(Stmt *S) { + for (auto *C : S->children()) + if (C) + Visit(C); + } + + void VisitUnaryOperator(UnaryOperator *UO) { + if (UO->getOpcode() == UO_AddrFat) { + IsVisitingAddrFatExpr = true; + CurrentAddrFatExpr = UO; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::OtherKind; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = nullptr; + } + Visit(UO->getSubExpr()); + } + + void VisitMemberExpr(MemberExpr *ME) { + if (IsVisitingAddrFatExpr) { + Expr *BaseE = ME->getBase()->IgnoreImpCasts(); + if (BaseE->getType().isFatPtrType()) { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::FatPtr; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = BaseE; + CurrentAddrFatExpr = nullptr; + IsVisitingAddrFatExpr = false; + return; + } + } + Visit(ME->getBase()); + } + + void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { + if (IsVisitingAddrFatExpr) { + Expr *BaseE = ASE->getBase()->IgnoreImpCasts(); + if (BaseE->getType().isFatPtrType()) { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::FatPtr; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = BaseE; + CurrentAddrFatExpr = nullptr; + IsVisitingAddrFatExpr = false; + return; + } + } + Visit(ASE->getBase()); + } + + void VisitDeclRefExpr(DeclRefExpr *DRE) { + auto VD = dyn_cast(DRE->getDecl()); + if (VD && IsVisitingAddrFatExpr) { + QualType QT = VD->getType(); + if (VD->hasLocalStorage()) { + if (hasConstantArray(QT)) { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::LocalArrayVar; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = DRE; + LocalArrayVarsSet.insert(VD); + } else { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::LocalNoArrayVar; + LocalNoArrayVarAddressIsFatTaken = true; + } + } else if (VD->hasGlobalStorage() && hasConstantArray(QT)) { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::GlobalArrayVar; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = DRE; + } + CurrentAddrFatExpr = nullptr; + IsVisitingAddrFatExpr = false; + } + } +}; + +// Desugar fat ptr for FunctionDecl, VarDecl and RecordDecl. +class TransformFatPtr : public TreeTransform { + typedef TreeTransform BaseTransform; + FunctionDecl *FD = nullptr; + + // cache fat ptr related function to avoid frequent lookup. + std::map FatPtrFD; + // cache fat ptr TemplateDecl to avoid frequent lookup. + ClassTemplateDecl *FatPtrTD = nullptr; + + AddressFatTakenFinder Finder; + VarDecl *LocalLockVersionVD = nullptr; + llvm::DenseMap> + AllocationUnitForLocalArrayVarMap; + + // when deref, access member or some pointer calculation, + // fat ptr should be desugared to its ptr member, for example: + // 1. `*p` should be desugared to `*p.raw_ptr` + // 2. `p->a` should be desugared to `p.raw_ptr->a` + // 3. `p1 - p2` should be desugared to `p1.raw_ptr - p2.raw_ptr` + // 4. `(int*)p` should be desugared to `(int*)p.raw_ptr` + // but `int *fat p1 = p2;` p2 should not be desugared, + // and `float *fat p1 = (float *fat)p2;` p2 should not be desugared. + bool DesugarToMemberPtr = false; + bool InsertCheckVersionCall = false; + std::pair InsertCheckOffsetCall = + std::make_pair(false, nullptr); + std::stack CheckCallExpr; + + // when accessing array member through FatPtr, + // such as `p->arr[3]` or `p->a.b.arr[3]` + // we should check offset to avoid out of bounds. + ArraySubscriptExpr *CurrentASEToAccessMemberThroughFatPtr = nullptr; + +public: + TransformFatPtr(Sema &SemaRef) : BaseTransform(SemaRef) {} + + TransformFatPtr(Sema &SemaRef, FunctionDecl *Fd) + : BaseTransform(SemaRef), FD(Fd) { + FatPtrFD["_check_version"] = nullptr; + FatPtrFD["_check_offset"] = nullptr; + FatPtrFD["_new_version_number"] = nullptr; + } + + // Entry to desugar FunctionDecl + void TransformFunctionDecl(); + // Entry to desugar global or local VarDecl + void TransformVarDecl(VarDecl *VD); + // Entry to desugar global or local RecordDecl + void TransformRecordDecl(RecordDecl *RD); + + StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); + StmtResult TransformCompoundStmt(CompoundStmt *CS); + StmtResult TransformDeclStmt(DeclStmt *DS); + StmtResult TransformReturnStmt(ReturnStmt *RS); + ExprResult TransformDeclRefExpr(DeclRefExpr *DRE); + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE); + ExprResult TransformParenExpr(ParenExpr *PE); + ExprResult TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE); + ExprResult TransformInitListExpr(InitListExpr *ILE); + ExprResult TransformCallExpr(CallExpr *CE); + ExprResult TransformArraySubscriptExpr(ArraySubscriptExpr *ASE); + ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE); + ExprResult TransformUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UETT); + ExprResult TransformMemberExpr(MemberExpr *ME); + + ExprResult TransformBinaryOperator(BinaryOperator *BO); + ExprResult HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO); + ExprResult HandleBOFatPtrSubFatPtr(BinaryOperator *BO); + + ExprResult TransformUnaryOperator(UnaryOperator *UO); + ExprResult HandleUODerefFatPtr(UnaryOperator *UO); + ExprResult HandleUOAddrFatOnExprWithFatPtrBase(UnaryOperator *UO, Expr *FatPtrBaseE); + ExprResult HandleUOAddrFatOnLocalNoArrayVar(UnaryOperator *UO); + ExprResult HandleUOAddrFatOnArrayVar(UnaryOperator *UO, Expr *VarBaseE); + +private: + // Handle allocation unit for local vars whose address is taken by &fat. + CompoundStmt *HandleLocalAllocationUnit(CompoundStmt *CS, + bool IsTopLevelCompoundStmt); + + std::pair + BuildAllocationUnitHeaderForLocalNoArrayVars(SourceLocation SLoc); + + Expr *BuildLockVersionAssignmentForLocalNoArrayVars(SourceLocation SLoc); + + std::pair + BuildAllocationUnitForLocalArrayVar(VarDecl *VD); + + QualType DesugarFatPtrType(SourceLocation SLoc, QualType T); + MemberExpr *BuildMemberForFatPtr(Expr *FatPtrBaseE, std::string FieldName); + Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, + llvm::SmallVector &Args); + Expr *BuildFatPtrCompoundLiteralExprForRawPtr(Expr *RawPtrE, QualType QT); + Expr *BuildFatPtrCompoundLiteralExpr(Expr *FirstInitE, Expr *SecondInitE, + Expr *ThirdInitE, QualType QT); + bool IsAccessingArrayMemberThroughFatPtr(Expr *E); + void VisitExprToBuildOffsetOfComponent( + Expr *E, SourceLocation SLoc, + SmallVector &Comps); +}; + +void TransformFatPtr::TransformFunctionDecl() { + // Desugar fat qualified return type. + QualType ReturnTy = + DesugarFatPtrType(FD->getBeginLoc(), FD->getReturnType()); + // Desugar fat qualified parameters. + llvm::SmallVector ParamTys; + for (ParmVarDecl *PVD : FD->parameters()) { + QualType ParamTy = + DesugarFatPtrType(PVD->getLocation(), PVD->getType()); + PVD->setType(ParamTy); + ParamTys.push_back(ParamTy); + } + // Set desugared function type. + FD->setType(SemaRef.Context.getFunctionType( + ReturnTy, ParamTys, + FD->getType()->getAs()->getExtProtoInfo())); + + // First traversal: + // find local vars whose address is taken by `&fat` expr. + Finder.VisitStmt(FD->getBody()); + + // Second traversal + CompoundStmt *NewCS = + HandleLocalAllocationUnit(dyn_cast(FD->getBody()), true); + + // Third traversal: desugar stmt and expr of function body. + FD->setBody(TransformStmt(NewCS).get()); +} + +void TransformFatPtr::TransformVarDecl(VarDecl *VD) { + QualType QT = DesugarFatPtrType(VD->getLocation(), VD->getType()); + VD->setType(QT); + if (Expr *Init = VD->getInit()) { + if (QT->isFatPtrRecordType() && isa(Init)) { + // `int *fat p = nullptr;` should be desugared to + // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` + VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); + } else { + VD->setInit(BaseTransform::TransformExpr(Init).get()); + } + } +} + +void TransformFatPtr::TransformRecordDecl(RecordDecl *RD) { + // Desugar fat ptr field. + for (FieldDecl *FD : RD->fields()) + FD->setType(DesugarFatPtrType(FD->getLocation(), FD->getType())); +} + +// 1. For all local vars whose type has no array and address is +// taken by &fat, we think they are packed in an allocation unit +// in stack and share a lock_version, so we build 2 VarDecl +// `_local_lock_version` and `_local_size` as allocation unit +// header and insert them to the beginning of function body. +// 2. Before function return, `_local_lock_version` should be set to +// 0, so we build assignment statement before return stmt and in the +// end of function body. +// 3. For local var whose type has array and address is taken by &fat, +// because every array have its own size, so we pack every var into +// a struct as allocation unit. +CompoundStmt * +TransformFatPtr::HandleLocalAllocationUnit(CompoundStmt *CS, + bool IsTopLevelCompoundStmt) { + if (CS == nullptr) + return CS; + llvm::SmallVector Statements; + if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) { + std::pair Headers = + BuildAllocationUnitHeaderForLocalNoArrayVars(CS->getBeginLoc()); + Statements.push_back(Headers.second); + Statements.push_back(Headers.first); + } + if (Finder.LocalArrayVarsSet.size() == 0) { + for (auto *B : CS->body()) + Statements.push_back(B); + } else { + for (auto *B : CS->body()) { + if (auto ChildCS = dyn_cast(B)) { + Statements.push_back(HandleLocalAllocationUnit(ChildCS, false)); + } else if (auto DS = dyn_cast(B)) { + if (DS->isSingleDecl()) { + if (VarDecl *VD = dyn_cast(DS->getSingleDecl())) { + if (Finder.LocalArrayVarsSet.count(VD)) { + std::pair Decls = + BuildAllocationUnitForLocalArrayVar(VD); + Statements.push_back(Decls.first); + Statements.push_back(Decls.second); + continue; + } + } + Statements.push_back(B); + } else { + // TODO: a DeclStmt contains more than one VarDecl, we should + // divide it into several DeclStmts. + Statements.push_back(B); + } + } else if (isa(B) && Finder.LocalNoArrayVarAddressIsFatTaken) { + Statements.push_back( + BuildLockVersionAssignmentForLocalNoArrayVars(B->getBeginLoc())); + Statements.push_back(B); + } else { + Statements.push_back(B); + } + } + } + if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) + Statements.push_back( + BuildLockVersionAssignmentForLocalNoArrayVars(CS->getEndLoc())); + auto NewCS = CompoundStmt::Create( + SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); + return NewCS; +} + +// Build allocation unit header for all local vars whose +// address is taken by `&fat`: +// @code +// uint32_t _local_size = 0; +// uint32_t _local_lock_version = _new_version_number(); +// @endcode +std::pair +TransformFatPtr::BuildAllocationUnitHeaderForLocalNoArrayVars( + SourceLocation SLoc) { + QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); + // Build VarDecl: uint32_t _local_lock_version = _new_version_number(); + VarDecl *LockVersionVD = + VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, + &SemaRef.Context.Idents.get("_local_lock_version"), + UInt32Ty, nullptr, SC_None); + llvm::SmallVector Args; + LockVersionVD->setInit(BuildFatPtrCall(SLoc, "_new_version_number", Args)); + DeclStmt *LockVersionDS = + new (SemaRef.Context) DeclStmt(DeclGroupRef(LockVersionVD), SLoc, SLoc); + LocalLockVersionVD = LockVersionVD; + + // Build VarDecl: uint32_t _local_size = 0; + VarDecl *SizeVD = VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, + &SemaRef.Context.Idents.get("_local_size"), + UInt32Ty, nullptr, SC_None); + SizeVD->setInit(IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 0), + UInt32Ty, SLoc)); + DeclStmt *SizeDS = + new (SemaRef.Context) DeclStmt(DeclGroupRef(SizeVD), SLoc, SLoc); + + return std::make_pair(LockVersionDS, SizeDS); +} + +// Build allocation unit RecordDecl for every local array var whose element +// address is taken by &fat, for example, if we have an array variable whose +// VarDecl is `int arr[3] = {1, 2, 3};`, we desugar this decl to: +// @code +// struct _arr_AllocationUnit { +// uint32_t lock_version; +// uint32_t size; +// int arr[3]; +// }; +// struct _arr_AllocationUnit _arr = +// { _new_version_number(), 12, {1, 2, 3} }; +// @endcode +std::pair +TransformFatPtr::BuildAllocationUnitForLocalArrayVar(VarDecl *VD) { + SourceLocation SLoc = VD->getLocation(); + // Build RecordDecl: struct _arr_AllocationUnit; + RecordDecl *RD = RecordDecl::Create( + SemaRef.Context, TagDecl::TagKind::TTK_Struct, FD, SLoc, SLoc, + &SemaRef.Context.Idents.get("_" + VD->getNameAsString() + + "_AllocationUnit")); + // Add three Field + RD->startDefinition(); + QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); + FieldDecl *LockVersionField = FieldDecl::Create( + SemaRef.Context, RD, SLoc, SLoc, &SemaRef.Context.Idents.get("lock_version"), + UInt32Ty, SemaRef.Context.getTrivialTypeSourceInfo(UInt32Ty), + nullptr, false, ICIS_NoInit); + FieldDecl *SizeField = FieldDecl::Create( + SemaRef.Context, RD, SLoc, SLoc, &SemaRef.Context.Idents.get("size"), + UInt32Ty, SemaRef.Context.getTrivialTypeSourceInfo(UInt32Ty), + nullptr, false, ICIS_NoInit); + FieldDecl *VDField = FieldDecl::Create( + SemaRef.Context, RD, SLoc, SLoc, + &SemaRef.Context.Idents.get(VD->getNameAsString()), VD->getType(), + SemaRef.Context.getTrivialTypeSourceInfo(VD->getType()), + nullptr, false, ICIS_NoInit); + LockVersionField->setAccess(AS_public); + SizeField->setAccess(AS_public); + VDField->setAccess(AS_public); + RD->addDecl(LockVersionField); + RD->addDecl(SizeField); + RD->addDecl(VDField); + RD->completeDefinition(); + + // Build VarDecl: struct _arr_AllocationUnit _arr; + VarDecl *NewVD = + VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, + &SemaRef.Context.Idents.get("_" + VD->getNameAsString()), + QualType(RD->getTypeForDecl(), 0), nullptr, SC_None); + // Build InitExpr + // Build first init expr: _ner_version_number() + llvm::SmallVector Args; + Expr *LockVersionInit = BuildFatPtrCall(SLoc, "_new_version_number", Args); + // Build second init expr by array size + Expr *SizeInit = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(VD->getType()) / 8), + UInt32Ty, SLoc); + // Build InitListExpr + SmallVector Inits{LockVersionInit, SizeInit}; + if (VD->getInit()) + Inits.push_back(VD->getInit()); + Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); + ILE->setType(QualType(RD->getTypeForDecl(), 0)); + NewVD->setInit(ILE); + AllocationUnitForLocalArrayVarMap[VD] = std::make_pair(RD, NewVD); + DeclStmt *RDDS = new (SemaRef.Context) DeclStmt(DeclGroupRef(RD), SLoc, SLoc); + DeclStmt *NewVDDS = new (SemaRef.Context) DeclStmt(DeclGroupRef(NewVD), SLoc, SLoc); + return std::make_pair(RDDS, NewVDDS); +} + +// Build assignment expr: _local_lock_version = 0; +Expr *TransformFatPtr::BuildLockVersionAssignmentForLocalNoArrayVars( + SourceLocation SLoc) { + if (LocalLockVersionVD) { + QualType VQT = LocalLockVersionVD->getType(); + Expr *LHS = + SemaRef.BuildDeclRefExpr(LocalLockVersionVD, VQT, VK_LValue, SLoc); + Expr *RHS = + IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 0), VQT, SLoc); + return SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, LHS, RHS).get(); + } + return nullptr; +} + +StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS) { + return this->TransformCompoundStmt(CS, false); +} + +StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS, + bool IsStmtExpr) { + if (CS == nullptr) + return CS; + llvm::SmallVector Statements; + for (auto *B : CS->body()) { + StmtResult Result = BaseTransform::TransformStmt(B); + while (!CheckCallExpr.empty()) { + Statements.push_back(CheckCallExpr.top()); + CheckCallExpr.pop(); + } + Statements.push_back(Result.getAs()); + } + auto NewCS = CompoundStmt::Create( + SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); + return NewCS; +} + +ExprResult TransformFatPtr::TransformDeclRefExpr(DeclRefExpr *DRE) { + SourceLocation SLoc = DRE->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, DRE->getType()); + DRE->setType(QT); + if (QT->isFatPtrRecordType()) { + if (InsertCheckOffsetCall.first) { + // Build CallExpr to check offset. + llvm::SmallVector Args{DRE, InsertCheckOffsetCall.second}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); + CheckCallExpr.push(CE); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + if (InsertCheckVersionCall) { + // Build CallExpr to check version. + llvm::SmallVector Args{DRE}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); + CheckCallExpr.push(CE); + InsertCheckVersionCall = false; + } + if (DesugarToMemberPtr) { + DesugarToMemberPtr = false; + // Desugar p to p.raw_ptr + return BuildMemberForFatPtr(DRE, "raw_ptr"); + } + } else if (auto VD = dyn_cast(DRE->getDecl())) { + if (AllocationUnitForLocalArrayVarMap.count(VD) || + SemaRef.Context.BSCDesugaredMap.count(VD)) { + // Desugar arr to _arr.arr where arr has its owned AllocationUnit. + RecordDecl *AllocationUnitRD; + VarDecl *AllocationUnitVD; + if (AllocationUnitForLocalArrayVarMap.count(VD)) { + AllocationUnitRD = AllocationUnitForLocalArrayVarMap[VD].first; + AllocationUnitVD = AllocationUnitForLocalArrayVarMap[VD].second; + } else { + AllocationUnitRD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][0]); + AllocationUnitVD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][1]); + } + Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( + AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); + RecordDecl::field_iterator it = AllocationUnitRD->field_begin(); + it++; + it++; + FieldDecl *ArrayFD = *it; + return SemaRef.BuildMemberExpr( + AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, + ArrayFD, DeclAccessPair::make(AllocationUnitRD, ArrayFD->getAccess()), + false, DeclarationNameInfo(), ArrayFD->getType(), VK_LValue, + OK_Ordinary); + } + } + return DRE; +} + +ExprResult TransformFatPtr::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { + QualType QT = ICE->getType(); + if (DesugarToMemberPtr && QT.isFatPtrType()) { + QT = QT->getFatPtrPointeeType(); + ICE->setType(SemaRef.Context.getPointerType(QT)); + ICE->setCastKind(CK_LValueToRValue); + } else { + QT = DesugarFatPtrType(ICE->getBeginLoc(), QT); + ICE->setType(QT); + } + Expr *SubE = ICE->getSubExpr(); + ICE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + if (ICE->getCastKind() == CK_NoOp && QT->isFatPtrRecordType() && + SubE->getType()->isFatPtrRecordType()) + ICE->setType(SubE->getType()); + return ICE; +} + +ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { + QualType QT = PE->getType(); + if (DesugarToMemberPtr && QT.isFatPtrType()) { + QT = QT->getFatPtrPointeeType(); + PE->setType(SemaRef.Context.getPointerType(QT)); + } else { + QT = DesugarFatPtrType(PE->getBeginLoc(), QT); + PE->setType(QT); + } + PE->setSubExpr(BaseTransform::TransformExpr(PE->getSubExpr()).get()); + return PE; +} + +StmtResult TransformFatPtr::TransformDeclStmt(DeclStmt *DS) { + for (auto D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) + TransformVarDecl(VD); + else if (RecordDecl *RD = dyn_cast(D)) + TransformRecordDecl(RD); + else + BaseTransform::TransformDefinition(D->getLocation(), D); + } + return DS; +} + +ExprResult +TransformFatPtr::TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE) { + CLE->setType(DesugarFatPtrType(CLE->getBeginLoc(), CLE->getType())); + CLE->setInitializer( + BaseTransform::TransformExpr(CLE->getInitializer()).get()); + return CLE; +} + +ExprResult TransformFatPtr::TransformInitListExpr(InitListExpr *ILE) { + ILE->setType(DesugarFatPtrType(ILE->getBeginLoc(), ILE->getType())); + for (unsigned i = 0; i < ILE->getNumInits(); i++) + ILE->setInit(i, BaseTransform::TransformExpr(ILE->getInit(i)).get()); + return ILE; +} + +StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { + Expr *RV = RS->getRetValue(); + QualType ReturnQT = FD->getReturnType(); + if (ReturnQT->isFatPtrRecordType() && isa(RV)) { + // if a function has fat pointer return type, such as `int *fat foo();` + // `return nullptr;` should be desugared to + // `return (_FatPtr){ nullptr, 0, 0 };` + RS->setRetValue(BuildFatPtrCompoundLiteralExprForRawPtr(RV, ReturnQT)); + } else { + RS->setRetValue(BaseTransform::TransformExpr(RV).get()); + } + return RS; +} + +ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { + CE->setType(DesugarFatPtrType(CE->getBeginLoc(), CE->getType())); + FunctionDecl *CalleeFD = CE->getDirectCallee(); + for (unsigned i = 0; i < CalleeFD->getNumParams(); i++) { + QualType ParamQT = CalleeFD->getParamDecl(i)->getType(); + Expr *ArgE = CE->getArg(i); + if (ParamQT->isFatPtrRecordType() && isa(ArgE)) { + // if a function has fat pointer parameter, such as + // `void foo(int *fat p);` + // `foo(nullptr)` should be desugared to + // `foo((_FatPtr){ nullptr, 0, 0 })` + CE->setArg(i, BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT)); + } else + CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); + } + return CE; +} + +ExprResult +TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { + SourceLocation SLoc = ASE->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, ASE->getType()); + ASE->setType(QT); + + Expr *LHS = ASE->getLHS(); + Expr *RHS = ASE->getRHS(); + QualType LHSQT = LHS->getType(); + if (LHSQT.isFatPtrType()) { + // p[i] should be desugared to `p.raw_ptr[i]` and insert check call expr. + ASE->setRHS(BaseTransform::TransformExpr(RHS).get()); + DesugarToMemberPtr = true; + InsertCheckVersionCall = true; + InsertCheckOffsetCall.first = true; + // Build offset arg expr, for example: + // for p[i] where p[i] is int type, + // offset should be `i * sizeof(int) + sizeof(int)` + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *OffsetExprLHS = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); + OffsetExprLHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, ASE->getRHS(), OffsetExprLHS).get(); + Expr *OffsetExprRHS = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); + Expr *OffsetExpr = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); + InsertCheckOffsetCall.second = OffsetExpr; + ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); + return ASE; + } else if (IsAccessingArrayMemberThroughFatPtr(ASE)) { + // If current ASE is accessing array member through fat ptr, + // such as `p->arr[i]` or `p->b.arr[i]`, in order to calculate + // offset when transform inner MemberEXpr, we record current ASE. + CurrentASEToAccessMemberThroughFatPtr = ASE; + } + ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); + ASE->setRHS(BaseTransform::TransformExpr(RHS).get()); + return ASE; +} + +ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { + SourceLocation SLoc = CSCE->getBeginLoc(); + QualType QT = CSCE->getType(); + Expr *SubE = CSCE->getSubExpr(); + QualType SubQT = SubE->getType(); + if (QT->isPointerType() && !QT.isFatPtrType() && SubQT.isFatPtrType()) { + // desuagr `(int*)fat_p` to `(int*)fat_p.raw_ptr` + DesugarToMemberPtr = true; + InsertCheckVersionCall = true; + InsertCheckOffsetCall.first = true; + // Build offset arg expr + Expr *OffsetExpr = + IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), + SemaRef.Context.getIntTypeForBitwidth(32, true), SLoc); + InsertCheckOffsetCall.second = OffsetExpr; + } else if (QT.isFatPtrType() && SubQT->isPointerType() && !SubQT.isFatPtrType()) { + QT = DesugarFatPtrType(SLoc, QT); + // desugar `(int *fat)raw_p` to `(_FatPtr) { raw_p, 0, 0 }` + Expr *SubRawPtrE = BaseTransform::TransformExpr(SubE).get(); + return BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); + } else if (QT.isFatPtrType() && SubQT.isFatPtrType()) { + return BaseTransform::TransformExpr(SubE).get(); + } + CSCE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + return CSCE; +} + +ExprResult TransformFatPtr::TransformUnaryExprOrTypeTraitExpr( + UnaryExprOrTypeTraitExpr *UETT) { + SourceLocation SLoc = UETT->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, UETT->getType()); + UETT->setType(QT); + // Recalculate size of fat ptr which is larger than raw ptr: + // `sizeof(int *)` equals to 8, but `sizeof(int *fat)` equals to 16 + if (UETT->isArgumentType()) { + QualType OldQT = UETT->getArgumentType(); + if (OldQT.isFatPtrType()) { + QualType NewQT = DesugarFatPtrType(SLoc, OldQT); + TypeSourceInfo *NewTInfo = + SemaRef.Context.getTrivialTypeSourceInfo(NewQT, SLoc); + return BaseTransform::RebuildUnaryExprOrTypeTrait( + NewTInfo, UETT->getOperatorLoc(), UETT->getKind(), + UETT->getSourceRange()); + } + } + return UETT; +} + +// BinaryOperator includes: +// reassign fat ptr : p1 = p2, p1 = nullptr +// calculation between fat ptr : p + 1, p - 1, p1 - p2 +ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { + SourceLocation SLoc = BO->getBeginLoc(); + BO->setType(DesugarFatPtrType(SLoc, BO->getType())); + Expr *LHS = BO->getLHS(); + Expr *RHS = BO->getRHS(); + QualType LHSQT = LHS->getType(); + QualType RHSQT = RHS->getType(); + BinaryOperator::Opcode Op = BO->getOpcode(); + if (Op == BO_Sub && LHSQT.isFatPtrType() && RHSQT.isFatPtrType()) { + // Substraction between two fat ptr + // `p1 - p2` should be desugared to `p1.raw_ptr - p2.raw_ptr` + return HandleBOFatPtrSubFatPtr(BO); + } else if (BO->isAdditiveOp() && LHSQT.isFatPtrType() && RHSQT->isIntegerType()) { + // addition or substraction between fat ptr and integer + // `p + 1` should be desugared to + // `(_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` + return HandleBOFatPtrAddOrSubInteger(BO); + } else if (BO->isAssignmentOp() && LHSQT.isFatPtrType() && + isa(RHS)) { + // `p = nullptr;` should be desugared to + // `p = (_FatPtr){ nullptr, 0, 0 };` + LHSQT = DesugarFatPtrType(SLoc, LHSQT); + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(BuildFatPtrCompoundLiteralExprForRawPtr(RHS, LHSQT)); + return BO; + } + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(BaseTransform::TransformExpr(RHS).get()); + return BO; +} + +ExprResult TransformFatPtr::HandleBOFatPtrSubFatPtr(BinaryOperator *BO) { + DesugarToMemberPtr = true; + BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); + DesugarToMemberPtr = true; + BO->setRHS(BaseTransform::TransformExpr(BO->getRHS()).get()); + return BO; +} + +// addition or substraction between fat ptr and integer +// `p + 1` should be desugared to +// `(_FatPtr) { p.ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` +ExprResult TransformFatPtr::HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO) { + SourceLocation SLoc = BO->getBeginLoc(); + QualType QT = BO->getType(); + BinaryOperator::Opcode Op = BO->getOpcode(); + Expr *FatPtrLHS = BO->getLHS()->IgnoreImpCasts(); + DesugarToMemberPtr = true; + Expr *LHS = BaseTransform::TransformExpr(BO->getLHS()).get(); + Expr *RHS = BaseTransform::TransformExpr(BO->getRHS()).get(); + Expr *FirstInitE = + SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); + Expr *SecondInitE = BuildMemberForFatPtr(FatPtrLHS, "key_version"); + + Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrLHS, "offset"); + QualType PointeeQT = FatPtrLHS->getType()->getFatPtrPointeeType(); + Expr *SizeOfExpr = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT), + SLoc, UETT_SizeOf, SourceRange()) + .get(); + Expr *ThirdInitRHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, SizeOfExpr).get(); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); +} + +ExprResult TransformFatPtr::TransformUnaryOperator(UnaryOperator *UO) { + QualType QT = DesugarFatPtrType(UO->getBeginLoc(), UO->getType()); + UO->setType(QT); + + UnaryOperator::Opcode Op = UO->getOpcode(); + Expr *SubE = UO->getSubExpr(); + QualType SubQT = SubE->getType(); + if (Op == UO_Deref && SubQT.isFatPtrType()) { + // handle *p if p is fat ptr. + return HandleUODerefFatPtr(UO); + } else if (Op == UO_AddrFat) { + // handle &fat expr. + AddrFatExprBaseKind BaseKind = Finder.AddrFatExprAndBaseMap[UO].first; + switch (BaseKind) { + case AddrFatExprBaseKind::FatPtr: + return HandleUOAddrFatOnExprWithFatPtrBase(UO, Finder.AddrFatExprAndBaseMap[UO].second); + case AddrFatExprBaseKind::LocalNoArrayVar: + return HandleUOAddrFatOnLocalNoArrayVar(UO); + case AddrFatExprBaseKind::LocalArrayVar: + case AddrFatExprBaseKind::GlobalArrayVar: + return HandleUOAddrFatOnArrayVar(UO, Finder.AddrFatExprAndBaseMap[UO].second); + default: + break; + } + } + UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + return UO; +} + +ExprResult TransformFatPtr::HandleUODerefFatPtr(UnaryOperator *UO) { + Expr *SubE = UO->getSubExpr(); + DesugarToMemberPtr = true; + InsertCheckVersionCall = true; + InsertCheckOffsetCall.first = true; + // Build offset arg expr, for example: + // for *p, offset should be `sizeof(int)` + Expr *OffsetExpr = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(UO->getType()), + UO->getBeginLoc(), UETT_SizeOf, SourceRange()) + .get(); + InsertCheckOffsetCall.second = OffsetExpr; + UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + return UO; +} + +// if p is fat ptr of struct G, +// 1. `int *fat p1 = &fat p->a;` should be desugared to +// @code +// _FatPtr p1 = (_FatPtr) { &p.ptr->a, p.key_version, +// (int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr + p.offset }; +// @endcode +// 2. `struct G *fat p1 = &fat p[i];` should be desugared to +// @code +// _FatPtr p1 = (_FatPtr) { &p.ptr[i], p.key_version, +// (int32_t)((uint8_t *)&p.ptr[i] - (uint8_t *)p.ptr) + p.offset }; +// @endcode +ExprResult TransformFatPtr::HandleUOAddrFatOnExprWithFatPtrBase( + UnaryOperator *UO, Expr *FatPtrBaseE) { + SourceLocation SLoc = UO->getBeginLoc(); + QualType QT = UO->getType(); + Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); + + // Build first init expr: &p.raw_ptr->a + Expr *FirstInitE = SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); + // Build second init expr: p.key_version + Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBaseE, "key_version"); + // Build third init expr: + // `(int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr + p.offset` + QualType UInt8PointerTy = SemaRef.Context.getPointerType( + SemaRef.Context.getIntTypeForBitwidth(8, false)); + Expr *ThirdInitLHSLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, FirstInitE) + .get(); + Expr *ThirdInitLHSRHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, BuildMemberForFatPtr(FatPtrBaseE, "raw_ptr")) + .get(); + Expr *ThirdInitLHS = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS) + .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + ThirdInitLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, + ThirdInitLHS) + .get(); + Expr *ThirdInitRHS = BuildMemberForFatPtr(FatPtrBaseE, "offset"); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); +} + +// if s is local variable whose type has no array, +// 1.`struct S *fat p = &fat s;` should be desugared to +// @code +// _FatPtr p = (_FatPtr) { &s, _local_lock_version, +// (int32_t)((uint8_t *)&a - (uint8_t *)&_local_lock_version) - 8 };` +// @endcode +// 2. `int *fat p = &fat s.a;` should be desugared to +// @code +// _FatPtr p = (_FatPtr) { &s.a, _local_lock_version, +// (int32_t)((uint8_t *)&s.a - (uint8_t *)&_local_lock_version) - 8 };` +// @endcode +ExprResult +TransformFatPtr::HandleUOAddrFatOnLocalNoArrayVar(UnaryOperator *UO) { + SourceLocation SLoc = UO->getBeginLoc(); + QualType QT = UO->getType(); + Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); + // Build first init expr: &a + Expr *FirstInitE = SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); + // Build second init expr: _local_version_lock + Expr *SecondInitE = SemaRef.BuildDeclRefExpr( + LocalLockVersionVD, LocalLockVersionVD->getType(), VK_LValue, SLoc); + // Build third init expr: + // `(int32_t)((uint8_t *)&a - (uint8_t *)&_local_lock_version) - 8` + QualType UInt8PointerTy = SemaRef.Context.getPointerType( + SemaRef.Context.getIntTypeForBitwidth(8, false)); + Expr *ThirdInitLHSLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, FirstInitE) + .get(); + Expr *ThirdInitLHSRHS = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SecondInitE).get(); + ThirdInitLHSRHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, ThirdInitLHSRHS) + .get(); + Expr *ThirdInitLHS = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS,ThirdInitLHSRHS) + .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + ThirdInitLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, + ThirdInitLHS) + .get(); + Expr *ThirdInitRHS = IntegerLiteral::Create( + SemaRef.Context, llvm::APInt(32, 8), Int32Ty, SLoc); + Expr *ThirdInitE = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHS, ThirdInitRHS) + .get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); +} + +// 1. if arr is array variable, +// `int *fat p = &fat arr[5]` should be desugared to +// @code +// _FatPtr p = (_FatPtr) { &_arr.arr[5], _arr.lock_version, +// (int32_t)((uint8_t *)&_arr.arr[5] - (uint8_t *)&_arr.lock_version) - 8 }; +// @endcode +// 2. if s is variable whose type is `struct G { int a; int arr[3]; }` +// `int *fat p = &fat s.a` should be desugared to +// @code +// _FatPtr p = (_FatPtr) { &_s.s.a, _s.lock_version, +// (int32_t)((uint8_t *)&_s.s.a - (uint8_t *)&_s.lock_version) - 8 }; +// @endcode +ExprResult TransformFatPtr::HandleUOAddrFatOnArrayVar( + UnaryOperator *UO, Expr *VarBaseE) { + SourceLocation SLoc = UO->getBeginLoc(); + QualType QT = UO->getType(); + Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); + // Build first init expr: &_arr.arr[5] + Expr *FirstInitE = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); + // Build second init expr: _arr.lock_version + RecordDecl *AllocationUnitRD = nullptr; + VarDecl *AllocationUnitVD = nullptr; + auto DRE = dyn_cast(VarBaseE); + auto VD = dyn_cast(DRE->getDecl()); + if (AllocationUnitForLocalArrayVarMap.count(VD)) { + // VD is local var + AllocationUnitRD = AllocationUnitForLocalArrayVarMap[VD].first; + AllocationUnitVD = AllocationUnitForLocalArrayVarMap[VD].second; + } else if (SemaRef.Context.BSCDesugaredMap.count(VD)) { + // VD is global var + AllocationUnitRD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][0]); + AllocationUnitVD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][1]); + } + FieldDecl *LockVersionFD = *(AllocationUnitRD->field_begin()); + Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( + AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); + Expr *SecondInitE = SemaRef.BuildMemberExpr( + AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, LockVersionFD, + DeclAccessPair::make(AllocationUnitRD, LockVersionFD->getAccess()), false, + DeclarationNameInfo(), LockVersionFD->getType(), VK_LValue, OK_Ordinary); + // Build third init expr: 5 * size(int); + QualType UInt8PointerTy = SemaRef.Context.getPointerType( + SemaRef.Context.getIntTypeForBitwidth(8, false)); + Expr *ThirdInitLHSLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, FirstInitE) + .get(); + Expr *ThirdInitLHSRHS = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SecondInitE).get(); + ThirdInitLHSRHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, ThirdInitLHSRHS) + .get(); + Expr *ThirdInitLHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS).get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + ThirdInitLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, + ThirdInitLHS) + .get(); + Expr *ThirdInitRHS = IntegerLiteral::Create( + SemaRef.Context, llvm::APInt(32, 8), Int32Ty, SLoc); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); +} + +ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { + SourceLocation SLoc = ME->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, ME->getType()); + ME->setType(QT); + + MemberExpr *MemberPtr = nullptr; + if (QT->isFatPtrRecordType()) { + if (InsertCheckOffsetCall.first) { + llvm::SmallVector Args{ME, InsertCheckOffsetCall.second}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); + CheckCallExpr.push(CE); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + if (InsertCheckVersionCall) { + llvm::SmallVector Args{ME}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); + CheckCallExpr.push(CE); + InsertCheckVersionCall = false; + } + if (DesugarToMemberPtr) { + // Desugar s.p to s.p.raw_ptr + MemberPtr = BuildMemberForFatPtr(ME, "raw_ptr"); + DesugarToMemberPtr = false; + } + } + QualType BaseType = ME->getBase()->getType(); + if (ME->isArrow() && BaseType.isFatPtrType()) { + DesugarToMemberPtr = true; + InsertCheckVersionCall = true; + InsertCheckOffsetCall.first = true; + // Build offset arg expr, for example: + // 1. for p->arr[3] where p is fat ptr and p->arr[3] is int type, + // offset is `__builtin_offsetof(struct S, arr[3]) + sizeof(int)` + // 2. for p->a, where p is fat ptr and p->a is int type, + // offset is `__builtin_offsetof(struct S, a) + sizeof(int)` + QualType PointeeQT = BaseType->getFatPtrPointeeType(); + TypeSourceInfo *PointeeTInfo = + SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT, SLoc); + QualType ElementType; + SmallVector Comps; + if (CurrentASEToAccessMemberThroughFatPtr) { + VisitExprToBuildOffsetOfComponent( + CurrentASEToAccessMemberThroughFatPtr, SLoc, Comps); + ElementType = CurrentASEToAccessMemberThroughFatPtr->getType(); + CurrentASEToAccessMemberThroughFatPtr = nullptr; + } else { + VisitExprToBuildOffsetOfComponent(ME, SLoc, Comps); + ElementType = QT; + } + Expr *OffsetExprLHS = + SemaRef.BuildBuiltinOffsetOf(SLoc, PointeeTInfo, Comps, SLoc).get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *OffsetExprRHS = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(ElementType) / 8), Int32Ty, SLoc); + InsertCheckOffsetCall.second = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); + } + ME->setBase(BaseTransform::TransformExpr(ME->getBase()).get()); + return MemberPtr ? MemberPtr : ME; +} + +// Desugar fat qualified QualType ,for example: +// 1. `int *fat` will be desugared to `_FatPtr`; +// 2. `struct S *fat` will be desugared to `_FatPtr`; +// 3. `int *fat *` will be desugared to `_FatPtr *`; +// 4. `int *fat *fat` will be desugared to `_FatPtr<_FatPtr>`; +QualType +TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { + T = T.getCanonicalType(); + if (!T.isFatQualified() && !T->hasFatFields()) + return T; + if (!FatPtrTD) { + DeclContext::lookup_result Decls = + SemaRef.Context.getTranslationUnitDecl()->lookup( + DeclarationName(&SemaRef.Context.Idents.get("_FatPtr"))); + if (Decls.isSingleResult()) + if (ClassTemplateDecl *CTD = dyn_cast(Decls.front())) + FatPtrTD = CTD; + } + if (FatPtrTD) { + if (auto PT = dyn_cast(T)) { + QualType PointeeT = DesugarFatPtrType(SLoc, PT->getPointeeType()); + if (T.isFatQualified()) { + TemplateArgumentListInfo Args(SLoc, SLoc); + Args.addArgument(TemplateArgumentLoc( + TemplateArgument(PointeeT), + SemaRef.Context.getTrivialTypeSourceInfo(PointeeT, SLoc))); + QualType FatPtrRecordType = + SemaRef.CheckTemplateIdType(TemplateName(FatPtrTD), SLoc, Args); + if (!FatPtrRecordType.isNull() && + !SemaRef.RequireCompleteType(SLoc, FatPtrRecordType, + diag::err_fat_ptr_missing_specialization)) + return FatPtrRecordType; + } else + return SemaRef.Context.getPointerType(PointeeT); + } + return T; + } + SemaRef.Diag(SLoc, diag::err_fat_ptr_type_not_found) << ""; + return T; +} + +Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, + llvm::SmallVector &Args) { + if (!FatPtrFD[FDName]) { + DeclContext::lookup_result Decls = + SemaRef.Context.getTranslationUnitDecl()->lookup( + DeclarationName(&SemaRef.Context.Idents.get(FDName))); + if (Decls.isSingleResult()) + FatPtrFD[FDName] = dyn_cast(Decls.front()); + } + if (FunctionDecl *FD = FatPtrFD[FDName]) { + DeclRefExpr *FDRef = + SemaRef.BuildDeclRefExpr(FD, FD->getType(), VK_LValue, SLoc); + CallExpr *CE = + dyn_cast(SemaRef.BuildCallExpr(nullptr, FDRef, SLoc, Args, SLoc).get()); + // Handle first arg of _check_version and _check_version. + if (CE && CE->getNumArgs() > 0 && + CE->getArg(0)->getType()->isFatPtrRecordType()) + if (auto ICE = dyn_cast(CE->getArg(0))) + ICE->setType(ICE->getSubExpr()->getType()); + return CE; + } + SemaRef.Diag(SLoc, diag::err_fat_ptr_func_not_found) << ""; + return nullptr; +} + +MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, + std::string FieldName) { + QualType FatPtrQT = + DesugarFatPtrType(FatPtrBaseE->getBeginLoc(), FatPtrBaseE->getType()); + if (FatPtrQT->isRecordType()) + if (RecordDecl *FatPtrRD = FatPtrQT->getAs()->getDecl()) + if (FatPtrRD->getNameAsString() == "_FatPtr") { + RecordDecl::field_iterator it = FatPtrRD->field_begin(); + if (FieldName == "key_version") { + it++; + } else if (FieldName == "offset") { + it++; + it++; + } + FieldDecl *FD = *it; + return SemaRef.BuildMemberExpr( + FatPtrBaseE, false, FatPtrBaseE->getBeginLoc(), + NestedNameSpecifierLoc(), FatPtrBaseE->getBeginLoc(), FD, + DeclAccessPair::make(FatPtrRD, FD->getAccess()), false, + DeclarationNameInfo(), FD->getType(), VK_LValue, OK_Ordinary); + } + return nullptr; +} + +Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExprForRawPtr( + Expr *RawPtrE, QualType QT) { + SourceLocation SLoc = RawPtrE->getBeginLoc(); + llvm::APInt Zero(32, 0); + QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *SecondInitE = + IntegerLiteral::Create(SemaRef.Context, Zero, UInt32Ty, SLoc); + Expr *ThirdInitE = + IntegerLiteral::Create(SemaRef.Context, Zero, Int32Ty, SLoc); + return BuildFatPtrCompoundLiteralExpr(RawPtrE, SecondInitE, ThirdInitE, QT); +} + +Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExpr( + Expr *FirstInitE, Expr *SecondInitE, Expr *ThirdInitE, QualType QT) { + SourceLocation SLoc = FirstInitE->getBeginLoc(); + SmallVector Inits{FirstInitE, SecondInitE, ThirdInitE}; + Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); + ILE->setType(QT); + return SemaRef + .BuildCompoundLiteralExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(QT), SLoc, ILE) + .get(); +} + +// return true when accessing array member through FatPtr, +// such as `p->arr[3]`, `p->a.b.arr[3]` +bool +TransformFatPtr::IsAccessingArrayMemberThroughFatPtr(Expr *E) { + if (!CurrentASEToAccessMemberThroughFatPtr) { + if (auto ME = dyn_cast(E)) { + if (ME->getBase()->getType().isFatPtrType()) { + return true; + } else { + return IsAccessingArrayMemberThroughFatPtr(ME->getBase()); + } + } else if (auto ASE = dyn_cast(E)) { + return IsAccessingArrayMemberThroughFatPtr(ASE->getBase()); + } else if (auto ICE = dyn_cast(E)) { + return IsAccessingArrayMemberThroughFatPtr(ICE->getSubExpr()); + } else if (auto PE = dyn_cast(E)) { + return IsAccessingArrayMemberThroughFatPtr(PE->getSubExpr()); + } + } + return false; +} + +void TransformFatPtr::VisitExprToBuildOffsetOfComponent( + Expr *E, SourceLocation SLoc, + SmallVector &Comps) { + if (E->getType().isFatPtrType()) { + return; + } else if (auto ASE = dyn_cast(E)) { + VisitExprToBuildOffsetOfComponent(ASE->getBase(), SLoc, Comps); + Comps.push_back(Sema::OffsetOfComponent()); + Comps.back().isBrackets = true; + Comps.back().U.E = ASE->getIdx(); + Comps.back().LocStart = Comps.back().LocEnd = SLoc; + } else if (auto ME = dyn_cast(E)) { + VisitExprToBuildOffsetOfComponent(ME->getBase(), SLoc, Comps); + Comps.push_back(Sema::OffsetOfComponent()); + Comps.back().isBrackets = false; + Comps.back().U.IdentInfo = ME->getMemberDecl()->getIdentifier(); + Comps.front().LocStart = Comps.back().LocEnd = SLoc; + } else if (auto ICE = dyn_cast(E)) { + VisitExprToBuildOffsetOfComponent(ICE->getSubExpr(), SLoc, Comps); + } else if (auto PE = dyn_cast(E)) { + VisitExprToBuildOffsetOfComponent(PE->getSubExpr(), SLoc, Comps); + } +} + +void Sema::DesugarFunctionDeclWithFatPtr(FunctionDecl *FD) { + if (Diags.hasErrorOccurred() || + FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) + return; + // Skip function template and class template. + if (auto RD = dyn_cast(FD->getParent())) { + if (RD->getDescribedClassTemplate()) + return; + } + + // Desugar fat ptr in function. + TransformFatPtr TFP(*this, FD); + TFP.TransformFunctionDecl(); +} + +void Sema::DesugarGlobalVarDeclWithFatPtr(VarDecl *VD) { + if (Diags.hasErrorOccurred()) + return; + + TransformFatPtr TFP(*this); + TFP.TransformVarDecl(VD); +} + +void Sema::DesugarRecordDeclWithFatPtr(RecordDecl *RD) { + if (Diags.hasErrorOccurred() || RD->getDescribedClassTemplate()) + return; + + TransformFatPtr TFP(*this); + TFP.TransformRecordDecl(RD); +} + +bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, + SourceLocation RLoc) { + if (!CheckFatQualTypeCStyleCast(LHSType, RHSType)) { + Diag(RLoc, diag::err_fat_qualcheck_incompatible) + << CompleteTraitType(RHSType) << CompleteTraitType(LHSType); + return false; + } + return true; +} + +bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType) { + QualType RHSCanType = RHSType.getCanonicalType(); + QualType LHSCanType = LHSType.getCanonicalType(); + bool IsSameType = (LHSCanType.getTypePtr() == RHSCanType.getTypePtr()); + const auto *LHSPtrType = LHSType->getAs(); + const auto *RHSPtrType = RHSType->getAs(); + bool IsPointer = LHSPtrType && RHSPtrType; + QualType RHSRawType = RHSCanType.getUnqualifiedType(); + QualType LHSRawType = LHSCanType.getUnqualifiedType(); + + if (IsSameType) { + return true; + } + if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && + (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) { + return true; + } + if (IsPointer) { + if (Context.hasSameType(LHSRawType, RHSRawType)) { + return true; + } else if (LHSCanType.isFatQualified() && RHSCanType.isFatQualified()) { + // T1 *fat is allowed to cast to T2 *fat + return true; + } else if (TryDesugarTrait(RHSType)) { + return true; + } else { + return CheckFatQualTypeCStyleCast(LHSPtrType->getPointeeType(), + RHSPtrType->getPointeeType()); + } + } + return false; +} + +bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, + SourceLocation RLoc) { + QualType RHSCanType = RHSType.getCanonicalType(); + QualType LHSCanType = LHSType.getCanonicalType(); + const auto *LHSPtrType = LHSType->getAs(); + const auto *RHSPtrType = RHSType->getAs(); + bool IsPointer = LHSPtrType && RHSPtrType; + + if (LHSCanType.isFatQualified() == RHSCanType.isFatQualified()) { + if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { + if (TD->getTypeImpledVarDecl(RHSCanType->getPointeeType())) + return true; + } + if (Context.hasSameType(LHSCanType, RHSCanType)) { + return true; + } + if (LHSCanType->isVoidPointerType()) { + if (LHSCanType->getPointeeType().isConstQualified() == + RHSCanType->getPointeeType().isConstQualified()) + return true; + } + if (!IsPointer) { + return false; + } else { + return CheckFatQualTypeAssignment(LHSPtrType->getPointeeType(), + RHSPtrType->getPointeeType(), RLoc); + } + } + return false; +} + +bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { + QualType LHSCanType = LHSType.getCanonicalType(); + QualType RHSCanType = RHSExpr->getType().getCanonicalType(); + SourceLocation ExprLoc = RHSExpr->getBeginLoc(); + bool Res = true; + + if (LHSCanType.isFatQualified() || RHSCanType.isFatQualified()) { + if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { + if (RHSCanType->isPointerType()) { + QualType ImplType = RHSCanType->getPointeeType() + .getUnqualifiedType() + .getCanonicalType(); + ImplType.removeLocalOwned(); + if (TD->getTypeImpledVarDecl(ImplType)) + return true; + } + // trait T* fat <-> trait T* fat // legal + if (TD) { + QualType QT = + DesugarTraitToStructTrait(TD, LHSCanType, RHSExpr->getExprLoc()); + if (QT.getCanonicalType() == RHSCanType) { + return true; + } + } + } + + // Fat pointer can be inited by nullptr. + if (LHSCanType.isFatQualified() && isa(RHSExpr)) + return true; + + if (LHSCanType->isVoidPointerType() && RHSCanType->isPointerType()) { + if (LHSCanType->getPointeeType().isConstQualified() == + RHSCanType->getPointeeType().isConstQualified()) + return true; + } + + if (!Context.hasSameType(LHSCanType, RHSCanType)) + Res = false; + } else { + Res = CheckFatQualTypeAssignment(LHSType, RHSCanType, ExprLoc); + } + if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && + (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) + return true; + if (!Res) { + Diag(ExprLoc, diag::err_fat_qualcheck_incompatible) + << CompleteTraitType(RHSExpr->getType()) << CompleteTraitType(LHSType); + } + return Res; +} + +bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { + const FunctionProtoType *LHSFuncType = LHSType->getAs() + ->getPointeeType() + ->getAs(); + const FunctionProtoType *RHSFuncType = + RHSExpr->getType()->isFunctionPointerType() + ? RHSExpr->getType() + ->getAs() + ->getPointeeType() + ->getAs() + : RHSExpr->getType()->getAs(); + SourceLocation ExprLoc = RHSExpr->getBeginLoc(); + + // return if no 'fat' in both side + if (!LHSFuncType->hasFatRetOrParams() && !RHSFuncType->hasFatRetOrParams()) { + return true; + } + + if (!Context.hasSameType(LHSFuncType, RHSFuncType)) { + Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) + << LHSType << RHSExpr->getType(); + return false; + } + for (unsigned i = 0; i < LHSFuncType->getNumParams(); i++) { + QualType LHSParamType = LHSFuncType->getParamType(i); + QualType RHSParamType = RHSFuncType->getParamType(i); + if (!Context.hasSameType(LHSParamType, RHSParamType)) { + Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) + << LHSType << RHSExpr->getType(); + return false; + } + } + return true; +} + +// Build allocation unit RecordDecl for every global array var +// which has attribute fat_ptr_checked, for example: +// @code +// __attribute((fat_ptr_checked)) int arr[3] = {1, 2, 3}; +// @endcode +// We desugar this decl to: +// @code +// struct _arr_AllocationUnit { +// uint32_t lock_version; +// uint32_t size; +// int arr[3]; +// }; +// struct _arr_AllocationUnit _arr = { 0, 12, {1, 2, 3} }; +// @endcode +std::pair +Sema::BuildAllocationUnitForGlobalArrayVar(VarDecl *VD) { + SourceLocation SLoc = VD->getLocation(); + // Build RecordDecl + RecordDecl *RD = RecordDecl::Create( + Context, TagDecl::TagKind::TTK_Struct, Context.getTranslationUnitDecl(), + SLoc, SLoc, + &Context.Idents.get("_" + VD->getNameAsString() + "_AllocationUnit")); + // Add three Field + RD->startDefinition(); + QualType UInt32Ty = Context.getIntTypeForBitwidth(32, false); + FieldDecl *LockVersionField = FieldDecl::Create( + Context, RD, SLoc, SLoc, &Context.Idents.get("lock_version"), UInt32Ty, + Context.getTrivialTypeSourceInfo(UInt32Ty), nullptr, false, ICIS_NoInit); + FieldDecl *SizeField = FieldDecl::Create( + Context, RD, SLoc, SLoc, &Context.Idents.get("size"), UInt32Ty, + Context.getTrivialTypeSourceInfo(UInt32Ty), nullptr, false, ICIS_NoInit); + FieldDecl *VDTypeField = FieldDecl::Create( + Context, RD, SLoc, SLoc, &Context.Idents.get(VD->getNameAsString()), + VD->getType(), Context.getTrivialTypeSourceInfo(VD->getType()), nullptr, + false, ICIS_NoInit); + LockVersionField->setAccess(AS_public); + SizeField->setAccess(AS_public); + VDTypeField->setAccess(AS_public); + RD->addDecl(LockVersionField); + RD->addDecl(SizeField); + RD->addDecl(VDTypeField); + RD->completeDefinition(); + PushOnScopeChains(RD, TUScope); + + // Build VarDecl + VarDecl *NewVD = + VarDecl::Create(Context, Context.getTranslationUnitDecl(), SLoc, SLoc, + &Context.Idents.get("_" + VD->getNameAsString()), + QualType(RD->getTypeForDecl(), 0), nullptr, SC_None); + // Build InitExpr + // Build first init expr : 0 + Expr *LockVersionInit = + IntegerLiteral::Create(Context, llvm::APInt(32, 0), UInt32Ty, SLoc); + // Build second init expr : array size + Expr *SizeInit = IntegerLiteral::Create( + Context, llvm::APInt(32, Context.getTypeSize(VD->getType()) / 8), + UInt32Ty, SLoc); + // Build InitListExpr + SmallVector Inits{LockVersionInit, SizeInit}; + if (VD->getInit()) + Inits.push_back(VD->getInit()); + Expr *ILE = BuildInitList(SLoc, Inits, SLoc).get(); + ILE->setType(QualType(RD->getTypeForDecl(), 0)); + NewVD->setInit(ILE); + PushOnScopeChains(NewVD, TUScope); + Context.BSCDesugaredMap[VD].push_back(RD); + Context.BSCDesugaredMap[VD].push_back(NewVD); + return std::make_pair(RD, NewVD); +} +#endif // ENABLE_BSC \ No newline at end of file diff --git a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp index e050e6e38ab6..066ff527858f 100644 --- a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp +++ b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp @@ -398,7 +398,7 @@ bool Sema::CheckBorrowFunctionPointerType(QualType LHSType, Expr *RHSExpr) { return true; } if (!Context.hasSameType(LSHFuncType, RSHFuncType)) { - Diag(ExprLoc, diag::err_funcPtr_incompatible) + Diag(ExprLoc, diag::err_borrow_funcPtr_incompatible) << LHSType << RHSExpr->getType(); return false; } diff --git a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp index cad99765f657..f3813e181333 100644 --- a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp +++ b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp @@ -233,7 +233,7 @@ bool Sema::IsSafeConversion(QualType DestType, Expr *Expr) { // Init a owned or borrow pointer by nullptr is allowed in the safe zone if ((DestType.isOwnedQualified() && DestType->isPointerType()) || - DestType.isBorrowQualified()) { + DestType.isBorrowQualified() || DestType.isFatQualified()) { if (isa(Expr)) return true; } diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index 06b38b53b1b0..2370457c7091 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -191,9 +191,9 @@ bool Sema::HasSafeZoneInFunction(const FunctionDecl* FnDecl) { return HasSafeZoneInCompoundStmt(FuncBody); } -void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, +void Sema::BSCDataflowAnalysis(const FunctionDecl *FD, bool EnableOwnershipCheck, bool EnableNullabilityCheck) { - AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, D); + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, FD); AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true; AC.getCFGBuildOptions().AddEHEdges = false; @@ -205,7 +205,6 @@ void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, AC.getCFGBuildOptions().setAllAlwaysAdd(); if (AC.getCFG()) { - const FunctionDecl *FD = cast(D); unsigned NumNullabilityCheckErrorsInCurrFD = 0; LangOptions::NullCheckZone CheckZone = getLangOpts().getNullabilityCheck(); if (EnableNullabilityCheck && (CheckZone == LangOptions::NC_ALL || HasSafeZoneInFunction(FD))) { diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index c0febac51a37..245bf606e420 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -15,6 +15,7 @@ add_clang_library(clangSema BSC/SemaBSCDestructor.cpp BSC/SemaBSCOwnedStruct.cpp BSC/SemaBSCCoroutine.cpp + BSC/SemaBSCFatPtr.cpp BSC/SemaBSCOwnership.cpp BSC/SemaBSCSafeZone.cpp BSC/SemaBSCTrait.cpp diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 7150cf94981d..826b2f616102 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -438,7 +438,9 @@ void DeclSpec::forEachCVRUQualifier( Handle(TQ_owned, "owned", TQ_ownedLoc); if (TypeQualifiers & TQ_borrow) Handle(TQ_borrow, "borrow", TQ_borrowLoc); - #endif + if (TypeQualifiers & TQ_fat) + Handle(TQ_fat, "fat", TQ_fatLoc); +#endif if (TypeQualifiers & TQ_volatile) Handle(TQ_volatile, "volatile", TQ_volatileLoc); if (TypeQualifiers & TQ_restrict) @@ -635,7 +637,9 @@ const char *DeclSpec::getSpecifierName(TQ T) { #if ENABLE_BSC case DeclSpec::TQ_owned: return "owned"; case DeclSpec::TQ_borrow: return "borrow"; - #endif + case DeclSpec::TQ_fat: + return "fat"; +#endif case DeclSpec::TQ_restrict: return "restrict"; case DeclSpec::TQ_volatile: return "volatile"; case DeclSpec::TQ_atomic: return "_Atomic"; @@ -1011,7 +1015,10 @@ bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc) { case TQ_borrow: TQ_borrowLoc = Loc; return false; - #endif + case TQ_fat: + TQ_fatLoc = Loc; + return false; +#endif case TQ_restrict: TQ_restrictLoc = Loc; return false; case TQ_volatile: TQ_volatileLoc = Loc; return false; case TQ_unaligned: TQ_unalignedLoc = Loc; return false; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index a5a00c76f649..e0780e8c796b 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -2196,11 +2196,7 @@ static void markEscapingByrefs(const FunctionScopeInfo &FSI, Sema &S) { /// \param BlockType The type of the block expression, if D is a BlockDecl. Sema::PoppedFunctionScopePtr Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, - const Decl *D, QualType BlockType - #if ENABLE_BSC - , bool isBSCCoroutine - #endif - ) { + const Decl *D, QualType BlockType) { assert(!FunctionScopes.empty() && "mismatched push/pop!"); markEscapingByrefs(*FunctionScopes.back(), *this); @@ -2211,29 +2207,6 @@ Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, if (LangOpts.OpenMP) popOpenMPFunctionRegion(Scope.get()); -#if ENABLE_BSC - // If D does not use memory safety features like "owned, borrow, &mut, &const", - // we do not do borrow checking. - bool useSafeFeature = LangOpts.BSC ? FindSafeFeatures(dyn_cast_or_null(D)) : false; - bool EnableOwnershipCheck = useSafeFeature && !(getLangOpts().DisableOwnershipCheck); - bool EnableNullabilityCheck = LangOpts.BSC ? (getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE) : false; - if (EnableOwnershipCheck || EnableNullabilityCheck) { - if (!isBSCCoroutine && - (getDiagnostics().getNumErrors() == - getDiagnostics().getNumOwnershipErrors() + - getDiagnostics().getNumBorrowCheckErrors() + - getDiagnostics().getNumNullabilityCheckErrors()) && - D) - if (const auto *const CastReturn = dyn_cast_or_null(D)) { - auto md = dyn_cast_or_null(D); - // Don't check ownership rules of destructor parameters - if (!md || !md->isDestructor()) - BSCDataflowAnalysis(D, EnableOwnershipCheck, - EnableNullabilityCheck); - } - } -#endif - // Issue any analysis-based warnings. if (WP && D) AnalysisWarnings.IssueWarnings(*WP, Scope.get(), D, BlockType); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index b174416b2a54..741617c32050 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -2879,43 +2879,70 @@ void CastOperation::CheckCStyleCast() { SrcExpr = ExprError(); return; } + QualType SrcType = SrcExpr.get()->getType(); + QualType SrcCanType = SrcType.getCanonicalType(); + QualType DestCanType = DestType.getCanonicalType(); // bsc owned type CStyleCast - if (SrcExpr.get()->getType().getCanonicalType().isOwnedQualified() || - DestType.getCanonicalType().isOwnedQualified()) { - if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcExpr.get()->getType(), SrcExpr.get()->getExprLoc())) { + if (SrcCanType.isOwnedQualified() || DestCanType.isOwnedQualified()) { + if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } } // bsc borrow type CStyleCast - if (SrcExpr.get()->getType().getCanonicalType().isBorrowQualified() || - DestType.getCanonicalType().isBorrowQualified()) { - if (!Self.CheckBorrowQualTypeCStyleCast(DestType, SrcExpr.get()->getType(), SrcExpr.get()->getExprLoc())) { + if (SrcCanType.isBorrowQualified() || DestCanType.isBorrowQualified()) { + if (!Self.CheckBorrowQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { + SrcExpr = ExprError(); + return; + } + } + // bsc fat type CStyleCast + if (SrcCanType.isFatQualified() || DestCanType.isFatQualified()) { + if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } } if (const auto *LHSPtrType = DestType->getAs()) { - if (const auto *RHSPtrType = SrcExpr.get()->getType()->getAs()) { + if (const auto *RHSPtrType = SrcType->getAs()) { if (LHSPtrType->hasOwnedFields() || RHSPtrType->hasOwnedFields()) { - if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcExpr.get()->getType(), SrcExpr.get()->getExprLoc())) { + if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { + SrcExpr = ExprError(); + return; + } + } + if (LHSPtrType->hasBorrowFields() || RHSPtrType->hasBorrowFields()) { + if (!Self.CheckBorrowQualTypeCStyleCast( + DestType, SrcType, SrcExpr.get()->getExprLoc())) { + SrcExpr = ExprError(); + return; + } + } + if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { + if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } } } } - } - if (Self.getLangOpts().BSC && Self.IsTraitExpr(SrcExpr.get()) && - DestType->isPointerType()) { - if (!DestType->hasTraitType()) - SrcExpr = Self.ActOnTraitPointerCast(SrcExpr.get()); - if (DestType->isVoidPointerType()) { - Kind = CK_NoOp; - } else { - Kind = CK_BitCast; + if (Self.IsTraitExpr(SrcExpr.get()) && DestType->isPointerType()) { + if (!DestType->hasTraitType()) + SrcExpr = Self.ActOnTraitPointerCast(SrcExpr.get()); + if (DestType->isVoidPointerType()) { + Kind = CK_NoOp; + } else { + Kind = CK_BitCast; + } + return; + } else if (SrcCanType->isFatPtrRecordType() && DestType.isFatQualified()) { + return; } - return; } #endif diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8985133fdb8a..cd16fd679c63 100755 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16014,12 +16014,38 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // the declaration context below. Otherwise, we're unable to transform // 'this' expressions when transforming immediate context functions. +#if ENABLE_BSC + if (LangOpts.BSC) { + if (auto FD = dyn_cast_or_null(dcl)) { + // If FD does not use memory safety features like "owned, + // borrow, &mut, &const", we do not do borrow checking. + bool EnableOwnershipCheck = + !LangOpts.DisableOwnershipCheck && FindSafeFeatures(FD); + bool EnableNullabilityCheck = + LangOpts.getNullabilityCheck() != LangOptions::NC_NONE; + if ((EnableOwnershipCheck || EnableNullabilityCheck) && + getDiagnostics().getNumErrors() == + getDiagnostics().getNumOwnershipErrors() + + getDiagnostics().getNumBorrowCheckErrors() + + getDiagnostics().getNumNullabilityCheckErrors()) { + auto md = dyn_cast_or_null(FD); + // Don't check ownership rules of destructor parameters + if (!md || !md->isDestructor()) + BSCDataflowAnalysis(FD, EnableOwnershipCheck, + EnableNullabilityCheck); + } + // Desugar BSC Function. + DesugarDestructorCall(FD); + if (getLangOpts().EnableFatPtr) + DesugarFunctionDeclWithFatPtr(FD); + } + } +#endif + if (!IsInstantiation) PopDeclContext(); PopFunctionScopeInfo(ActivePolicy, dcl); - if (auto *FD = dyn_cast(dcl)) - DesugarDestructorCall(FD); // If any errors have occurred, clear out any temporaries that may have // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. @@ -19014,6 +19040,11 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (!Completed) Record->completeDefinition(); +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) + DesugarRecordDeclWithFatPtr(Record); +#endif + // Handle attributes before checking the layout. ProcessDeclAttributeList(S, Record, Attrs); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index bfd17fce4f1a..a1b6e7117af1 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8687,6 +8687,18 @@ static void handleFunctionLikeMacro(Sema &S, Decl *D, const ParsedAttr &Attrs) { static void handleOperatorAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) { D->addAttr(::new (S.Context) OperatorAttr(S.Context, Attrs)); } + +static void handleFatPtrCheckedAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) { + if (auto VD = dyn_cast(D)) { + if (VD->hasGlobalStorage() && VD->getType()->isConstantArrayType()) { + D->addAttr(::new (S.Context) FatPtrCheckedAttr(S.Context, Attrs)); + return; + } + } + S.Diag(Attrs.getLoc(), diag::err_fat_ptr_checked_attr_global_array_var); + Attrs.setInvalid(); + return; +} #endif //===----------------------------------------------------------------------===// @@ -9561,6 +9573,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_Operator: handleOperatorAttr(S, D, AL); break; + + case ParsedAttr::AT_FatPtrChecked: + handleFatPtrCheckedAttr(S, D, AL); + break; #endif } } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index cd1482829f89..018aaea10aaf 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5892,6 +5892,13 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, BaseExpr = LHSExp; IndexExpr = RHSExp; ResultType = PTy->getPointeeType(); +#if ENABLE_BSC + } else if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + LHSTy->isFatPtrRecordType()) { + BaseExpr = LHSExp; + IndexExpr = RHSExp; + ResultType = LHSTy->getFatPtrPointeeType(); +#endif } else if (const ObjCObjectPointerType *PTy = LHSTy->getAs()) { BaseExpr = LHSExp; @@ -10437,6 +10444,37 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } return false; } + +static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { + if (DstType.getCanonicalType().isFatQualified()) + if (SrcType.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + SrcType.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + if (SrcType.getCanonicalType().isFatQualified()) + if (DstType.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + DstType.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + if (DstType.getCanonicalType().isFatQualified()) + if (SrcType.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + SrcType.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + if (SrcType.getCanonicalType()->isRecordType() && + DstType.getCanonicalType()->isRecordType()) + if (RecordDecl *DstRD = + DstType.getCanonicalType()->getAs()->getDecl()) + if (DstRD->getNameAsString() == "_FatPtr") + if (RecordDecl *SrcRD = + SrcType.getCanonicalType()->getAs()->getDecl()) + if (SrcRD->getNameAsString() == "_FatPtr") + return true; + return false; +} #endif Sema::AssignConvertType @@ -10470,10 +10508,21 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (!IsSafeConversion(LHSType, RHS.get())) { return IncompatibleBSCSafeZone; } - if (RHS.get()->getType().getCanonicalType().isOwnedQualified() || LHSType.getCanonicalType().isOwnedQualified()) { + QualType LHSCanType = LHSType.getCanonicalType(); + QualType RHSCanType = RHS.get()->getType().getCanonicalType(); + if (RHSCanType.isOwnedQualified() || LHSCanType.isOwnedQualified()) { if (!CheckOwnedQualTypeAssignment(LHSType, RHS.get())) return IncompatibleOwnedPointer; } + if (RHSCanType.isBorrowQualified() || LHSCanType.isBorrowQualified()) { + if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) + return IncompatibleBorrowPointer; + } + if ((RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType()) && + (LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType())) { + if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) + return IncompatibleFatPointer; + } if (const auto *LHSPtrType = LHSType->getAs()) { if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { if (LHSPtrType->hasOwnedFields() || RHSPtrType->hasOwnedFields()) { @@ -10481,18 +10530,14 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleOwnedPointer; } } - } - } - - if (RHS.get()->getType().getCanonicalType().isBorrowQualified() || LHSType.getCanonicalType().isBorrowQualified()) { - if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) - return IncompatibleOwnedPointer; - } - if (const auto *LHSPtrType = LHSType->getAs()) { - if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { if (LHSPtrType->hasBorrowFields() || RHSPtrType->hasBorrowFields()) { if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) { - return IncompatibleOwnedPointer; + return IncompatibleBorrowPointer; + } + } + if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { + if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) { + return IncompatibleFatPointer; } } } @@ -10504,6 +10549,8 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleOwnedPointer; if (!CheckBorrowFunctionPointerType(LHSType, RHS.get())) return IncompatibleBorrowPointer; + if (!CheckFatFunctionPointerType(LHSType, RHS.get())) + return IncompatibleFatPointer; } } #endif @@ -14905,11 +14952,12 @@ bool Sema::IsAddrBorrowDerefOp(ExprResult &OrigOp) { return false; } -QualType Sema::GetBorrowAddressOperandQualType(QualType resultType, - ExprResult &Input, - const Expr *InputExpr, - UnaryOperatorKind &Opc, - SourceLocation OpLoc) { +// Handle BSC AddressOperand, Include &mut, &const, &mut*, &const*, &fat. +QualType Sema::GetBSCAddressOperandQualType(QualType resultType, + ExprResult &Input, + const Expr *InputExpr, + UnaryOperatorKind &Opc, + SourceLocation OpLoc) { if (Opc == UO_AddrMut || Opc == UO_AddrMutDeref) { if (Opc == UO_AddrMut && IsAddrBorrowDerefOp(Input)) { Opc = UO_AddrMutDeref; @@ -14963,6 +15011,28 @@ QualType Sema::GetBorrowAddressOperandQualType(QualType resultType, resultType = resultType.addConstBorrow(Context); } } + } else if (Opc == UO_AddrFat && !resultType.isNull()) { + if (auto ME = dyn_cast(InputExpr)) { + // if p is no-fat pointer, `&fat p->a` is not allowed. + if (ME->isArrow() && !ME->getBase()->getType().isFatQualified()) + Diag(OpLoc, diag::err_addr_fat_on_no_fat_pointer) + << InputExpr->getSourceRange(); + InputExpr = ME->getBase()->IgnoreImpCasts(); + } else if (auto ASE = dyn_cast(InputExpr)) { + // if p is no-fat pointer, `&fat p[5]` is not allowed. + QualType BaseQT = ASE->getBase()->IgnoreImpCasts()->getType(); + if (BaseQT->isPointerType() && !BaseQT.isFatQualified()) + Diag(OpLoc, diag::err_addr_fat_on_no_fat_pointer) + << InputExpr->getSourceRange(); + InputExpr = ASE->getBase()->IgnoreImpCasts(); + } + if (auto DRE = dyn_cast(InputExpr)) { + if (auto VD = dyn_cast(DRE->getDecl())) + if (VD->hasGlobalStorage() && !VD->hasAttr()) + Diag(OpLoc, diag::err_addr_fat_on_global_var_without_checked) + << InputExpr->getSourceRange(); + } + resultType.addFat(); } return resultType; } @@ -15237,6 +15307,10 @@ static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, else if (const ObjCObjectPointerType *OPT = OpTy->getAs()) Result = OPT->getPointeeType(); +#if ENABLE_BSC + else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && OpTy->isFatPtrRecordType()) + Result = OpTy->getFatPtrPointeeType(); +#endif else { ExprResult PR = S.CheckPlaceholderExpr(Op); if (PR.isInvalid()) return QualType(); @@ -15338,6 +15412,9 @@ static inline UnaryOperatorKind ConvertTokenKindToUnaryOpcode( #if ENABLE_BSC case tok::ampmut: Opc = UO_AddrMut; break; case tok::ampconst: Opc = UO_AddrConst; break; + case tok::ampfat: + Opc = UO_AddrFat; + break; #endif } return Opc; @@ -16375,19 +16452,20 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, #if ENABLE_BSC case UO_AddrMutDeref: case UO_AddrConstDeref: - resultType = GetBorrowAddressOperandQualType(Input.get()->getType(), Input, - InputExpr, Opc, OpLoc); + resultType = GetBSCAddressOperandQualType(Input.get()->getType(), Input, + InputExpr, Opc, OpLoc); break; case UO_AddrMut: case UO_AddrConst: + case UO_AddrFat: #endif case UO_AddrOf: resultType = CheckAddressOfOperand(Input, OpLoc); CheckAddressOfNoDeref(InputExpr); RecordModifiableNonNullParam(*this, InputExpr); #if ENABLE_BSC - resultType = GetBorrowAddressOperandQualType(resultType, Input, InputExpr, - Opc, OpLoc); + resultType = + GetBSCAddressOperandQualType(resultType, Input, InputExpr, Opc, OpLoc); #endif break; case UO_Deref: { @@ -17903,6 +17981,8 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, isInvalid = true; break; case Incompatible: + if (getLangOpts().BSC && IsFatPtrEqualExpr(*this, DstType, SrcType)) + return false; if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { if (Complained) *Complained = true; @@ -17918,6 +17998,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, #if ENABLE_BSC case IncompatibleOwnedPointer: case IncompatibleBorrowPointer: + case IncompatibleFatPointer: return false; case IncompatibleBSCSafeZone: return true; diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index a53524dd7b02..cacceda41ec9 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1075,8 +1075,17 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, ActOnMemberAccessExtraArgs *ExtraArgs) { QualType BaseType = BaseExprType; if (IsArrow) { +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + BaseType->isFatPtrRecordType()) + BaseType = BaseType->getFatPtrPointeeType(); + else { +#endif assert(BaseType->isPointerType()); BaseType = BaseType->castAs()->getPointeeType(); +#if ENABLE_BSC + } +#endif } R.setBaseObjectType(BaseType); @@ -1496,6 +1505,11 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, else if (const ObjCObjectPointerType *Ptr = BaseType->getAs()) BaseType = Ptr->getPointeeType(); +# if ENABLE_BSC + else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && + BaseType->isFatPtrRecordType()) + BaseType = BaseType->getFatPtrPointeeType(); +# endif else if (BaseType->isRecordType()) { // Recover from arrow accesses to records, e.g.: // struct MyRecord foo; @@ -2102,8 +2116,18 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, VK = VK_LValue; } else { QualType BaseType = BaseExpr->getType(); - if (IsArrow) BaseType = BaseType->castAs()->getPointeeType(); - + if (IsArrow) +# if ENABLE_BSC + { + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + BaseType->isFatPtrRecordType()) + BaseType = BaseType->getFatPtrPointeeType(); + else +# endif + BaseType = BaseType->castAs()->getPointeeType(); +# if ENABLE_BSC + } +# endif Qualifiers BaseQuals = BaseType.getQualifiers(); // GC attributes are never picked up by members. diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 439ca1fcf82b..f9348718d2c8 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8022,7 +8022,8 @@ BuiltinCandidateTypeSet::AddPointerWithMoreQualifiedTypeVariants(QualType Ty, #if ENABLE_BSC bool hasOwned = VisibleQuals.hasOwned(); bool hasBorrow = VisibleQuals.hasBorrow(); - #endif + bool hasFat = VisibleQuals.hasFat(); +#endif // Iterate through all strict supersets of BaseCVR. for (unsigned CVR = BaseCVR+1; CVR <= Qualifiers::CVRMask; ++CVR) { @@ -8036,7 +8037,11 @@ BuiltinCandidateTypeSet::AddPointerWithMoreQualifiedTypeVariants(QualType Ty, // Skip over borrow if no borrow found anywhere in the types. if ((CVR & Qualifiers::Borrow) && !hasBorrow) continue; - #endif + + // Skip over fat if no fat found anywhere in the types. + if ((CVR & Qualifiers::Fat) && !hasFat) + continue; +#endif // Skip over restrict if no restrict found anywhere in the types, or if // the type cannot be restrict-qualified. diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index f04325d2063b..5882e115de86 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1999,10 +1999,13 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, Qs.removeVolatile(); } #if ENABLE_BSC - if (getLangOpts().BSC && Qs.hasBorrow()) { - if (!T->isPointerType()) + if (getLangOpts().BSC && !T->isPointerType()) { + if (Qs.hasBorrow()) Diag(DS ? DS->getBorrowSpecLoc() : Loc, diag::err_typecheck_invalid_borrow_not_pointer) << T; + if (Qs.hasFat()) + Diag(DS ? DS->getFatSpecLoc() : Loc, + diag::err_typecheck_invalid_fat_not_pointer); } #endif diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index cb19ab6202fa..3dac2d46a7cf 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -758,7 +758,8 @@ void ASTStmtWriter::VisitUnaryOperator(UnaryOperator *E) { Record.push_back(HasFPFeatures); Record.AddStmt(E->getSubExpr()); #if ENABLE_BSC - if (E->getOpcode() == UO_AddrMut || E->getOpcode() == UO_AddrConst) + if (E->getOpcode() == UO_AddrMut || E->getOpcode() == UO_AddrConst || + E->getOpcode() == UO_AddrFat) Record.push_back(UO_AddrOf); else #endif diff --git a/clang/test/BSC/Negative/FatPtr/addr_fat.cbs b/clang/test/BSC/Negative/FatPtr/addr_fat.cbs new file mode 100644 index 000000000000..09c1b718876a --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/addr_fat.cbs @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -verify %s + +struct S { int a; }; +void test1() { + struct S *fat p1; + struct S *p2; + int *fat p3 = &fat p1->a; + int *fat p4 = &fat p2->a;// expected-error {{'&fat' on member of no-fat pointer is not allowed}} + float *fat p5 = &fat p1->a;// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p6 = (float *fat)&fat p1->a; + + int *fat p7; + int *p8; + int *fat p9 = &fat p7[1]; + int *fat p10 = &fat p8[1]; // expected-error {{'&fat' on member of no-fat pointer is not allowed}} + float *fat p11 = &fat p7[1];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p12 = (float *fat)&fat p7[1]; + + struct S s; + int arr[10]; + int *fat p13 = &fat s.a; + float *fat p14 = &fat s.a; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p15 = (float *fat)&fat s.a; + int *fat p16 = &fat arr[5]; + float *fat p17 = &fat arr[5];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p18 = (float *fat)&fat arr[5]; +} + +__attribute__((fat_ptr_checked)) int arr1[5]; +int arr2[5]; +__attribute__((fat_ptr_checked)) static int arr3[5]; +static int arr4[5]; +void test2() { + __attribute__((fat_ptr_checked)) static int arr5[5]; + static int arr6[5]; + int *fat p1 = &fat arr1[0]; + int *fat p2 = &fat arr2[0]; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + int *fat p3 = &fat arr3[0]; + int *fat p4 = &fat arr4[0]; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + int *fat p5 = &fat arr5[0]; + int *fat p6 = &fat arr6[0]; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} +} + +__attribute__((fat_ptr_checked)) int g1 = 10; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} +__attribute__((fat_ptr_checked)) static int g2 = 10; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} +extern __attribute__((fat_ptr_checked)) int g3; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} +int g4 = 10; +static int g5 = 10; +extern int g6; +void test3() { + int *fat p1 = &fat g4; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + int *fat p2 = &fat g5; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + int *fat p3 = &fat g6; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + + __attribute__((fat_ptr_checked)) static int x1 = 10; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} + static int x2 = 10; + int *fat p4 = &fat x2; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_cast.cbs b/clang/test/BSC/Negative/FatPtr/fat_cast.cbs new file mode 100644 index 000000000000..50008ac52448 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/fat_cast.cbs @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -verify %s +void foo1(int *fat p); +void foo2(int *p); +int *fat foo3(); +int * foo4(); + +typedef void (*PF1)(int *fat p); +typedef void (*PF2)(int *p); +typedef int *fat (*PF3)(); +typedef int * (*PF4)(); + +void test1() { //cast between function pointer and function + PF1 p1 = foo1; + PF1 p2 = foo2;// expected-error {{incompatible fat function pointer types, cannot cast 'PF1' (aka 'void (*)(int *fat)') to 'void (int *)'}} + PF2 p3 = foo1;// expected-error {{incompatible fat function pointer types, cannot cast 'PF2' (aka 'void (*)(int *)') to 'void (int *fat)'}} + PF2 p4 = foo2; + PF3 p5 = foo3; + PF3 p6 = foo4;// expected-error {{incompatible fat function pointer types, cannot cast 'PF3' (aka 'int *fat (*)(void)') to 'int *(void)'}} + PF4 p7 = foo3;// expected-error {{incompatible fat function pointer types, cannot cast 'PF4' (aka 'int *(*)(void)') to 'int *fat (void)'}} + PF4 p8 = foo4; +} + +void test2() { //cast between zero and fat pointer + int * fat b1 = 0; // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} + int * fat b2 = {0};// expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} + foo1(0); // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} +} + +void test3() { //cast between raw pointer and fat ptr + int a = 1; + // explicit cast between raw pointer and fat ptr is allowed. + int *fat p1 = nullptr; + const int *fat p2 = nullptr; + int *p3 = &a; + const int *p4 = &a; + // T * --> T *fat + p1 = (int *fat)&a; + p2 = (const int *fat)&a; + p1 = (int *fat)p3; + p2 = (const int *fat)p4; + p1 = (int *fat)foo4(); + foo1((int *fat)p3); + // T *fat --> T * + p3 = (int *)p1; + p4 = (const int *)p2; + p3 = (int *)foo3(); + foo2((int *)p1); + // implicit cast between raw pointer and fat ptr is not allowed. + // T * --> T *fat + p1 = p3; // expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} + p2 = p4; // expected-error {{incompatible fat types, cannot cast 'const int *' to 'const int *fat'}} + p1 = foo4();// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} + foo1(p3);// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} + // T *fat --> T * + p3 = p1; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} + p4 = p2; // expected-error {{incompatible fat types, cannot cast 'const int *fat' to 'const int *'}} + p3 = foo3();// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} + foo2(p1);// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} + + safe { + int *fat p1 = nullptr; + const int *fat p2 = nullptr; + int *p3 = (int *)p1; // expected-error {{conversion from type 'int *fat' to 'int *' is forbidden in the safe zone}} + const int *p4 = (const int *)p2; // expected-error {{conversion from type 'const int *fat' to 'const int *' is forbidden in the safe zone}} + // T * --> T *fat + p1 = (int *fat)p3; // expected-error {{conversion from type 'int *' to 'int *fat' is forbidden in the safe zone}} + p2 = (const int *fat)p4; // expected-error {{conversion from type 'const int *' to 'const int *fat' is forbidden in the safe zone}} + // T *fat --> T * + p3 = (int *)p1; // expected-error {{conversion from type 'int *fat' to 'int *' is forbidden in the safe zone}} + p4 = (const int *)p2; // expected-error {{conversion from type 'const int *fat' to 'const int *' is forbidden in the safe zone}} + } +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs new file mode 100644 index 000000000000..3006aa21235d --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -verify %s +struct R { + int fat p1; // expected-error {{only pointer type can be qualified by fat}} + const int fat p2; // expected-error {{only pointer type can be qualified by fat}} +}; +struct V { + T fat p1; // expected-error {{only pointer type can be qualified by fat}} + const T fat p2; // expected-error {{only pointer type can be qualified by fat}} +}; + +void f1(int fat a); // expected-error {{only pointer type can be qualified by fat}} +void f2(int fat a) {} // expected-error {{only pointer type can be qualified by fat}} +void f3(T fat a) {} // expected-error {{only pointer type can be qualified by fat}} + +void f4(int *fat a) {} +void f5(T *fat a) {} + + +void test() { + int fat b1; // expected-error {{only pointer type can be qualified by fat}} + const int fat b2; // expected-error {{only pointer type can be qualified by fat}} + int fat * b3; // expected-error {{only pointer type can be qualified by fat}} + const int fat * b4; // expected-error {{only pointer type can be qualified by fat}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs new file mode 100644 index 000000000000..5594d6b6d211 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs @@ -0,0 +1,12 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +__attribute__((fat_ptr_checked)) int arr[3]; +int main() { + int *fat p = &fat arr[3]; + *p = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs new file mode 100644 index 000000000000..2d8b7aae4bb6 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +__attribute__((fat_ptr_checked)) int arr[3]; + +int main() { + int *fat p = &fat arr[2]; + int *fat p1 = p + 1; + *p1 = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs new file mode 100644 index 000000000000..43d004ddc475 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs @@ -0,0 +1,11 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(3 * sizeof(int)); + int a = p[3]; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs new file mode 100644 index 000000000000..c3b97993ee12 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs @@ -0,0 +1,11 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include +struct S { int *fat p; }; +int main() { + struct S s = { checked_malloc(3 * sizeof(int)) }; + int a = s.p[3]; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs new file mode 100644 index 000000000000..910359d28473 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs @@ -0,0 +1,12 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p1 = p + 3; + int a = *p1; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs new file mode 100644 index 000000000000..9a6cbe7ba543 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct S { int a; }; +int main() { + struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p1 = p + 1; + p1->a = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs new file mode 100644 index 000000000000..8267ee6635cd --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct S { int a; int arr[3]; }; +int main() { + struct S *fat p1 = checked_malloc(sizeof(struct S) * 2); + struct S *fat p2 = p1 + 1; + int a = p2->arr[3]; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs new file mode 100644 index 000000000000..dedb1e9f8d70 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs @@ -0,0 +1,11 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct S { int arr[3]; }; +int main() { + struct S *fat p = checked_malloc(sizeof(struct S)); + int a = p->arr[3]; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs new file mode 100644 index 000000000000..474e6c77a4ab --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs @@ -0,0 +1,21 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output longstring 2>&1 | FileCheck %s + +#include +#define BUFSIZE 8 + +char *fat fat_strcpy(char *fat dest, const char *src) { + while (*src != '\0') { + *dest = *src; + dest = dest + 1; + src = src + 1; + } + return dest; +} + +int main(int argc, char **argv) { + char *fat buf = checked_malloc(sizeof(char)*BUFSIZE); + fat_strcpy(buf, argv[1]); + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs new file mode 100644 index 000000000000..b7cf5d4ce5e7 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + checked_free(p); + return 0; +} + +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs new file mode 100644 index 000000000000..adb31c07c81e --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = p1; + checked_free(p1); + checked_free(p2); + return 0; +} + +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs new file mode 100644 index 000000000000..a5a4b818647f --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs @@ -0,0 +1,16 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct G { int *fat a; }; +int main() { + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); + p->a = g.a; + checked_free(p->a); + checked_free(g.a); + return 0; +} + +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs new file mode 100644 index 000000000000..f9ddbb207a9c --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs @@ -0,0 +1,18 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int *fat test() { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + return p; +} + +int main() { + int *fat p = test(); + checked_free(p); + return 0; +} + +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs new file mode 100644 index 000000000000..42fc9676d354 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int)); + *p = 10; + checked_free(p); + *p = 10; + return 0; +} + +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs new file mode 100644 index 000000000000..31ff1217ea98 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs @@ -0,0 +1,15 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct S { int a; }; +int main() { + struct S *fat p = checked_malloc(sizeof(struct S)); + p->a = 10; + checked_free(p); + p->a = 10; + return 0; +} + +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs new file mode 100644 index 000000000000..3c9ef85f86d0 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs @@ -0,0 +1,17 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int *fat test() __attribute__((fat_ptr_checked)) { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + return p; +} + +int main() { + int *fat p = test(); + *p = 10; + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs new file mode 100644 index 000000000000..e0227f63c4db --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs @@ -0,0 +1,15 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct G { int *fat a; }; +int main() { + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); + p->a = g.a; + checked_free(p->a); + *g.a = 10; + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs new file mode 100644 index 000000000000..0c420fce218d --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs @@ -0,0 +1,29 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include +#define BUFSIZE 32 + +char *fat fat_strncpy(char *fat dest, const char *src, size_t n) { + size_t i; + for (i = 0; i < n && src[i] != '\0'; i++) { + dest[i] = src[i]; + } + for (; i < n; i++) { + dest[i] = '\0'; + } + return dest; +} + +int main() { + char *fat buf1 = checked_malloc(BUFSIZE); + checked_free(buf1); + + char *fat buf2 = checked_malloc(BUFSIZE); + memset((char *)buf2, 0, BUFSIZE); + + fat_strncpy(buf1, "hack", 5); + checked_free(buf2); + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs new file mode 100644 index 000000000000..4b00fec29811 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +void pass_fat(int *p) {} + +int main() { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + pass_fat((int *)p); + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs new file mode 100644 index 000000000000..07ea7ca2204c --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs @@ -0,0 +1,12 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int arr[3] = { 1, 2, 3 }; + int *fat p = &fat arr[3]; + *p = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs new file mode 100644 index 000000000000..80280bf71a33 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int arr[3] = { 1, 2, 3 }; + int *fat p = &fat arr[2]; + int *fat p1 = p + 1; + *p1 = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs new file mode 100644 index 000000000000..d4230082860d --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs @@ -0,0 +1,22 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output longstring 2>&1 | FileCheck %s + +#include +#define BUFSIZE 8 + +char *fat fat_strcpy(char *fat dest, const char *src) { + while (*src != '\0') { + *dest = *src; + dest = dest + 1; + src = src + 1; + } + return dest; +} + +int main(int argc, char **argv) { + char buf[BUFSIZE]; + char *fat p = &fat buf[0]; + fat_strcpy(p, argv[1]); + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs new file mode 100644 index 000000000000..c39bf785d13b --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs @@ -0,0 +1,17 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int *fat test() { + int local; + int *fat p = &fat local; + return p; +} + +int main() { + int *fat p = test(); + *p = 10; + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/global_array.cbs b/clang/test/BSC/Positive/FatPtr/global_array.cbs new file mode 100644 index 000000000000..7f4893eab5f0 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/global_array.cbs @@ -0,0 +1,23 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +#include + +__attribute__((fat_ptr_checked)) int arr[3]; +void test1() { + int *fat p = &fat arr[2]; + *p = 10; +} + +void test2() { + int *fat p = &fat arr[1]; + int *fat p1 = p + 1; + *p1 = 10; +} + +int main() { + test1(); + test2(); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/heap.cbs b/clang/test/BSC/Positive/FatPtr/heap.cbs new file mode 100644 index 000000000000..5c8a83374c7e --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/heap.cbs @@ -0,0 +1,94 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +#include +struct S { int a; }; +void test1() { + int *fat p1 = checked_malloc(sizeof(int)); + *p1 = 10; + checked_free(p1); + + struct S *fat p2 = checked_malloc(sizeof(struct S)); + p2->a = 10; + checked_free(p2); +} + +struct G { int *fat a; }; +void test2() { + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); + *p = g; + *p->a = 10; + checked_free(p->a); + checked_free(p); +} + +void test3() { + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); + *p1 = *p2; + checked_free(p1); + checked_free(p2); +} + +void test4() { + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); + p1 = p2; + checked_free(p1); //only free once +} + +void test5() { + int *fat p = checked_malloc(sizeof(int) * 3); + int a = p[2]; + checked_free(p); +} + +void test6() { + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = p1; + int *p3 = (int *)p1; + float *fat p4 = (float *fat)p1; + checked_free(p1); +} + +void test7() { + int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p1 = p + 2; + int a = *p1; +} + +void test8(int *fat p1, int *fat p2) { + p1 = checked_malloc(3 * sizeof(int)); + p2 = p1 + 2; + int a = *p1; + int b = *p2; +} + +struct K { int arr[3]; }; +void test9() { + struct K *fat p1 = checked_malloc(sizeof(struct K) * 2); + int a = p1->arr[3]; + int *fat p2 = &fat p1->arr[3]; + *p2 = 10; + + struct K *fat p3 = p1 + 1; + int b = p3->arr[2]; + int *fat p4 = &fat p3->arr[2]; + *p4 = 10; +} + +int main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + int *fat p1; + int *fat p2; + test8(p1, p2); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/stack.cbs b/clang/test/BSC/Positive/FatPtr/stack.cbs new file mode 100644 index 000000000000..c44dab32adca --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/stack.cbs @@ -0,0 +1,24 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +#include + +void test1() { + int arr[3] = { 1, 2, 3 }; + int *fat p = &fat arr[2]; + *p = 10; +} + +void test2() { + int arr[3] = { 1, 2, 3 }; + int *fat p = &fat arr[1]; + int *fat p1 = p + 1; + *p1 = 10; +} + +int main() { + test1(); + test2(); + return 0; +} \ No newline at end of file -- Gitee From 37449a28416bbb174eb5e980705b36baea4fa306 Mon Sep 17 00:00:00 2001 From: xiaoyu Date: Tue, 31 Dec 2024 17:05:03 +0800 Subject: [PATCH 2/2] [FatPtr] Desugar the function type of the self-call Expr 1. For self-call Expr, desugar the function type of the calling expression 2. nullptr auto cast in fat ptr 3. fix type check when assignment 4. Transform a classic binary sorting algorithm: Bisort in Olden case 2 tree add --- clang/include/clang/Sema/Sema.h | 1 + clang/lib/Sema/BSC/SemaBSCDestructor.cpp | 15 + clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 65 +++- clang/lib/Sema/SemaExpr.cpp | 45 +-- .../FatPtr/nullptr_cast/nullptr_cast.cbs | 41 +++ .../perform_anaysis/deref_loop/deref_loop.cbs | 39 +++ .../perform_anaysis/olden/bisort/bitonic.c | 303 +++++++++++++++++ .../olden/bisort_bsc/bitonic.cbs | 304 ++++++++++++++++++ .../perform_anaysis/olden/treeadd/node.c | 233 ++++++++++++++ .../perform_anaysis/olden/treeadd/tree.h | 25 ++ .../olden/treeadd_bsc/node.cbs | 193 +++++++++++ .../olden/treeadd_bsc/tree.hbs | 29 ++ .../destructor_call/destructor_call.cbs | 21 ++ 13 files changed, 1268 insertions(+), 46 deletions(-) create mode 100644 clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs create mode 100644 clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0babd61ee984..8bb64e308e37 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3228,6 +3228,7 @@ public: std::pair BuildAllocationUnitForGlobalArrayVar(VarDecl *VD); // BSC Destructor related. + bool IsCallDestructorExpr(Expr *E); BSCMethodDecl *getOrInsertBSCDestructor(RecordDecl *RD); void HandleBSCDestructorBody(RecordDecl *RD, BSCMethodDecl *Destructor, std::stack InstanceFields); diff --git a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp index a3593c7c9c8d..5d63abe8dc43 100644 --- a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp +++ b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp @@ -731,6 +731,21 @@ public: } }; +bool Sema::IsCallDestructorExpr(Expr *E) { + Expr *NakedE = E->IgnoreParens(); + if (auto *CastExpr = llvm::dyn_cast(NakedE)) { + if (Expr *SubExpr = CastExpr->getSubExpr()) { + if (auto *DRE = llvm::dyn_cast(SubExpr)) { + NamedDecl *NDecl = DRE->getDecl(); + if (auto *MD = dyn_cast(NDecl)) { + return MD->isDestructor(); + } + } + } + } + return false; +} + void Sema::DesugarDestructorCall(FunctionDecl *FD) { if (!getLangOpts().BSC) return; diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index 41a5c1ae7b21..e789857a4099 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -200,12 +200,14 @@ public: FatPtrFD["_new_version_number"] = nullptr; } + bool IsNullPtrExpr(Expr *E); // Entry to desugar FunctionDecl void TransformFunctionDecl(); // Entry to desugar global or local VarDecl void TransformVarDecl(VarDecl *VD); // Entry to desugar global or local RecordDecl void TransformRecordDecl(RecordDecl *RD); + void TransformSelfCallExprFunctionType(CallExpr *CE); StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); StmtResult TransformCompoundStmt(CompoundStmt *CS); @@ -258,6 +260,13 @@ private: SmallVector &Comps); }; +bool TransformFatPtr::IsNullPtrExpr (Expr *E) { + if (auto *CastExpr = dyn_cast(E)) { + E = CastExpr->getSubExpr(); + } + return isa(E); +} + void TransformFatPtr::TransformFunctionDecl() { // Desugar fat qualified return type. QualType ReturnTy = @@ -291,7 +300,7 @@ void TransformFatPtr::TransformVarDecl(VarDecl *VD) { QualType QT = DesugarFatPtrType(VD->getLocation(), VD->getType()); VD->setType(QT); if (Expr *Init = VD->getInit()) { - if (QT->isFatPtrRecordType() && isa(Init)) { + if (QT->isFatPtrRecordType() && IsNullPtrExpr(Init)) { // `int *fat p = nullptr;` should be desugared to // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); @@ -632,7 +641,7 @@ ExprResult TransformFatPtr::TransformInitListExpr(InitListExpr *ILE) { StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { Expr *RV = RS->getRetValue(); QualType ReturnQT = FD->getReturnType(); - if (ReturnQT->isFatPtrRecordType() && isa(RV)) { + if (ReturnQT->isFatPtrRecordType() && IsNullPtrExpr(RV)) { // if a function has fat pointer return type, such as `int *fat foo();` // `return nullptr;` should be desugared to // `return (_FatPtr){ nullptr, 0, 0 };` @@ -643,18 +652,37 @@ StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { return RS; } +void TransformFatPtr::TransformSelfCallExprFunctionType(CallExpr *CE) { + QualType FQT = FD->getType(); + Expr *CalleeExpr = CE->getCallee(); + if (auto *ImplCastExpr = dyn_cast(CalleeExpr)) { + ImplCastExpr->setType(SemaRef.Context.getPointerType(FQT)); + if (Expr *subExpr = ImplCastExpr->getSubExpr()) { + if (auto *declRefExpr = dyn_cast(subExpr)) { + declRefExpr->setType(FQT); + } + } + } +} + ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { CE->setType(DesugarFatPtrType(CE->getBeginLoc(), CE->getType())); FunctionDecl *CalleeFD = CE->getDirectCallee(); + // For self-call Expr, desugar the function type of the calling expression + if (CalleeFD == FD) { + TransformSelfCallExprFunctionType(CE); + } for (unsigned i = 0; i < CalleeFD->getNumParams(); i++) { QualType ParamQT = CalleeFD->getParamDecl(i)->getType(); Expr *ArgE = CE->getArg(i); - if (ParamQT->isFatPtrRecordType() && isa(ArgE)) { + if (ParamQT->isFatPtrRecordType() && IsNullPtrExpr(ArgE)) { // if a function has fat pointer parameter, such as // `void foo(int *fat p);` // `foo(nullptr)` should be desugared to // `foo((_FatPtr){ nullptr, 0, 0 })` - CE->setArg(i, BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT)); + Expr *CLE = BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT); + CLE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); + CE->setArg(i, CLE); } else CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); } @@ -773,13 +801,18 @@ ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { // `(_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` return HandleBOFatPtrAddOrSubInteger(BO); } else if (BO->isAssignmentOp() && LHSQT.isFatPtrType() && - isa(RHS)) { + IsNullPtrExpr(RHS)) { // `p = nullptr;` should be desugared to // `p = (_FatPtr){ nullptr, 0, 0 };` LHSQT = DesugarFatPtrType(SLoc, LHSQT); BO->setLHS(BaseTransform::TransformExpr(LHS).get()); BO->setRHS(BuildFatPtrCompoundLiteralExprForRawPtr(RHS, LHSQT)); return BO; + } else if (BO->isEqualityOp() && LHSQT.isFatPtrType() && IsNullPtrExpr(RHS)) { + // `p1 == p2` should be desugared to `p1.ptr == nullptr` + DesugarToMemberPtr = true; + BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); + return BO; } BO->setLHS(BaseTransform::TransformExpr(LHS).get()); BO->setRHS(BaseTransform::TransformExpr(RHS).get()); @@ -1215,6 +1248,11 @@ MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExprForRawPtr( Expr *RawPtrE, QualType QT) { + if (auto *ICE = dyn_cast(RawPtrE)) { + if (isa(ICE->getSubExpr())) { + RawPtrE = ICE->getSubExpr(); + } + } SourceLocation SLoc = RawPtrE->getBeginLoc(); llvm::APInt Zero(32, 0); QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); @@ -1338,8 +1376,7 @@ bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType) { if (IsSameType) { return true; } - if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && - (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) { + if (LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) { return true; } if (IsPointer) { @@ -1395,6 +1432,10 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { SourceLocation ExprLoc = RHSExpr->getBeginLoc(); bool Res = true; + // Fat pointer can be inited by nullptr. + if (LHSCanType.isFatPtrType() && isa(RHSExpr)) + return true; + if (LHSCanType.isFatQualified() || RHSCanType.isFatQualified()) { if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { if (RHSCanType->isPointerType()) { @@ -1414,24 +1455,18 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { } } } - - // Fat pointer can be inited by nullptr. - if (LHSCanType.isFatQualified() && isa(RHSExpr)) - return true; - if (LHSCanType->isVoidPointerType() && RHSCanType->isPointerType()) { if (LHSCanType->getPointeeType().isConstQualified() == RHSCanType->getPointeeType().isConstQualified()) return true; } - if (!Context.hasSameType(LHSCanType, RHSCanType)) Res = false; } else { Res = CheckFatQualTypeAssignment(LHSType, RHSCanType, ExprLoc); } - if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && - (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) + if ((LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) && + !(LHSCanType.isFatQualified() && RHSCanType.isFatQualified())) return true; if (!Res) { Diag(ExprLoc, diag::err_fat_qualcheck_incompatible) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 018aaea10aaf..11bb1b666c9b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7125,7 +7125,7 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, #if ENABLE_BSC if (getLangOpts().BSC) { // unsafe function call is forbidden in the safe zone - if ((IsInSafeZone()) && + if (!IsCallDestructorExpr(Fn) && IsInSafeZone() && (Fn->getType()->checkFunctionProtoType(SZ_None) || Fn->getType()->checkFunctionProtoType(SZ_Unsafe))) { Diag(Fn->getBeginLoc(), diag::err_unsafe_action) @@ -10446,33 +10446,16 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { - if (DstType.getCanonicalType().isFatQualified()) - if (SrcType.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - SrcType.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - if (SrcType.getCanonicalType().isFatQualified()) - if (DstType.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - DstType.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - if (DstType.getCanonicalType().isFatQualified()) - if (SrcType.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - SrcType.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - if (SrcType.getCanonicalType()->isRecordType() && - DstType.getCanonicalType()->isRecordType()) - if (RecordDecl *DstRD = - DstType.getCanonicalType()->getAs()->getDecl()) - if (DstRD->getNameAsString() == "_FatPtr") - if (RecordDecl *SrcRD = - SrcType.getCanonicalType()->getAs()->getDecl()) - if (SrcRD->getNameAsString() == "_FatPtr") - return true; + if (auto *Typedef = dyn_cast(DstType)) { + DstType = Typedef->getDecl()->getUnderlyingType(); + } + if (auto *Typedef = dyn_cast(SrcType)) { + SrcType = Typedef->getDecl()->getUnderlyingType(); + } + if (DstType.isFatPtrType() && + (SrcType.isFatPtrType() || SrcType->isNullPtrType())) { + return true; + } return false; } #endif @@ -10518,8 +10501,7 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) return IncompatibleBorrowPointer; } - if ((RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType()) && - (LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType())) { + if (RHSCanType.isFatPtrType() || LHSCanType.isFatPtrType()) { if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) return IncompatibleFatPointer; } @@ -17839,7 +17821,8 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, case IncompatiblePointer: #if ENABLE_BSC // int a = 1; trait F* f = &a; - if (getLangOpts().BSC && IsTraitEqualExpr(*this, DstType, SrcType, Loc)) + if (getLangOpts().BSC && (IsTraitEqualExpr(*this, DstType, SrcType, Loc) || + IsFatPtrEqualExpr(*this, DstType, SrcType))) return false; #endif if (Action == AA_Passing_CFAudited) { diff --git a/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs new file mode 100644 index 000000000000..9a47ca5dac2b --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs @@ -0,0 +1,41 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +#include +#include + +typedef struct node HANDLE; + +struct node { + int value; + HANDLE *fat left; +}; + +#define NIL ((HANDLE *) 0) +typedef int * fat myFatPtr; + +myFatPtr test() { + return nullptr; +} + +myFatPtr foo(int *fat p) { + return p; +} + +int main() { + HANDLE * fat a = nullptr; + myFatPtr b = foo(nullptr); + myFatPtr c = test(); + + if (a == nullptr) { + printf("a == nullptr\n"); + } + if (b == nullptr) { + printf("b == nullptr\n"); + } + if (c == nullptr) { + printf("c == nullptr\n"); + } + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs new file mode 100644 index 000000000000..ac7ec4d457e9 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs @@ -0,0 +1,39 @@ +// RUN: %clang %s -enable-fat-ptr -o %test.output +// RUN: %test.output 20 + +#include + +long dealwithargs(int argc, char *argv[]) +{ + int num = 0; + if (argc > 2) + num = atoi(argv[2]); + return 1L << num; +} + +void test_raw(long n) { + long *p = malloc(sizeof(long)); + while (n > 0) { + *p = n; + n--; + } +} + +void test_fat(long n) { + long *fat p = checked_malloc(sizeof(long)); + while (n > 0) { + *p = n; + n--; + } +} + +int main(int argc, char *argv[]) { + long n = dealwithargs(argc, argv); + int flag = atoi(argv[1]); + if (flag == 0) { + test_raw(n); + } else { + test_fat(n); + } + return 0; +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c new file mode 100644 index 000000000000..cc3f2cb278aa --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c @@ -0,0 +1,303 @@ +// RUN: %clang %s -o %test.output +// RUN: %test.output 300 + +#include +#include + +#define CONST_m1 10000 +#define CONST_b 31415821 +#define RANGE 100 + +typedef struct node HANDLE; + +struct node { + int value; + HANDLE* left; + HANDLE *right; +}; + +typedef struct future_cell_int{ + HANDLE *value; +} future_cell_int; + +extern void *malloc(unsigned long); + +#define NIL ((HANDLE *) 0) + +int NumNodes, NDim; + +int random_c(int); + +int flag=0,foo=0; + +#define LocalNewNode(h,v) \ +{ \ + h = (HANDLE *) malloc(sizeof(struct node)); \ + h->value = v; \ + h->left = NIL; \ + h->right = NIL; \ + }; + +#define NewNode(h,v,procid) LocalNewNode(h,v) + +int mylog(int num) { + int j=0,k=1; + + while(k 3) + flag = atoi(argv[3]); + else + flag = 1; + + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; + + if (argc > 1) + size = atoi(argv[1]); + else + size = 1 << 15; + + NDim = mylog(NumNodes); + return size; +} + +void InOrder(HANDLE *h) { + HANDLE *l, *r; + if ((h != NIL)) { + l = h->left; + r = h->right; + InOrder(l); + static unsigned char counter = 0; + if (counter++ == 0) /* reduce IO */ + printf("%d @ 0x%x\n",h->value, 0); + InOrder(r); + } +} + +int mult(int p, int q) { + int p1, p0, q1, q0; + + p1 = p/CONST_m1; p0 = p%CONST_m1; + q1 = q/CONST_m1; q0 = q%CONST_m1; + return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; +} + +/* Generate the nth random_c # */ +int skiprand(int seed, int n) { + for (; n; n--) seed=random_c(seed); + return seed; +} + +int random_c(int seed) { + return mult(seed,CONST_b)+1; +} + +HANDLE* RandTree(int n, int seed, int node, int level) { + int next_val,my_name; + future_cell_int f_left, f_right; + HANDLE *h; + my_name=foo++; + if (n > 1) { + int newnode; + if (levelleft = f_left.value; + h->right = f_right.value; + } else { + h = 0; + } + return h; +} + +void SwapValue(HANDLE *l, HANDLE *r) { + int temp,temp2; + + temp = l->value; + temp2 = r->value; + r->value = temp; + l->value = temp2; +} + +void +/***********/ +SwapValLeft(HANDLE *l, HANDLE *r, HANDLE *ll, HANDLE *rl, int lval, int rval) +/***********/ +{ + r->value = lval; + r->left = ll; + l->left = rl; + l->value = rval; +} + + +void +/************/ +SwapValRight(HANDLE *l, HANDLE *r, HANDLE *lr, HANDLE *rr, int lval, int rval) +/************/ +{ + r->value = lval; + r->right = lr; + l->right = rr; + l->value = rval; + /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ +} + +int +/********************/ +Bimerge(HANDLE *root, int spr_val, int dir) +/********************/ +{ int rightexchange; + int elementexchange; + HANDLE *pl,*pll,*plr; + HANDLE *pr,*prl,*prr; + HANDLE *rl; + HANDLE *rr; + int rv,lv; + + + /*printf("enter bimerge %x\n", root);*/ + rv = root->value; + + pl = root->left; + pr = root->right; + rightexchange = ((rv > spr_val) ^ dir); + if (rightexchange) + { + root->value = spr_val; + spr_val = rv; + } + + while ((pl != NIL)) + { + /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ + lv = pl->value; /* <------- 8.2% load penalty */ + pll = pl->left; + plr = pl->right; /* <------- 1.35% load penalty */ + rv = pr->value; /* <------ 57% load penalty */ + prl = pr->left; /* <------ 7.6% load penalty */ + prr = pr->right; /* <------ 7.7% load penalty */ + elementexchange = ((lv > rv) ^ dir); + if (rightexchange) + if (elementexchange) + { + SwapValRight(pl,pr,plr,prr,lv,rv); + pl = pll; + pr = prl; + } + else + { pl = plr; + pr = prr; + } + else + if (elementexchange) + { + SwapValLeft(pl,pr,pll,prl,lv,rv); + pl = plr; + pr = prr; + } + else + { pl = pll; + pr = prl; + } + } + if ((root->left != NIL)) + { + int value; + rl = root->left; + rr = root->right; + value = root->value; + + root->value=Bimerge(rl,value,dir); + spr_val=Bimerge(rr,spr_val,dir); + } + /*printf("exit bimerge %x\n", root);*/ + return spr_val; +} + +int +/*******************/ +Bisort(HANDLE *root, int spr_val, int dir) +/*******************/ +{ HANDLE *l; + HANDLE *r; + int val; + /*printf("bisort %x\n", root);*/ + if (root->left == NIL) /* <---- 8.7% load penalty */ + { + if (((root->value > spr_val) ^ dir)) + { + val = spr_val; + spr_val = root->value; + root->value =val; + } + } + else + { + int ndir; + l = root->left; + r = root->right; + val = root->value; + /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ + root->value=Bisort(l,val,dir); + ndir = !dir; + spr_val=Bisort(r,spr_val,ndir); + spr_val=Bimerge(root,spr_val,dir); + } + /*printf("exit bisort %x\n", root);*/ + return spr_val; +} + +int main(int argc, char **argv) { + HANDLE *h; + int sval; + int n; + + n = dealwithargs(argc,argv); + + printf("Bisort with %d size of dim %d\n", n, NDim); + + h = RandTree(n,12345768,0,0); + sval = random_c(245867) % RANGE; + if (flag) { + InOrder(h); + printf("%d\n",sval); + } + printf("**************************************\n"); + printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); + printf("**************************************\n"); + + sval=Bisort(h,sval,0); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + sval=Bisort(h,sval,1); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + return 0; +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs new file mode 100644 index 000000000000..4535551ee5b1 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs @@ -0,0 +1,304 @@ +// RUN: %clang %s -enable-fat-ptr -o %test.output +// RUN: %test.output 3000 + +#include +#include +#include + +#define CONST_m1 10000 +#define CONST_b 31415821 +#define RANGE 100 + +typedef struct node HANDLE; + +struct node { + int value; + HANDLE *fat left; + HANDLE *fat right; +}; + +typedef struct future_cell_int{ + HANDLE *fat value; +} future_cell_int; + +#define NIL ((HANDLE *) 0) + +int NumNodes, NDim; + +int random_c(int); + +int flag=0,foo=0; + +//FIXME +#define LocalNewNode(h,v) \ +{ \ + h = checked_malloc(sizeof(HANDLE)); \ + h->value = v; \ + h->left = (HANDLE *fat)NIL; \ + h->right = (HANDLE *fat)NIL; \ + }; + +#define NewNode(h,v,procid) LocalNewNode(h,v) + +int mylog(int num) { + int j=0,k=1; + + while(k 3) + flag = atoi(argv[3]); + else + flag = 1; + + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; + + if (argc > 1) + size = atoi(argv[1]); + else + size = 1 << 15; + + NDim = mylog(NumNodes); + return size; +} + +void InOrder(HANDLE *fat h) { + HANDLE *fat l, *fat r; + if ((h != nullptr)) { + l = h->left; + r = h->right; + InOrder(l); + static unsigned char counter = 0; + if (counter++ == 0) /* reduce IO */ + // printf("%d @ 0x%x\n",h->value, 0); + InOrder(r); + } +} + +int mult(int p, int q) { + int p1, p0, q1, q0; + + p1 = p/CONST_m1; p0 = p%CONST_m1; + q1 = q/CONST_m1; q0 = q%CONST_m1; + return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; +} + +/* Generate the nth random_c # */ +int skiprand(int seed, int n) { + for (; n; n--) seed=random_c(seed); + return seed; +} + +int random_c(int seed) { + return mult(seed, CONST_b) + 1; +} + +HANDLE *fat RandTree(int n, int seed, int node, int level) { + int next_val,my_name; + future_cell_int f_left, f_right; + HANDLE *fat h; + my_name=foo++; + if (n > 1) { + int newnode; + if (levelleft = f_left.value; + h->right = f_right.value; + } else { + h = checked_malloc(sizeof(HANDLE)); //FIXME + } + return h; +} + +void SwapValue(HANDLE *fat l, HANDLE *fat r) { + int temp,temp2; + + temp = l->value; + temp2 = r->value; + r->value = temp; + l->value = temp2; +} + +void +/***********/ +SwapValLeft(HANDLE *fat l, HANDLE *fat r, HANDLE *fat ll, HANDLE *fat rl, int lval, int rval) +/***********/ +{ + r->value = lval; + r->left = ll; + l->left = rl; + l->value = rval; +} + + +void +/************/ +SwapValRight(HANDLE *fat l, HANDLE *fat r, HANDLE *fat lr, HANDLE *fat rr, int lval, int rval) +/************/ +{ + r->value = lval; + r->right = lr; + l->right = rr; + l->value = rval; + /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ +} + +int +/********************/ +Bimerge(HANDLE *fat root, int spr_val, int dir) +/********************/ +{ int rightexchange; + int elementexchange; + HANDLE *fat pl,*fat pll,*fat plr; + HANDLE *fat pr,*fat prl,*fat prr; + HANDLE *fat rl; + HANDLE *fat rr; + int rv,lv; + + + /*printf("enter bimerge %x\n", root);*/ + rv = root->value; + + pl = root->left; + pr = root->right; + rightexchange = ((rv > spr_val) ^ dir); + if (rightexchange) + { + root->value = spr_val; + spr_val = rv; + } + + while ((pl != nullptr)) + { + /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ + lv = pl->value; /* <------- 8.2% load penalty */ + pll = pl->left; + plr = pl->right; /* <------- 1.35% load penalty */ + rv = pr->value; /* <------ 57% load penalty */ + prl = pr->left; /* <------ 7.6% load penalty */ + prr = pr->right; /* <------ 7.7% load penalty */ + elementexchange = ((lv > rv) ^ dir); + if (rightexchange) + if (elementexchange) + { + SwapValRight(pl,pr,plr,prr,lv,rv); + pl = pll; + pr = prl; + } + else + { pl = plr; + pr = prr; + } + else + if (elementexchange) + { + SwapValLeft(pl,pr,pll,prl,lv,rv); + pl = plr; + pr = prr; + } + else + { pl = pll; + pr = prl; + } + } + HANDLE *fat root_left = root->left; + if (root_left != nullptr) + { + int value; + rl = root->left; + rr = root->right; + value = root->value; + + root->value=Bimerge(rl,value,dir); + spr_val=Bimerge(rr,spr_val,dir); + } + /*printf("exit bimerge %x\n", root);*/ + return spr_val; +} + +int +/*******************/ +Bisort(HANDLE *fat root, int spr_val, int dir) +/*******************/ +{ HANDLE *fat l; + HANDLE *fat r; + int val; + /*printf("bisort %x\n", root);*/ + HANDLE *fat root_left = root->left; + if (root_left == nullptr) /* <---- 8.7% load penalty */ + { + if (((root->value > spr_val) ^ dir)){ + val = spr_val; + spr_val = root->value; + root->value =val; + } + } + else + { + int ndir; + l = root->left; + r = root->right; + val = root->value; + /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ + root->value=Bisort(l,val,dir); + ndir = !dir; + spr_val=Bisort(r,spr_val,ndir); + spr_val=Bimerge(root,spr_val,dir); + } + /*printf("exit bisort %x\n", root);*/ + return spr_val; +} + +int main(int argc, char **argv) { + HANDLE *fat h; + int sval; + int n; + + n = dealwithargs(argc,argv); + + printf("Bisort with %d size of dim %d\n", n, NDim); + + h = RandTree(n,12345768,0,0); + sval = random_c(245867) % RANGE; + if (flag) { + InOrder(h); + printf("%d\n",sval); + } + printf("**************************************\n"); + printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); + printf("**************************************\n"); + + sval=Bisort(h,sval,0); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + sval=Bisort(h,sval,1); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + return 0; +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c new file mode 100644 index 000000000000..c8af4371bf4b --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c @@ -0,0 +1,233 @@ +// RUN: %clang %s -DTORONTO -o %test.output +// RUN: %test.output 16 + +/* For copyright information, see olden_v1.0/COPYRIGHT */ + +/* node.c + */ +#ifndef TORONTO +#include +#include +#include "mem-ref.h" +#endif + +#ifdef FUTURES +#include "future-cell.h" +#endif + +#include +#include "tree.h" + +#ifdef TORONTO +int NumNodes; +#else +int __NumNodes; +#endif + +typedef struct { + long level; +} startmsg_t; + +#ifndef TORONTO +void filestuff() +{ + CMMD_fset_io_mode(stdout, CMMD_independent); + fcntl(fileno(stdout), F_SETFL, O_APPEND); + if (CMMD_self_address()) exit(0); + __InitRegs(0); +} +#endif + +int dealwithargs(int argc, char *argv[]) +{ + int level; + +#ifdef TORONTO + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; +#else + if (argc > 2) + __NumNodes = atoi(argv[2]); + else + __NumNodes = 4; +#endif + + if (argc > 1) + level = atoi(argv[1]); + else + level = 16; + + return level; +} + +tree_t *TreeAlloc (int level, int lo, int proc) { + if (level == 0) + return NULL; + else { + struct tree *new, *right, *left; + new = (struct tree *) malloc(sizeof(tree_t)); + left = TreeAlloc(level -1, lo+proc/2, proc/2); + right=TreeAlloc(level-1,lo,proc/2); + new->val = 1; + new->left = (struct tree *) left; + new->right = (struct tree *) right; + return new; + } +} + +/* TreeAdd: + */ +int TreeAdd (tree_t *t) +{ + if (t == NULL) { + return 0; + } + else { +#ifdef FUTURES + future_cell_int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; + RPC(tleft, tleft,TreeAdd,&(leftval)); + NOTEST(); + tright = t->right; + rightval = TreeAdd(tright); + RTOUCH(&leftval); + /*chatting("after touch @ 0x%x\n",t);*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval.value + rightval + value; + RETEST(); +#else + int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; /* <---- 57% load penalty */ + leftval = TreeAdd(tleft); + tright = t->right; /* <---- 11.4% load penalty */ + rightval = TreeAdd(tright); + /*chatting("after touch\n");*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval + rightval + value; +#endif + } +} /* end of TreeAdd */ + +int main (int argc, char *argv[]) +{ + tree_t *root; + int level,result; + +#ifdef FUTURES + level = SPMDInit(argc,argv); +#else +#ifndef TORONTO + filestuff(); +#endif + level = dealwithargs(argc, argv); +#endif +#ifndef TORONTO + CMMD_node_timer_clear(0); + CMMD_node_timer_clear(1); +#endif +#ifdef TORONTO + chatting("Treeadd with %d levels on %d processors \n", + level, NumNodes); +#else + chatting("Treeadd with %d levels on %d processors \n", + level, __NumNodes); +#endif + /* only processor 0 will continue here. */ + chatting("About to enter TreeAlloc\n"); +#ifndef TORONTO + CMMD_node_timer_start(0); +#endif + +#ifdef TORONTO + root = TreeAlloc (level, 0, NumNodes); +#else + root = TreeAlloc (level, 0, __NumNodes); +#endif + +#ifndef TORONTO + CMMD_node_timer_stop(0); +#endif + chatting("About to enter TreeAdd\n"); + +#ifndef PLAIN + ClearAllStats(); +#endif +#ifndef TORONTO + CMMD_node_timer_start(1); +#endif +{ int i; for (i = 0; i < 100; ++i) + result = TreeAdd (root); +} +#ifndef TORONTO + CMMD_node_timer_stop(1); +#endif + chatting("Received result of %d\n",result); + +#ifndef TORONTO + chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); + chatting(/* TreeAdd: + */ +int TreeAdd (tree_t *t) +{ + if (t == NULL) { + return 0; + } + else { +#ifdef FUTURES + future_cell_int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; + RPC(tleft, tleft,TreeAdd,&(leftval)); + NOTEST(); + tright = t->right; + rightval = TreeAdd(tright); + RTOUCH(&leftval); + /*chatting("after touch @ 0x%x\n",t);*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval.value + rightval + value; + RETEST(); +#else + int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; /* <---- 57% load penalty */ + leftval = TreeAdd(tleft); + tright = t->right; /* <---- 11.4% load penalty */ + rightval = TreeAdd(tright); + /*chatting("after touch\n");*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval + rightval + value; +#endif + } +} /* end of TreeAdd */ +"Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); +#endif + +#ifdef FUTURES + __ShutDown(); +#endif + exit(0); +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h new file mode 100644 index 000000000000..b8d075e41cb0 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h @@ -0,0 +1,25 @@ +/* For copyright information, see olden_v1.0/COPYRIGHT */ + +/* tree.h + */ + +#ifdef TORONTO +#include +#define chatting printf +#define PLAIN +#endif + +typedef struct tree { + int val; + struct tree *left, *right; +} tree_t; + +tree_t *TreeAlloc (int level, int lo, int hi); +int TreeAdd (tree_t *t); + + + + + + + diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs new file mode 100644 index 000000000000..f859e368b7ae --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs @@ -0,0 +1,193 @@ +// RUN: %clang %s -DTORONTO -enable-fat-ptr -o %test.output +// RUN: %test.output 16 + +#ifndef TORONTO +#include +#include +#endif + +#ifdef FUTURES +#include "future-cell.h" +#endif + +#ifndef TORONTO +#include "mem-ref.h" +#endif + +#include +#include "tree.hbs" + +#ifdef TORONTO +int NumNodes; +#else +int __NumNodes; +#endif + +extern int atoi(const char *); + +#ifndef TORONTO +void filestuff() +{ + CMMD_fset_io_mode(stdout, CMMD_independent); + fcntl(fileno(stdout), F_SETFL, O_APPEND); + if (CMMD_self_address()) exit(0); + __InitRegs(0); +} +#endif + +int dealwithargs(int argc, char *argv[]) +{ + int level; + +#ifdef TORONTO + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; +#else + if (argc > 2) + __NumNodes = atoi(argv[2]); + else + __NumNodes = 4; +#endif + + if (argc > 1) + level = atoi(argv[1]); + else + level = 16; + + return level; +} + +typedef struct { + long level; +} startmsg_t; + +tree_t *fat TreeAlloc (int level, int lo, int proc) { + if (level == 0) + return nullptr; + else { + tree_t *fat new; + tree_t *fat right; + tree_t *fat left; + new = checked_malloc(sizeof(tree_t)); + left = TreeAlloc(level -1, lo+proc/2, proc/2); + right = TreeAlloc(level-1,lo,proc/2); + new->val = 1; + new->left = left; + new->right = right; + return new; + } +} + +/* TreeAdd: + */ +int TreeAdd (tree_t *fat t) +{ + if (t == nullptr) { + return 0; + } + else { +#ifdef FUTURES + future_cell_int leftval; + int rightval; + tree_t *fat tleft, *fat tright; + int value; + + tleft = t->left; + RPC(tleft, tleft,TreeAdd,&(leftval)); + NOTEST(); + tright = t->right; + rightval = TreeAdd(tright); + RTOUCH(&leftval); + /*chatting("after touch @ 0x%x\n",t);*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval.value + rightval + value; + RETEST(); +#else + int leftval; + int rightval; + tree_t *fat tleft; + tree_t *fat tright; + int value; + + tleft = t->left; /* <---- 57% load penalty */ + leftval = TreeAdd(tleft); + tright = t->right; /* <---- 11.4% load penalty */ + rightval = TreeAdd(tright); + /*chatting("after touch\n");*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval + rightval + value; +#endif + } +} /* end of TreeAdd */ + +int main (int argc, char *argv[]) +{ + tree_t *fat root; + int level, result; + +#ifdef FUTURES + level = SPMDInit(argc,argv); +#else +#ifndef TORONTO + filestuff(); +#endif + level = dealwithargs(argc, argv); +#endif +#ifndef TORONTO + CMMD_node_timer_clear(0); + CMMD_node_timer_clear(1); +#endif +#ifdef TORONTO + chatting("Treeadd with %d levels on %d processors \n", + level, NumNodes); +#else + chatting("Treeadd with %d levels on %d processors \n", + level, __NumNodes); +#endif + /* only processor 0 will continue here. */ + chatting("About to enter TreeAlloc\n"); +#ifndef TORONTO + CMMD_node_timer_start(0); +#endif + int a0 = 0; +#ifdef TORONTO + root = TreeAlloc (level, a0, NumNodes); +#else + root = TreeAlloc (level, a0, __NumNodes); +#endif + +#ifndef TORONTO + CMMD_node_timer_stop(0); +#endif + chatting("About to enter TreeAdd\n"); + +#ifndef PLAIN + ClearAllStats(); +#endif +#ifndef TORONTO + CMMD_node_timer_start(1); +#endif +{ int i; for (i = 0; i < 100; ++i) + result = TreeAdd(root); +} +#ifndef TORONTO + CMMD_node_timer_stop(1); +#endif + chatting("Received result of %d\n",result); + +#ifndef TORONTO + chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); + chatting("Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); +#endif + +#ifdef FUTURES + __ShutDown(); +#endif + exit(0); +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs new file mode 100644 index 000000000000..0b8014a09f3d --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs @@ -0,0 +1,29 @@ +/* For copyright information, see olden_v1.0/COPYRIGHT */ + +/* tree.h + */ + +#ifdef TORONTO +#include +#define chatting printf +#define PLAIN +#endif + +#include +typedef struct tree tree_t; + +struct tree { + int val; + tree_t *fat left; + tree_t *fat right; +}; + +tree_t *fat TreeAlloc (int level, int lo, int hi); +int TreeAdd (tree_t *fat t); + + + + + + + diff --git a/clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs b/clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs new file mode 100644 index 000000000000..9c5c2136db11 --- /dev/null +++ b/clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs @@ -0,0 +1,21 @@ +// RUN: %clang %s -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +owned struct RawVec { +public: + T a; + ~RawVec(This this) {} +}; + +safe int test(void) { + unsafe { + RawVec rv = { .a = 1}; + } + return 0; +} + +int main() { + int a = test(); + return 0; +} -- Gitee