diff --git a/clang/include/clang/AST/BSC/WalkerBSC.h b/clang/include/clang/AST/BSC/WalkerBSC.h index 5f8435f9c8d0a18fc15e4153a6382287fa106451..5db08af81cee016161eb351394b146545486c69e 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 9b7a1d4efe969e2269c5f66d011329bb00dd4fda..89258bedde76dddf432eacb11421f32ae2a36b1a 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 0c48c0a51353f4cdefb3009817ecdc77f84bdee7..b744276b1b46484cd762b7cfef2c2f6186070b7d 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 da654e5a616bc2067c770e923ba4a9e1eac2d70a..b013581c178b6d9b0317c8e74d971ec749034db8 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 cdaca83b0f61cfce23f20183ff8f4f356ea5ed1f..22f7e986a5b07413d4c246e91f442d9a131a99a5 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 1bc3ca7c902df928520897622e782fe9006e4cb2..6cc7de54bf6ad7d934ab583018c0d2bc6f3db74a 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 da4060a40f0eb53bad3ebee1078b125938fae88c..da40644d24cd095bdd232c14d9a277c19553c9ed 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 6530a91d7f785238abe87acc4710b389e99104a9..01b65d0b44a6ae204953b501ab1205460b42804a 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 0def6903efe1341709f6d73ebc912c2d1a8af691..e2f7b89cff7c98f57848f8b0ea4ac4d833ff922d 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 1b7d0328d08c4f108fc4d1f91b02264f32eb13aa..6c782e581767e5cce5d789df841a79f5c25bab31 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 f16d5abd33180b41b06712bc983b4a409a347449..e0bc8603e4f68f58c18bdcfc2787a4be45728592 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 b02939c4439ffdd9e625a82d7c9e7c14b55d02d9..071a7e7c1766ad6fcefdc8f23cc2afacaf90e1a4 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,7 +3214,21 @@ 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 DesugarFunctionWithFatPtr(FunctionDecl *FD); + void DesugarStructWithFatPtr(RecordDecl *RD); + void DesugarGlobalVarWithFatPtr(VarDecl *VD); + std::pair + BuildGlobalArrayAllocationUnit(VarDecl *VD); // BSC Destructor related. + bool IsCallDestructorExpr(Expr *E); BSCMethodDecl *getOrInsertBSCDestructor(RecordDecl *RD); void HandleBSCDestructorBody(RecordDecl *RD, BSCMethodDecl *Destructor, std::stack InstanceFields); @@ -12307,20 +12317,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 +12413,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 +12448,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 11d3138573fd14aa6bede638da4ddac0794a0c7f..0d34dfc803771374ed2080192fccdccf35a92674 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 196d193d5b249886f6a23f4408c39fa40d920f34..38e841e5c02d662027af609976bbe2a9466a47b5 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 bba9b782c748f8eec03dac9fdf0fce091154196a..f7e1cbf52688090940a11e939e4deca270553209 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 c55477431d52f1296d758eb2ef8ecc74a949edd5..025a70a754fa08f99660a9426e6ca21d5bbf3806 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 0d7aa2caeab15f0540f409300a5fd9aa32210084..894692ab2d5c2e57e7cf60a4db7131e89ea0edaa 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 1aea4e166576c434f2cababaec592974fd07a8e4..fdafb8620f7cb3058350959b6682849ef3957478 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 0cc42b5cf96f6d9f8f527c5e7a5df2a55e477256..8279e434ad67bd3673cd9a78fa56267cf9c95ccc 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 bc9243999a9c8dceb779aaa1aa5a2568c14eb098..7b4d8d3d47f8651a4f69b18beb9f3cbfc79e3b3d 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 e6ab441058d0c5f56bb0b7cc9fa37d60d9aede8c..89b71e7a37e4fa3bc96ff9f9f3a472131830f644 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 dfa78bf59c65808acaadf4c0d1371c090afe2b11..4ff156690c8210a2be50d21282060cad284a629e 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 73b05690537d94927aab3e7cd10e96b5c99639dd..f5d307974bd63420b9ec0c241d9c0bb19c46a7f4 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 c689fa310858f069fa18497a1de4dd465472b0a3..9a0fba739fe036bb68149e589a11e4acdbb9ac6e 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 f32da6d90e6514095f24b1246818e9a4a2d4431c..543964111f2c93064b566b926edfa8da3199b154 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 158dfb759f75964c6455f34a819f9c10cfa6d518..81faf5ebd67c13378bfb83ef3e789adfd73f6a36 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 0000000000000000000000000000000000000000..49cf86f678cc33554473ea7ba5ce0dc6304275e0 --- /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"); +} +// __always_inline +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 27cdfc68c13e63c7a949642134aa84473533606f..c38f31d4324ac60c44b7aa5727e14ed29c0ad352 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 3faa4ceba9ca2408482efd1a7d14c24dbdb63e30..d008151a3273b960a3d7b9beacbea0f0ed0d145b 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.BuildGlobalArrayAllocationUnit(VD); + DeclsInGroup.push_back(Decls.first); + DeclsInGroup.push_back(Decls.second); + } else if (QT.isFatQualified() || QT->hasFatFields()) { + Actions.DesugarGlobalVarWithFatPtr(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 f92348520b7c6ac7140c2083f1e7bd2c7a224aeb..fa64e38058325cfdff3135dd80141cf12a98c86f 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 a67c1c8dfea5da0973605bc68831a3551aff9036..18aa747e27fa93c6ae2c873b3fa1efed87933929 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 f44821d2a515641058714dee2350a6531bcf3eb4..5d63abe8dc43817cdb88fee1ed3a76225eecae5a 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" @@ -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 new file mode 100644 index 0000000000000000000000000000000000000000..b1a4725824ca0b756b2ac15f3247fe211c05bfa7 --- /dev/null +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -0,0 +1,1667 @@ +//===--- 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; + +static ClassTemplateDecl *lookupFatPtrTemplateDecl(Sema &S, SourceLocation SLoc) { + DeclContext::lookup_result Decls = + S.Context.getTranslationUnitDecl()->lookup( + DeclarationName(&S.Context.Idents.get("_FatPtr"))); + if (Decls.isSingleResult()) + if (ClassTemplateDecl *CTD = dyn_cast(Decls.front())) + return CTD; + S.Diag(SLoc, diag::err_fat_ptr_type_not_found) << ""; + return nullptr; +} + +static QualType DesugarFatPtrQT(Sema &S, SourceLocation SLoc, + QualType T, ClassTemplateDecl *FatPtrTD) { + T = T.getCanonicalType(); + if (auto PT = dyn_cast(T)) { + QualType PointeeT = DesugarFatPtrQT(S, SLoc, PT->getPointeeType(), FatPtrTD); + if (T.isFatQualified()) { + TemplateArgumentListInfo Args(SLoc, SLoc); + Args.addArgument(TemplateArgumentLoc( + TemplateArgument(PointeeT), + S.Context.getTrivialTypeSourceInfo(PointeeT, SLoc))); + QualType FatPtrRecordType = + S.CheckTemplateIdType(TemplateName(FatPtrTD), SLoc, Args); + if (!FatPtrRecordType.isNull() && + !S.RequireCompleteType(SLoc, FatPtrRecordType, + diag::err_fat_ptr_missing_specialization)) + return FatPtrRecordType; + } else + return S.Context.getPointerType(PointeeT); + } + return T; +} + +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 + LocalVarWithoutArray, + + // `&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 + LocalVarWithArray, + + // `&fat arr[5];` arr is global array variable + // `&fat s.arr[5];` s is global variable whose type has array field + // `&fat s.a;` s is global variable whose type has array field + GlobalVarWithArray, + + OtherKind, +}; + +// Collect object whose address is taken by &fat in current function. +class FatAddressTakenFinder : public StmtVisitor { + bool IsVisitingAddrFatExpr = false; + Expr *CurrentAddrFatExpr = nullptr; + +public: + llvm::SmallSet LocalVarWithoutArraySet; + llvm::SmallSet LocalVarWithArraySet; + // Map of AddrFatExpr and its base expr: + // key is AddrFatExpr, value is its base expr and kind. + // For example, if we have `&fat p->a` where p is fat ptr and + // `&fat arr[5]` where arr is local array variable, + // the map will be: + // &fat p->a : (FatPtr, p) + // &fat arr[5] : (LocalVarWithArray, arr) + llvm::DenseMap> + AddrFatExprAndBaseMap; + + bool hasConstantArray(QualType QT) { + 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::LocalVarWithArray; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = DRE; + LocalVarWithArraySet.insert(VD); + } else { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::LocalVarWithoutArray; + LocalVarWithoutArraySet.insert(VD); + } + } else if (VD->hasGlobalStorage() && hasConstantArray(QT)) { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::GlobalVarWithArray; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = DRE; + } + CurrentAddrFatExpr = nullptr; + IsVisitingAddrFatExpr = false; + } + } +}; + +// Desugar fat ptr for FunctionDecl or VarDecl. +class TransformFatPtr : public TreeTransform { + typedef TreeTransform BaseTransform; + + FunctionDecl *FD = nullptr; + // when deref, access member, or some pointer calculation, + // fat ptr should be desugared to its ptr member, + // for example: + // `*p` should be desugared to `*p.ptr` + // `p->a` should be desugared to `p.ptr->a` + // `p1 - p2` should be desugared to `p1.ptr - p2.ptr` + // `(int*)p` should be desugared to `(int*)p.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]`, `p->a.b.arr[3]` + // we should check offset to avoid out of bounds. + ArraySubscriptExpr *CurrentASEToAccessMemberThroughFatPtr = nullptr; + + // cache fat ptr related function to avoid frequent lookup. + std::map FatPtrFD; + // cache fat ptr TemplateDecl to avoid frequent lookup. + ClassTemplateDecl *FatPtrTD = nullptr; + + FatAddressTakenFinder Finder; + VarDecl *LocalLockVersionVD = nullptr; + llvm::DenseMap> + LocalArrayAllocationUnitMap; + +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; + } + + // make sure redo semantic analysis + bool AlwaysRebuild() { return true; } + bool IsNullPtrExpr(Expr *E); + + // Entry to desugar FunctionDecl + void TransformFunctionDecl(); + // Entry to desugar VarDecl + void TransformVarDecl(VarDecl *VD); + void TransformSelfCallExprFunctionType(CallExpr *CE); + + // Handle allocation unit for local objects whose address is taken by &fat. + CompoundStmt *HandleLocalAllocationUnit(CompoundStmt *CS, + bool IsTopLevelCompoundStmt); + std::pair + BuildLocalNoArrayVarsAllocationUnitHeader(SourceLocation SLoc); + std::pair + BuildLocalArrayAllocationUnit(VarDecl *VD); + Expr *BuildLocalNoArrayVarsLockVersionAssignment(SourceLocation SLoc); + + 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 HandleUOAddrFatOnLocalVarWithoutArray(UnaryOperator *UO); + ExprResult HandleUOAddrFatOnVarWithArray(UnaryOperator *UO, Expr *VarBaseE); + + QualType DesugarFatPtrType(SourceLocation SLoc, QualType T); + MemberExpr *BuildMemberForFatPtr(Expr *FatPtrBaseE, unsigned FieldIdx); + Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, + llvm::SmallVector &Args); + Expr *BuildCompoundLiteralExprForNullptr(Expr *NullptrE, QualType QT); + bool IsAccessingArrayMemberThroughFatPtr(Expr *E); + void VisitExprToGetOffsetOfComponent( + Expr *E, SourceLocation SLoc, + 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 = + 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())); + + // Find local no-array or array vars whose address is taken by `&fat` expr, + // and get kind of base expr. + Finder.VisitStmt(FD->getBody()); + + // Insert and init allocation unit header at the begin of function body + // for local vars whose address is taken by `&fat` expression, + // and set `_local_lock_version = 0` before function return. + CompoundStmt *NewCS = + HandleLocalAllocationUnit(dyn_cast(FD->getBody()), true); + + // Desugar 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() && IsNullPtrExpr(Init)) { + // `int *fat p = nullptr;` should be desugared to + // `_FatPtr p = _FatPtr { nullptr, 0, 0 };` + VD->setInit(BuildCompoundLiteralExprForNullptr(Init, QT)); + } else { + VD->setInit(BaseTransform::TransformExpr(Init).get()); + } + } +} + +// 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 +// `_local_lock_version` VarDecl and `_local_size` VarDecl as +// allocation unit header and insert it to the begin of function body. +// 2. Before function return, `_local_lock_version` should be set to +// 0, so we build assignment statement before return stmt or 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.LocalVarWithoutArraySet.size() > 0) { + std::pair Headers = + BuildLocalNoArrayVarsAllocationUnitHeader(CS->getBeginLoc()); + Statements.push_back(Headers.second); + Statements.push_back(Headers.first); + } + if (Finder.LocalVarWithArraySet.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.LocalVarWithArraySet.count(VD)) { + std::pair Decls = + BuildLocalArrayAllocationUnit(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.LocalVarWithoutArraySet.size() > 0) { + Statements.push_back( + BuildLocalNoArrayVarsLockVersionAssignment(B->getBeginLoc())); + Statements.push_back(B); + } else { + Statements.push_back(B); + } + } + } + if (IsTopLevelCompoundStmt && Finder.LocalVarWithoutArraySet.size() > 0) + Statements.push_back( + BuildLocalNoArrayVarsLockVersionAssignment(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::BuildLocalNoArrayVarsAllocationUnitHeader( + 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; + if (Expr *CE = BuildFatPtrCall(SLoc, "_new_version_number", Args)) + LockVersionVD->setInit(CE); + 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::BuildLocalArrayAllocationUnit(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 *VDTypeField = 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); + VDTypeField->setAccess(AS_public); + RD->addDecl(LockVersionField); + RD->addDecl(SizeField); + RD->addDecl(VDTypeField); + 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); + LocalArrayAllocationUnitMap[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::BuildLocalNoArrayVarsLockVersionAssignment( + 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; +} + +// 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) + FatPtrTD = lookupFatPtrTemplateDecl(SemaRef, SLoc); + if (FatPtrTD) { + return DesugarFatPtrQT(SemaRef, SLoc, T, FatPtrTD); + } + 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; +} + +// FieldIdx = 0, build for the first field raw_ptr; +// FieldIdx = 1, build for the second field key_version; +// FieldIdx = 2, build for the third field offset; +MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, + unsigned FieldIdx) { + 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 (FieldIdx == 1) { + it++; + } else if (FieldIdx == 2) { + 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::BuildCompoundLiteralExprForNullptr(Expr *NullptrE, + QualType QT) { + if (auto *CastExpr = dyn_cast(NullptrE)) { + NullptrE = CastExpr->getSubExpr(); + } + SourceLocation SLoc = NullptrE->getBeginLoc(); + llvm::APInt Zero(32, 0); + QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); + Expr *SecondInitExpr = + IntegerLiteral::Create(SemaRef.Context, Zero, UInt32Ty, SLoc); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *ThirdInitExpr = + IntegerLiteral::Create(SemaRef.Context, Zero, Int32Ty, SLoc); + SmallVector Inits{NullptrE, SecondInitExpr, ThirdInitExpr}; + 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::VisitExprToGetOffsetOfComponent( + Expr *E, SourceLocation SLoc, + SmallVector &Comps) { + if (E->getType().isFatPtrType()) { + return; + } else if (auto ASE = dyn_cast(E)) { + VisitExprToGetOffsetOfComponent(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)) { + VisitExprToGetOffsetOfComponent(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)) { + VisitExprToGetOffsetOfComponent(ICE->getSubExpr(), SLoc, Comps); + } else if (auto PE = dyn_cast(E)) { + VisitExprToGetOffsetOfComponent(PE->getSubExpr(), SLoc, Comps); + } +} + +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()) { + // Build CallExpr to check offset. + if (InsertCheckOffsetCall.first) { + llvm::SmallVector Args{DRE, InsertCheckOffsetCall.second}; + if (Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args)) + CheckCallExpr.push(CE); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + // Build CallExpr to check version. + if (InsertCheckVersionCall) { + llvm::SmallVector Args{DRE}; + if (Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args)) + CheckCallExpr.push(CE); + InsertCheckVersionCall = false; + } + if (DesugarToMemberPtr) { + DesugarToMemberPtr = false; + // Desugar p to p.ptr + return BuildMemberForFatPtr(DRE, 0); + } + } else if (auto VD = dyn_cast(DRE->getDecl())) { + if (LocalArrayAllocationUnitMap.count(VD)) { + // Desugar arr to _arr.arr + VarDecl *AllocationUnitVD = LocalArrayAllocationUnitMap[VD].second; + Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( + AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); + RecordDecl *AllocationUnitRD = LocalArrayAllocationUnitMap[VD].first; + 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); + } else if (SemaRef.Context.BSCDesugaredMap.count(VD)) { + // Desugar arr to _arr.arr + VarDecl *AllocationUnitVD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][1]); + Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( + AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); + RecordDecl *AllocationUnitRD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][0]); + 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 { + BaseTransform::TransformDefinition(D->getLocation(), D); + } + } + return DS; +} + +ExprResult +TransformFatPtr::TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE) { + QualType QT = DesugarFatPtrType(CLE->getBeginLoc(), CLE->getType()); + CLE->setType(QT); + CLE->setInitializer( + BaseTransform::TransformExpr(CLE->getInitializer()).get()); + return CLE; +} + +ExprResult TransformFatPtr::TransformInitListExpr(InitListExpr *ILE) { + QualType QT = DesugarFatPtrType(ILE->getBeginLoc(), ILE->getType()); + ILE->setType(QT); + 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() && 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 };` + RS->setRetValue(BuildCompoundLiteralExprForNullptr(RV, ReturnQT)); + } else + RS->setRetValue(BaseTransform::TransformExpr(RV).get()); + 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) { + QualType QT = DesugarFatPtrType(CE->getBeginLoc(), CE->getType()); + CE->setType(QT); + + 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() && 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 })` + Expr *CLE = BuildCompoundLiteralExprForNullptr(ArgE, ParamQT); + CLE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); + CE->setArg(i, CLE); + } 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()) { + 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 * 4 + 4` + 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)) { + 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.isFatQualified() && SubQT.isFatPtrType()) { + // desuagr `(int*)fat_p` to `(int*)fat_p.ptr` + DesugarToMemberPtr = true; + } else if (QT.isFatQualified() && SubQT->isPointerType() && + !SubQT.isFatQualified()) { + QT = DesugarFatPtrType(SLoc, QT); + // desugar `(int *fat)raw_p` to `(_FatPtr) { raw_p, 0, 0 }` + Expr *FirstInitExpr = BaseTransform::TransformExpr(SubE).get(); + llvm::APInt Zero(32, 0); + QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); + Expr *SecondInitExpr = IntegerLiteral::Create( + SemaRef.Context, Zero, UInt32Ty, SLoc); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *ThirdInitExpr = IntegerLiteral::Create(SemaRef.Context, Zero, Int32Ty, + SLoc); + SmallVector Inits{FirstInitExpr, SecondInitExpr, ThirdInitExpr}; + Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); + Expr *CLE = SemaRef + .BuildCompoundLiteralExpr( + SLoc, + SemaRef.Context.getTrivialTypeSourceInfo(QT), + SLoc, ILE) + .get(); + return CLE; + } else if (QT.isFatQualified() && SubQT.isFatQualified()) { + 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.isFatQualified()) { + QualType NewQT = DesugarFatPtrType(SLoc, OldQT); + TypeSourceInfo *NewTInfo = + SemaRef.Context.getTrivialTypeSourceInfo(NewQT, SLoc); + return BaseTransform::RebuildUnaryExprOrTypeTrait( + NewTInfo, UETT->getOperatorLoc(), UETT->getKind(), + UETT->getSourceRange()); + } + } + return UETT; +} + +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 *FirstInitExpr = + SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); + Expr *SecondInitExpr = BuildMemberForFatPtr(FatPtrLHS, 1); + + Expr *ThirdInitExprLHS = BuildMemberForFatPtr(FatPtrLHS, 2); + QualType PointeeQT = FatPtrLHS->getType()->getFatPtrPointeeType(); + Expr *SizeOfExpr = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT), + SLoc, UETT_SizeOf, SourceRange()) + .get(); + Expr *ThirdInitExprRHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, SizeOfExpr).get(); + Expr *ThirdInitExpr = + SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitExprLHS, ThirdInitExprRHS).get(); + + SmallVector Inits{FirstInitExpr, SecondInitExpr, + ThirdInitExpr}; + Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); + ILE->setType(QT); + Expr *CLE = SemaRef + .BuildCompoundLiteralExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(QT), SLoc, ILE) + .get(); + return CLE; +} + +// BinaryOperator includes: +// reassign fat ptr : p1 = p2, p1 = nullptr +// calculation between fat ptr : p + 1, p - 1, p1 - p2 +ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { + QualType QT = DesugarFatPtrType(BO->getBeginLoc(), BO->getType());; + BO->setType(QT); + + 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.ptr - p2.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.ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` + return HandleBOFatPtrAddOrSubInteger(BO); + } else if (BO->isAssignmentOp() && LHSQT.isFatQualified() && + IsNullPtrExpr(RHS)) { + // `p = nullptr;` should be desugared to + // `p = (_FatPtr){ nullptr, 0, 0 };` + LHSQT = DesugarFatPtrType(BO->getBeginLoc(), LHSQT); + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(BuildCompoundLiteralExprForNullptr(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()); + return BO; +} + +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.ptr->a + Expr *FirstInitExpr = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); + // Build second init expr: p.version + Expr *SecondInitExpr = BuildMemberForFatPtr(FatPtrBaseE, 1); + // 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 *ThirdInitExprLHSLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, FirstInitExpr) + .get(); + Expr *ThirdInitExprLHSRHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, BuildMemberForFatPtr(FatPtrBaseE, 0)) + .get(); + Expr *ThirdInitExprLHS = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitExprLHSLHS, + ThirdInitExprLHSRHS) + .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + ThirdInitExprLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, + ThirdInitExprLHS) + .get(); + Expr *ThirdInitExprRHS = BuildMemberForFatPtr(FatPtrBaseE, 2); + Expr *ThirdInitExpr = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Add, ThirdInitExprLHS, ThirdInitExprRHS) + .get(); + // Build CompoundLiteralExpr. + SmallVector Inits{FirstInitExpr, SecondInitExpr, ThirdInitExpr}; + Expr *ILE = + SemaRef.BuildInitList(SLoc, Inits, UO->getEndLoc()) + .get(); + ILE->setType(QT); + Expr *CLE = SemaRef + .BuildCompoundLiteralExpr( + SLoc, + SemaRef.Context.getTrivialTypeSourceInfo(QT), + SLoc, ILE) + .get(); + return CLE; +} + +// 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::HandleUOAddrFatOnLocalVarWithoutArray(UnaryOperator *UO) { + SourceLocation SLoc = UO->getBeginLoc(); + QualType QT = UO->getType(); + Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); + // Build first init expr: &a + Expr *FirstInitExpr = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); + // Build second init expr: _local_version_lock + Expr *SecondInitExpr = 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 *ThirdInitExprLHSLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, FirstInitExpr) + .get(); + Expr *ThirdInitExprLHSRHS = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SecondInitExpr).get(); + ThirdInitExprLHSRHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, ThirdInitExprLHSRHS) + .get(); + Expr *ThirdInitExprLHS = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitExprLHSLHS, + ThirdInitExprLHSRHS) + .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + ThirdInitExprLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, + ThirdInitExprLHS) + .get(); + Expr *ThirdInitExprRHS = IntegerLiteral::Create( + SemaRef.Context, llvm::APInt(32, 8), Int32Ty, SLoc); + Expr *ThirdInitExpr = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitExprLHS, ThirdInitExprRHS) + .get(); + // Build CompoundLiteralExpr. + SmallVector Inits{FirstInitExpr, SecondInitExpr, ThirdInitExpr}; + Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); + ILE->setType(QT); + Expr *CLE = + SemaRef + .BuildCompoundLiteralExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(QT), SLoc, ILE) + .get(); + return CLE; +} + +// 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::HandleUOAddrFatOnVarWithArray( + 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 *FirstInitExpr = + 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 (LocalArrayAllocationUnitMap.count(VD)) { + // VD is local var + AllocationUnitRD = LocalArrayAllocationUnitMap[VD].first; + AllocationUnitVD = LocalArrayAllocationUnitMap[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 *SecondInitExpr = 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 *ThirdInitExprLHSLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, FirstInitExpr) + .get(); + Expr *ThirdInitExprLHSRHS = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SecondInitExpr).get(); + ThirdInitExprLHSRHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, ThirdInitExprLHSRHS) + .get(); + Expr *ThirdInitExprLHS = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitExprLHSLHS, + ThirdInitExprLHSRHS) + .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + ThirdInitExprLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, + ThirdInitExprLHS) + .get(); + Expr *ThirdInitExprRHS = IntegerLiteral::Create( + SemaRef.Context, llvm::APInt(32, 8), Int32Ty, SLoc); + Expr *ThirdInitExpr = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitExprLHS, ThirdInitExprRHS) + .get(); + // Build CompoundLiteralExpr. + SmallVector Inits{FirstInitExpr, SecondInitExpr, + ThirdInitExpr}; + Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); + ILE->setType(QT); + Expr *CLE = + SemaRef + .BuildCompoundLiteralExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(QT), + SLoc, ILE) + .get(); + return CLE; +} + +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::LocalVarWithoutArray: + return HandleUOAddrFatOnLocalVarWithoutArray(UO); + case AddrFatExprBaseKind::LocalVarWithArray: + case AddrFatExprBaseKind::GlobalVarWithArray: + return HandleUOAddrFatOnVarWithArray(UO, Finder.AddrFatExprAndBaseMap[UO].second); + default: + break; + } + } + UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + return UO; +} + +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}; + if (Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args)) + CheckCallExpr.push(CE); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + if (InsertCheckVersionCall) { + llvm::SmallVector Args{ME}; + if (Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args)) + CheckCallExpr.push(CE); + InsertCheckVersionCall = false; + } + if (DesugarToMemberPtr) { + // Desugar s.p to s.p.ptr + MemberPtr = BuildMemberForFatPtr(ME, 0); + 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: + // for p->arr[3] where p is fat ptr and p->arr[3] is int type, + // offset is `__builtin_offsetof(struct S, arr[3]) + 4` + // for p->a, where p is fat ptr and p->a is int type, + // offset is `__builtin_offsetof(struct S, a) + 4` + QualType PointeeQT = BaseType->getFatPtrPointeeType(); + TypeSourceInfo *PointeeTInfo = + SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT, SLoc); + QualType ElementType; + SmallVector Comps; + if (CurrentASEToAccessMemberThroughFatPtr) { + VisitExprToGetOffsetOfComponent( + CurrentASEToAccessMemberThroughFatPtr, SLoc, Comps); + ElementType = CurrentASEToAccessMemberThroughFatPtr->getType(); + CurrentASEToAccessMemberThroughFatPtr = nullptr; + } else { + VisitExprToGetOffsetOfComponent(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; +} + +void Sema::DesugarFunctionWithFatPtr(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() != nullptr) + return; + } + + // Desugar fat ptr in function. + TransformFatPtr TFP(*this, FD); + TFP.TransformFunctionDecl(); +} + +void Sema::DesugarGlobalVarWithFatPtr(VarDecl *VD) { + if (Diags.hasErrorOccurred()) + return; + + // Desugar fat ptr in global VarDecl. + TransformFatPtr TFP(*this); + TFP.TransformVarDecl(VD); +} + +void Sema::DesugarStructWithFatPtr(RecordDecl *RD) { + if (Diags.hasErrorOccurred() || RD->getTagKind() != TTK_Struct) + return; + if (RD->getDescribedClassTemplate() != nullptr) + return; + bool ShouldDesugar = false; + for (FieldDecl *FD : RD->fields()) { + QualType QT = FD->getType().getCanonicalType(); + if (QT.isFatQualified() || QT->hasFatFields()) { + ShouldDesugar = true; + break; + } + } + if (!ShouldDesugar) + return; + // Desugar fat ptr field. + if (auto FatPtrTD = lookupFatPtrTemplateDecl(*this, RD->getLocation())) { + for (FieldDecl *FD : RD->fields()) { + QualType QT = FD->getType().getCanonicalType(); + if (QT.isFatQualified() || QT->hasFatFields()) + FD->setType( + DesugarFatPtrQT(*this, FD->getLocation(), QT, FatPtrTD)); + } + } +} + +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.isFatPtrType() && RHSCanType.isFatPtrType()) { + 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; + + // 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()) { + 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; + } + } + } + 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.isFatPtrType() && RHSCanType.isFatPtrType()) && + !(LHSCanType.isFatQualified() && RHSCanType.isFatQualified())) + 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::BuildGlobalArrayAllocationUnit(VarDecl *VD) { + SourceLocation SLoc = VD->getLocation(); + // Build RecordDecl: struct _arr_AllocationUnit; + 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: struct _arr_AllocationUnit _arr = { 0, 12, {1, 2, 3} }; + 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 by 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); + + VD->addAttr(UnusedAttr::CreateImplicit(Context, SLoc, + AttributeCommonInfo::AS_GNU, + UnusedAttr::GNU_unused)); + 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 e050e6e38ab64428ffb18a43c364a5ec38ec8327..066ff527858f07d2e4b5209a0db5ed689003c060 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 cad99765f657745e13adcd00b8d51d23a940798e..f3813e181333b47dea93b254ecf85852403cc626 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 06b38b53b1b0a7334059f80d125418ada1fb931b..2370457c70915fc5a7ce124df807feb9269bdf74 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 c0febac51a372f1cca47995083da0fe0580a071c..245bf606e420d9ced5e4c6a901143551aa071af0 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 7150cf94981d760442c1d2edd09d61caf7356b33..826b2f616102efd46f54983c8f98b96e0334649c 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 a5a00c76f6497f4c872275027db963178e49546e..e0780e8c796b92e0e261fa1dd29b7e2da7ee2a74 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 b174416b2a54141d7e60aecb9b218d61a37aebe5..741617c3205062be843df1a84b531d656766b198 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 8985133fdb8a8da0a259e48eaaf5b87d5a8edd00..0d7d75b68e2a0f00803fc41564e82263b59a9bb1 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) + DesugarFunctionWithFatPtr(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) + DesugarStructWithFatPtr(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 bfd17fce4f1a7c3481181f35083a71db17f64a36..a1b6e7117af116b60cea39d5f41e31266b14e889 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 cd1482829f89736de925065c1cb977c453f42054..11bb1b666c9baf371957a311d955ea7dfc75a21b 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; @@ -7118,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) @@ -10437,6 +10444,20 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } return false; } + +static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { + 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 Sema::AssignConvertType @@ -10470,10 +10491,20 @@ 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.isFatPtrType() || LHSCanType.isFatPtrType()) { + 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 +10512,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 +10531,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 +14934,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 +14993,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 +15289,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 +15394,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 +16434,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: { @@ -17761,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) { @@ -17903,6 +17964,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 +17981,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 a53524dd7b02c09a42119c17d723469db17cf984..cacceda41ec99d08495379d437a0db0e0e0906d8 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 439ca1fcf82bbabac0cdb0a9200d9b3de159c712..f9348718d2c8b558adaac2d4ee8a297bb7f61550 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 f04325d2063bc014d28ca5f2bdfa000bd05cf982..5882e115de868016456d495f1715411de36caff3 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 cb19ab6202fa7992edf8e2f2968c4382785aaefb..3dac2d46a7cf0afea77fda68fee5e65c6ce6f296 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 0000000000000000000000000000000000000000..09c1b718876a4feef125b9167321946d9708ddfe --- /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 0000000000000000000000000000000000000000..50008ac524487a1a224c7276841c2941b0fc34a0 --- /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 0000000000000000000000000000000000000000..3006aa21235d019ec2d4a197e4ff2cef171b5114 --- /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 0000000000000000000000000000000000000000..5594d6b6d21142956e208b737e1d24fac6569e9a --- /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 0000000000000000000000000000000000000000..2d8b7aae4bb655563d74ec164f088b637fda11ab --- /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 0000000000000000000000000000000000000000..43d004ddc4754242de9c6c3240a189aa01757fec --- /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 0000000000000000000000000000000000000000..c3b97993ee12e9eac6e2c3e4238dd4b8d4d34e6e --- /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 0000000000000000000000000000000000000000..910359d28473c2ae1d45cbc5c96fc3c70242ff80 --- /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 0000000000000000000000000000000000000000..9a6cbe7ba54394fed64889e65a02b765490ef6b2 --- /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 0000000000000000000000000000000000000000..8267ee6635cde627f9a44b878db0df91c9a580bb --- /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 0000000000000000000000000000000000000000..dedb1e9f8d708180506a9018057ea8ac0b7cf465 --- /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/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs new file mode 100644 index 0000000000000000000000000000000000000000..b7cf5d4ce5e7548a064f4763f2db0c7d92c053b2 --- /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 0000000000000000000000000000000000000000..adb31c07c81e755c7effb4f56780952fff40401a --- /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 0000000000000000000000000000000000000000..a5a4b818647faa418fc2e298b482b0994459f0ac --- /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 0000000000000000000000000000000000000000..f9ddbb207a9c2f54041ca68fdbae277b1112894f --- /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 0000000000000000000000000000000000000000..42fc9676d3542ef21342f83bcadfaa1c8c1d0db4 --- /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 0000000000000000000000000000000000000000..31ff1217ea981c38eac09c9cd383731262aacd0e --- /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 0000000000000000000000000000000000000000..3c9ef85f86d086b39e35f7e760a9126f5ac22bb4 --- /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 0000000000000000000000000000000000000000..e0227f63c4dbd2988974cd0beeb6cfd4bb56a405 --- /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/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 0000000000000000000000000000000000000000..07ea7ca2204c4355fa8fa7ec0ebef267cd175621 --- /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 0000000000000000000000000000000000000000..80280bf71a337e8efc04e2e9e678f8dae24fd10a --- /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/return_local_address.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs new file mode 100644 index 0000000000000000000000000000000000000000..c39bf785d13b5f99ed1f07fad8537590ec92f549 --- /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 0000000000000000000000000000000000000000..7f4893eab5f06f21e5c61a9ad7ac22b642c13deb --- /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 0000000000000000000000000000000000000000..5c8a83374c7eca6a72f0054f93a0d96f6d2dc55d --- /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/nullptr_cast/nullptr_cast.cbs b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs new file mode 100644 index 0000000000000000000000000000000000000000..9a47ca5dac2bed5ad720afb022d4e55ed27a3d18 --- /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/olden/bisort/bitonic.c b/clang/test/BSC/Positive/FatPtr/olden/bisort/bitonic.c new file mode 100644 index 0000000000000000000000000000000000000000..b1d9d364a0cff2b69ddf813bbd3c1cb7edac5789 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/olden/bisort/bitonic.c @@ -0,0 +1,303 @@ +// RUN: %clang %s -o %test.output +// RUN: %test.output 3000 + +#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/olden/bisort_bsc/bitonic.cbs b/clang/test/BSC/Positive/FatPtr/olden/bisort_bsc/bitonic.cbs new file mode 100644 index 0000000000000000000000000000000000000000..ea9eff27e94c2f999efb6bef1cb156abf933bc72 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/olden/bisort_bsc/bitonic.cbs @@ -0,0 +1,305 @@ +// 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 (((HANDLE *)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 *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 (((HANDLE *)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; + } + } + HANDLE *fat root_left = root->left; + if ((HANDLE *)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 *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 ((HANDLE *)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 *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/olden/treeadd/node.c b/clang/test/BSC/Positive/FatPtr/olden/treeadd/node.c new file mode 100644 index 0000000000000000000000000000000000000000..c8af4371bf4b4cacb0e8202094a075bffc2d948b --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/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/olden/treeadd/tree.h b/clang/test/BSC/Positive/FatPtr/olden/treeadd/tree.h new file mode 100644 index 0000000000000000000000000000000000000000..b8d075e41cb07c727547425dc09e2e8fff55c833 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/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/olden/treeadd_bsc/node.cbs b/clang/test/BSC/Positive/FatPtr/olden/treeadd_bsc/node.cbs new file mode 100644 index 0000000000000000000000000000000000000000..f859e368b7ae73d25f75ae60c556e1a04bc61346 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/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/olden/treeadd_bsc/tree.hbs b/clang/test/BSC/Positive/FatPtr/olden/treeadd_bsc/tree.hbs new file mode 100644 index 0000000000000000000000000000000000000000..0b8014a09f3d501f9418e066dd18bb785d7678c6 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/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/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 0000000000000000000000000000000000000000..77874e118c52422e24087c93ebd98312dd4b03f7 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs @@ -0,0 +1,36 @@ +#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/ir_asm_anaysis/test.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/ir_asm_anaysis/test.cbs new file mode 100644 index 0000000000000000000000000000000000000000..8208635c90dc89f22de2fec135e48d8e44b4fd95 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/ir_asm_anaysis/test.cbs @@ -0,0 +1,60 @@ +#include + +int main() { + int *fat p = checked_malloc(sizeof(int)); + int a = 6; + *p = a; +} +// IR: +// %p = alloca %struct._FatPtr.0, align 8 +// %a = alloca i32, align 4 +// %agg.tmp = alloca %struct._FatPtr.0, align 8 +// %agg.tmp1 = alloca %struct._FatPtr.0, align 8 + +// %call = call { ptr, i64 } @checked_malloc(i64 noundef 4) +// %0 = getelementptr inbounds { ptr, i64 }, ptr %p, i32 0, i32 0 +// %1 = extractvalue { ptr, i64 } %call, 0 +// store ptr %1, ptr %0, align 8 +// %2 = getelementptr inbounds { ptr, i64 }, ptr %p, i32 0, i32 1 +// %3 = extractvalue { ptr, i64 } %call, 1 +// store i64 %3, ptr %2, align 8 + +// store i32 6, ptr %a, align 4 + +// call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp, ptr align 8 %p, i64 16, i1 false) +// %4 = getelementptr inbounds { ptr, i64 }, ptr %agg.tmp, i32 0, i32 0 +// %5 = load ptr, ptr %4, align 8 +// %6 = getelementptr inbounds { ptr, i64 }, ptr %agg.tmp, i32 0, i32 1 +// %7 = load i64, ptr %6, align 8 +// call void @_check_version(ptr %5, i64 %7) + +// call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp1, ptr align 8 %p, i64 16, i1 false) +// %8 = getelementptr inbounds { ptr, i64 }, ptr %agg.tmp1, i32 0, i32 0 +// %9 = load ptr, ptr %8, align 8 +// %10 = getelementptr inbounds { ptr, i64 }, ptr %agg.tmp1, i32 0, i32 1 +// %11 = load i64, ptr %10, align 8 +// call void @_check_offset(ptr %9, i64 %11, i32 noundef 4) + +// ASM: +// callq checked_malloc +// movq %rax, -16(%rbp) +// movq %rdx, -8(%rbp) + +// movl $6, -20(%rbp) + +// movq -16(%rbp), %rax +// movq %rax, -40(%rbp) +// movq -8(%rbp), %rax +// movq %rax, -32(%rbp) +// movq -40(%rbp), %rdi +// movq -32(%rbp), %rsi +// callq _check_version + +// movq -16(%rbp), %rax +// movq %rax, -56(%rbp) +// movq -8(%rbp), %rax +// movq %rax, -48(%rbp) +// movq -56(%rbp), %rdi +// movq -48(%rbp), %rsi +// movl $4, %edx +// callq _check_offset \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/ir_asm_anaysis/test.ll b/clang/test/BSC/Positive/FatPtr/perform_anaysis/ir_asm_anaysis/test.ll new file mode 100644 index 0000000000000000000000000000000000000000..9ea2127af3d87ebce0ae9d36418de79d01204e6b --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/ir_asm_anaysis/test.ll @@ -0,0 +1,368 @@ +; ModuleID = 'test.cbs' +source_filename = "test.cbs" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct._FatPtr = type { ptr, i32, i32 } +%struct._AllocationUnit = type { i32, i32, [0 x i8] } +%struct._FatPtr.0 = type { ptr, i32, i32 } + +@stderr = external global ptr, align 8 +@.str = private unnamed_addr constant [3 x i8] c"%s\00", align 1 +@.str.1 = private unnamed_addr constant [13 x i8] c"Call stack:\0A\00", align 1 +@.str.2 = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1 +@_new_version_number.uuid = internal global i32 10000, align 4 +@.str.3 = private unnamed_addr constant [25 x i8] c"allocated size is ZERO!\0A\00", align 1 +@.str.4 = private unnamed_addr constant [33 x i8] c"version number error when free!\0A\00", align 1 +@.str.5 = private unnamed_addr constant [65 x i8] c"free a pointer which is not point to the head of an allocation!\0A\00", align 1 +@.str.6 = private unnamed_addr constant [23 x i8] c"version number error!\0A\00", align 1 +@.str.7 = private unnamed_addr constant [44 x i8] c"pointer offset exceed the allocation size!\0A\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @_report_error(ptr noundef %msg) #0 { +entry: + %msg.addr = alloca ptr, align 8 + %callstack = alloca [10 x ptr], align 16 + %num_frames = alloca i32, align 4 + %symbols = alloca ptr, align 8 + %i = alloca i32, align 4 + store ptr %msg, ptr %msg.addr, align 8 + %0 = load ptr, ptr @stderr, align 8 + %1 = load ptr, ptr %msg.addr, align 8 + %call = call i32 (ptr, ptr, ...) @fprintf(ptr noundef %0, ptr noundef @.str, ptr noundef %1) + %arraydecay = getelementptr inbounds [10 x ptr], ptr %callstack, i64 0, i64 0 + %call1 = call i32 @backtrace(ptr noundef %arraydecay, i32 noundef 10) + store i32 %call1, ptr %num_frames, align 4 + %arraydecay2 = getelementptr inbounds [10 x ptr], ptr %callstack, i64 0, i64 0 + %2 = load i32, ptr %num_frames, align 4 + %call3 = call ptr @backtrace_symbols(ptr noundef %arraydecay2, i32 noundef %2) #7 + store ptr %call3, ptr %symbols, align 8 + %3 = load ptr, ptr @stderr, align 8 + %call4 = call i32 (ptr, ptr, ...) @fprintf(ptr noundef %3, ptr noundef @.str.1) + store i32 0, ptr %i, align 4 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %4 = load i32, ptr %i, align 4 + %5 = load i32, ptr %num_frames, align 4 + %cmp = icmp slt i32 %4, %5 + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %6 = load ptr, ptr @stderr, align 8 + %7 = load ptr, ptr %symbols, align 8 + %8 = load i32, ptr %i, align 4 + %idxprom = sext i32 %8 to i64 + %arrayidx = getelementptr inbounds ptr, ptr %7, i64 %idxprom + %9 = load ptr, ptr %arrayidx, align 8 + %call5 = call i32 (ptr, ptr, ...) @fprintf(ptr noundef %6, ptr noundef @.str.2, ptr noundef %9) + br label %for.inc + +for.inc: ; preds = %for.body + %10 = load i32, ptr %i, align 4 + %inc = add nsw i32 %10, 1 + store i32 %inc, ptr %i, align 4 + br label %for.cond, !llvm.loop !6 + +for.end: ; preds = %for.cond + %11 = load ptr, ptr %symbols, align 8 + call void @free(ptr noundef %11) #7 + call void @exit(i32 noundef 1) #8 + unreachable +} + +declare i32 @fprintf(ptr noundef, ptr noundef, ...) #1 + +declare i32 @backtrace(ptr noundef, i32 noundef) #1 + +; Function Attrs: nounwind +declare ptr @backtrace_symbols(ptr noundef, i32 noundef) #2 + +; Function Attrs: nounwind +declare void @free(ptr noundef) #2 + +; Function Attrs: noreturn nounwind +declare void @exit(i32 noundef) #3 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @_new_version_number() #0 { +entry: + %.atomictmp = alloca i32, align 4 + %atomic-temp = alloca i32, align 4 + %atomic-temp1 = alloca i32, align 4 + store i32 1, ptr %.atomictmp, align 4 + %0 = load i32, ptr %.atomictmp, align 4 + %1 = atomicrmw add ptr @_new_version_number.uuid, i32 %0 seq_cst, align 4 + store i32 %1, ptr %atomic-temp, align 4 + %2 = load i32, ptr %atomic-temp, align 4 + %3 = load atomic i32, ptr @_new_version_number.uuid seq_cst, align 4 + store i32 %3, ptr %atomic-temp1, align 4 + %4 = load i32, ptr %atomic-temp1, align 4 + ret i32 %4 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local { ptr, i64 } @checked_malloc(i64 noundef %payload_bytes) #0 { +entry: + %retval = alloca %struct._FatPtr, align 8 + %payload_bytes.addr = alloca i64, align 8 + %all_size = alloca i64, align 8 + %raw = alloca ptr, align 8 + %p = alloca ptr, align 8 + %ver = alloca i32, align 4 + store i64 %payload_bytes, ptr %payload_bytes.addr, align 8 + %0 = load i64, ptr %payload_bytes.addr, align 8 + %cmp = icmp eq i64 %0, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + call void @_report_error(ptr noundef @.str.3) + br label %if.end + +if.end: ; preds = %if.then, %entry + %1 = load i64, ptr %payload_bytes.addr, align 8 + %add = add i64 %1, 8 + store i64 %add, ptr %all_size, align 8 + %2 = load i64, ptr %all_size, align 8 + %call = call noalias ptr @malloc(i64 noundef %2) #9 + store ptr %call, ptr %raw, align 8 + %3 = load ptr, ptr %raw, align 8 + store ptr %3, ptr %p, align 8 + %call1 = call i32 @_new_version_number() + store i32 %call1, ptr %ver, align 4 + %4 = load i32, ptr %ver, align 4 + %5 = load ptr, ptr %p, align 8 + store i32 %4, ptr %5, align 4 + %6 = load i64, ptr %payload_bytes.addr, align 8 + %conv = trunc i64 %6 to i32 + %7 = load ptr, ptr %p, align 8 + %add.ptr = getelementptr inbounds i32, ptr %7, i64 1 + store i32 %conv, ptr %add.ptr, align 4 + %raw_ptr = getelementptr inbounds %struct._FatPtr, ptr %retval, i32 0, i32 0 + %8 = load ptr, ptr %p, align 8 + %add.ptr2 = getelementptr inbounds i32, ptr %8, i64 2 + store ptr %add.ptr2, ptr %raw_ptr, align 8 + %key_version = getelementptr inbounds %struct._FatPtr, ptr %retval, i32 0, i32 1 + %9 = load i32, ptr %ver, align 4 + store i32 %9, ptr %key_version, align 8 + %offset = getelementptr inbounds %struct._FatPtr, ptr %retval, i32 0, i32 2 + store i32 0, ptr %offset, align 4 + %10 = load { ptr, i64 }, ptr %retval, align 8 + ret { ptr, i64 } %10 +} + +; Function Attrs: nounwind allocsize(0) +declare noalias ptr @malloc(i64 noundef) #4 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @checked_free(ptr %ptr.coerce0, i64 %ptr.coerce1) #0 { +entry: + %ptr = alloca %struct._FatPtr, align 8 + %head = alloca ptr, align 8 + %phead = alloca ptr, align 8 + %0 = getelementptr inbounds { ptr, i64 }, ptr %ptr, i32 0, i32 0 + store ptr %ptr.coerce0, ptr %0, align 8 + %1 = getelementptr inbounds { ptr, i64 }, ptr %ptr, i32 0, i32 1 + store i64 %ptr.coerce1, ptr %1, align 8 + %raw_ptr = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 0 + %2 = load ptr, ptr %raw_ptr, align 8 + %offset = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 2 + %3 = load i32, ptr %offset, align 4 + %idx.ext = sext i32 %3 to i64 + %idx.neg = sub i64 0, %idx.ext + %add.ptr = getelementptr inbounds i8, ptr %2, i64 %idx.neg + %add.ptr1 = getelementptr inbounds i8, ptr %add.ptr, i64 -8 + store ptr %add.ptr1, ptr %head, align 8 + %4 = load ptr, ptr %head, align 8 + store ptr %4, ptr %phead, align 8 + %key_version = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 1 + %5 = load i32, ptr %key_version, align 8 + %6 = load ptr, ptr %phead, align 8 + %lock_version = getelementptr inbounds %struct._AllocationUnit, ptr %6, i32 0, i32 0 + %7 = load i32, ptr %lock_version, align 4 + %cmp = icmp ne i32 %5, %7 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + call void @_report_error(ptr noundef @.str.4) + br label %if.end + +if.end: ; preds = %if.then, %entry + %offset2 = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 2 + %8 = load i32, ptr %offset2, align 4 + %cmp3 = icmp ne i32 %8, 0 + br i1 %cmp3, label %if.then4, label %if.end5 + +if.then4: ; preds = %if.end + call void @_report_error(ptr noundef @.str.5) + br label %if.end5 + +if.end5: ; preds = %if.then4, %if.end + %9 = load ptr, ptr %phead, align 8 + %10 = load ptr, ptr %phead, align 8 + %size = getelementptr inbounds %struct._AllocationUnit, ptr %10, i32 0, i32 1 + %11 = load i32, ptr %size, align 4 + %add = add i32 %11, 8 + %conv = zext i32 %add to i64 + call void @llvm.memset.p0.i64(ptr align 4 %9, i8 0, i64 %conv, i1 false) + %12 = load ptr, ptr %phead, align 8 + call void @free(ptr noundef %12) #7 + ret void +} + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #5 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @_check_version(ptr %ptr.coerce0, i64 %ptr.coerce1) #0 { +entry: + %ptr = alloca %struct._FatPtr, align 8 + %head = alloca ptr, align 8 + %phead = alloca ptr, align 8 + %0 = getelementptr inbounds { ptr, i64 }, ptr %ptr, i32 0, i32 0 + store ptr %ptr.coerce0, ptr %0, align 8 + %1 = getelementptr inbounds { ptr, i64 }, ptr %ptr, i32 0, i32 1 + store i64 %ptr.coerce1, ptr %1, align 8 + %raw_ptr = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 0 + %2 = load ptr, ptr %raw_ptr, align 8 + %offset = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 2 + %3 = load i32, ptr %offset, align 4 + %idx.ext = sext i32 %3 to i64 + %idx.neg = sub i64 0, %idx.ext + %add.ptr = getelementptr inbounds i8, ptr %2, i64 %idx.neg + %add.ptr1 = getelementptr inbounds i8, ptr %add.ptr, i64 -8 + store ptr %add.ptr1, ptr %head, align 8 + %4 = load ptr, ptr %head, align 8 + store ptr %4, ptr %phead, align 8 + %key_version = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 1 + %5 = load i32, ptr %key_version, align 8 + %cmp = icmp ne i32 %5, 0 + br i1 %cmp, label %land.lhs.true, label %if.end + +land.lhs.true: ; preds = %entry + %key_version2 = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 1 + %6 = load i32, ptr %key_version2, align 8 + %7 = load ptr, ptr %phead, align 8 + %lock_version = getelementptr inbounds %struct._AllocationUnit, ptr %7, i32 0, i32 0 + %8 = load i32, ptr %lock_version, align 4 + %cmp3 = icmp ne i32 %6, %8 + br i1 %cmp3, label %if.then, label %if.end + +if.then: ; preds = %land.lhs.true + call void @_report_error(ptr noundef @.str.6) + br label %if.end + +if.end: ; preds = %if.then, %land.lhs.true, %entry + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @_check_offset(ptr %ptr.coerce0, i64 %ptr.coerce1, i32 noundef %bytes) #0 { +entry: + %ptr = alloca %struct._FatPtr, align 8 + %bytes.addr = alloca i32, align 4 + %head = alloca ptr, align 8 + %phead = alloca ptr, align 8 + %new_offset = alloca i32, align 4 + %0 = getelementptr inbounds { ptr, i64 }, ptr %ptr, i32 0, i32 0 + store ptr %ptr.coerce0, ptr %0, align 8 + %1 = getelementptr inbounds { ptr, i64 }, ptr %ptr, i32 0, i32 1 + store i64 %ptr.coerce1, ptr %1, align 8 + store i32 %bytes, ptr %bytes.addr, align 4 + %raw_ptr = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 0 + %2 = load ptr, ptr %raw_ptr, align 8 + %offset = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 2 + %3 = load i32, ptr %offset, align 4 + %idx.ext = sext i32 %3 to i64 + %idx.neg = sub i64 0, %idx.ext + %add.ptr = getelementptr inbounds i8, ptr %2, i64 %idx.neg + %add.ptr1 = getelementptr inbounds i8, ptr %add.ptr, i64 -8 + store ptr %add.ptr1, ptr %head, align 8 + %4 = load ptr, ptr %head, align 8 + store ptr %4, ptr %phead, align 8 + %offset2 = getelementptr inbounds %struct._FatPtr, ptr %ptr, i32 0, i32 2 + %5 = load i32, ptr %offset2, align 4 + %6 = load i32, ptr %bytes.addr, align 4 + %add = add nsw i32 %5, %6 + store i32 %add, ptr %new_offset, align 4 + %7 = load ptr, ptr %phead, align 8 + %size = getelementptr inbounds %struct._AllocationUnit, ptr %7, i32 0, i32 1 + %8 = load i32, ptr %size, align 4 + %cmp = icmp ugt i32 %8, 0 + br i1 %cmp, label %land.lhs.true, label %if.end + +land.lhs.true: ; preds = %entry + %9 = load i32, ptr %new_offset, align 4 + %10 = load ptr, ptr %phead, align 8 + %size3 = getelementptr inbounds %struct._AllocationUnit, ptr %10, i32 0, i32 1 + %11 = load i32, ptr %size3, align 4 + %cmp4 = icmp ugt i32 %9, %11 + br i1 %cmp4, label %if.then, label %if.end + +if.then: ; preds = %land.lhs.true + call void @_report_error(ptr noundef @.str.7) + br label %if.end + +if.end: ; preds = %if.then, %land.lhs.true, %entry + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { +entry: + %p = alloca %struct._FatPtr.0, align 8 + %a = alloca i32, align 4 + %agg.tmp = alloca %struct._FatPtr.0, align 8 + %agg.tmp1 = alloca %struct._FatPtr.0, align 8 + %call = call { ptr, i64 } @checked_malloc(i64 noundef 4) + %0 = getelementptr inbounds { ptr, i64 }, ptr %p, i32 0, i32 0 + %1 = extractvalue { ptr, i64 } %call, 0 + store ptr %1, ptr %0, align 8 + %2 = getelementptr inbounds { ptr, i64 }, ptr %p, i32 0, i32 1 + %3 = extractvalue { ptr, i64 } %call, 1 + store i64 %3, ptr %2, align 8 + store i32 6, ptr %a, align 4 + call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp, ptr align 8 %p, i64 16, i1 false) + %4 = getelementptr inbounds { ptr, i64 }, ptr %agg.tmp, i32 0, i32 0 + %5 = load ptr, ptr %4, align 8 + %6 = getelementptr inbounds { ptr, i64 }, ptr %agg.tmp, i32 0, i32 1 + %7 = load i64, ptr %6, align 8 + call void @_check_version(ptr %5, i64 %7) + call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp1, ptr align 8 %p, i64 16, i1 false) + %8 = getelementptr inbounds { ptr, i64 }, ptr %agg.tmp1, i32 0, i32 0 + %9 = load ptr, ptr %8, align 8 + %10 = getelementptr inbounds { ptr, i64 }, ptr %agg.tmp1, i32 0, i32 1 + %11 = load i64, ptr %10, align 8 + call void @_check_offset(ptr %9, i64 %11, i32 noundef 4) + %12 = load i32, ptr %a, align 4 + %raw_ptr = getelementptr inbounds %struct._FatPtr.0, ptr %p, i32 0, i32 0 + %13 = load ptr, ptr %raw_ptr, align 8 + store i32 %12, ptr %13, align 4 + ret i32 0 +} + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #6 + +attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #2 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #3 = { noreturn nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #4 = { nounwind allocsize(0) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #5 = { argmemonly nocallback nofree nounwind willreturn writeonly } +attributes #6 = { argmemonly nocallback nofree nounwind willreturn } +attributes #7 = { nounwind } +attributes #8 = { noreturn nounwind } +attributes #9 = { nounwind allocsize(0) } + +!llvm.module.flags = !{!0, !1, !2, !3, !4} +!llvm.ident = !{!5} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"PIC Level", i32 2} +!2 = !{i32 7, !"PIE Level", i32 2} +!3 = !{i32 7, !"uwtable", i32 2} +!4 = !{i32 7, !"frame-pointer", i32 2} +!5 = !{!"clang version 15.0.4 (git@gitee.com:dengxy2020/llvm-project.git deb995844840c6244907f39a1dbb2c77375cd31e)"} +!6 = distinct !{!6, !7} +!7 = !{!"llvm.loop.mustprogress"} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/ir_asm_anaysis/test.s b/clang/test/BSC/Positive/FatPtr/perform_anaysis/ir_asm_anaysis/test.s new file mode 100644 index 0000000000000000000000000000000000000000..3a60d8b155d99c06c041338ce1b96b82a9e69295 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/ir_asm_anaysis/test.s @@ -0,0 +1,397 @@ + .text + .file "test.cbs" + .globl _report_error # -- Begin function _report_error + .p2align 4, 0x90 + .type _report_error,@function +_report_error: # @_report_error + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + subq $128, %rsp + movq %rdi, -8(%rbp) + movq stderr@GOTPCREL(%rip), %rax + movq (%rax), %rdi + movq -8(%rbp), %rdx + leaq .L.str(%rip), %rsi + movb $0, %al + callq fprintf@PLT + leaq -96(%rbp), %rdi + movl $10, %esi + callq backtrace@PLT + movl %eax, -100(%rbp) + leaq -96(%rbp), %rdi + movl -100(%rbp), %esi + callq backtrace_symbols@PLT + movq %rax, -112(%rbp) + movq stderr@GOTPCREL(%rip), %rax + movq (%rax), %rdi + leaq .L.str.1(%rip), %rsi + movb $0, %al + callq fprintf@PLT + movl $0, -116(%rbp) +.LBB0_1: # %for.cond + # =>This Inner Loop Header: Depth=1 + movl -116(%rbp), %eax + cmpl -100(%rbp), %eax + jge .LBB0_4 +# %bb.2: # %for.body + # in Loop: Header=BB0_1 Depth=1 + movq stderr@GOTPCREL(%rip), %rax + movq (%rax), %rdi + movq -112(%rbp), %rax + movslq -116(%rbp), %rcx + movq (%rax,%rcx,8), %rdx + leaq .L.str.2(%rip), %rsi + movb $0, %al + callq fprintf@PLT +# %bb.3: # %for.inc + # in Loop: Header=BB0_1 Depth=1 + movl -116(%rbp), %eax + addl $1, %eax + movl %eax, -116(%rbp) + jmp .LBB0_1 +.LBB0_4: # %for.end + movq -112(%rbp), %rdi + callq free@PLT + movl $1, %edi + callq exit@PLT +.Lfunc_end0: + .size _report_error, .Lfunc_end0-_report_error + .cfi_endproc + # -- End function + .globl _new_version_number # -- Begin function _new_version_number + .p2align 4, 0x90 + .type _new_version_number,@function +_new_version_number: # @_new_version_number + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + movl $1, -4(%rbp) + movl -4(%rbp), %eax + lock xaddl %eax, _new_version_number.uuid(%rip) + movl %eax, -8(%rbp) + movl _new_version_number.uuid(%rip), %eax + movl %eax, -12(%rbp) + movl -12(%rbp), %eax + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Lfunc_end1: + .size _new_version_number, .Lfunc_end1-_new_version_number + .cfi_endproc + # -- End function + .globl checked_malloc # -- Begin function checked_malloc + .p2align 4, 0x90 + .type checked_malloc,@function +checked_malloc: # @checked_malloc + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + subq $64, %rsp + movq %rdi, -24(%rbp) + cmpq $0, -24(%rbp) + jne .LBB2_2 +# %bb.1: # %if.then + leaq .L.str.3(%rip), %rdi + callq _report_error +.LBB2_2: # %if.end + movq -24(%rbp), %rax + addq $8, %rax + movq %rax, -32(%rbp) + movq -32(%rbp), %rdi + callq malloc@PLT + movq %rax, -40(%rbp) + movq -40(%rbp), %rax + movq %rax, -48(%rbp) + callq _new_version_number + movl %eax, -52(%rbp) + movl -52(%rbp), %ecx + movq -48(%rbp), %rax + movl %ecx, (%rax) + movl -24(%rbp), %ecx + movq -48(%rbp), %rax + movl %ecx, 4(%rax) + movq -48(%rbp), %rax + addq $8, %rax + movq %rax, -16(%rbp) + movl -52(%rbp), %eax + movl %eax, -8(%rbp) + movl $0, -4(%rbp) + movq -16(%rbp), %rax + movq -8(%rbp), %rdx + addq $64, %rsp + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Lfunc_end2: + .size checked_malloc, .Lfunc_end2-checked_malloc + .cfi_endproc + # -- End function + .globl checked_free # -- Begin function checked_free + .p2align 4, 0x90 + .type checked_free,@function +checked_free: # @checked_free + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + subq $32, %rsp + movq %rdi, -16(%rbp) + movq %rsi, -8(%rbp) + movq -16(%rbp), %rax + movslq -4(%rbp), %rdx + xorl %ecx, %ecx + # kill: def $rcx killed $ecx + subq %rdx, %rcx + addq %rcx, %rax + addq $-8, %rax + movq %rax, -24(%rbp) + movq -24(%rbp), %rax + movq %rax, -32(%rbp) + movl -8(%rbp), %eax + movq -32(%rbp), %rcx + cmpl (%rcx), %eax + je .LBB3_2 +# %bb.1: # %if.then + leaq .L.str.4(%rip), %rdi + callq _report_error +.LBB3_2: # %if.end + cmpl $0, -4(%rbp) + je .LBB3_4 +# %bb.3: # %if.then4 + leaq .L.str.5(%rip), %rdi + callq _report_error +.LBB3_4: # %if.end5 + movq -32(%rbp), %rdi + movq -32(%rbp), %rax + movl 4(%rax), %eax + addl $8, %eax + movl %eax, %eax + movl %eax, %edx + xorl %esi, %esi + callq memset@PLT + movq -32(%rbp), %rdi + callq free@PLT + addq $32, %rsp + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Lfunc_end3: + .size checked_free, .Lfunc_end3-checked_free + .cfi_endproc + # -- End function + .globl _check_version # -- Begin function _check_version + .p2align 4, 0x90 + .type _check_version,@function +_check_version: # @_check_version + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + subq $32, %rsp + movq %rdi, -16(%rbp) + movq %rsi, -8(%rbp) + movq -16(%rbp), %rax + movslq -4(%rbp), %rdx + xorl %ecx, %ecx + # kill: def $rcx killed $ecx + subq %rdx, %rcx + addq %rcx, %rax + addq $-8, %rax + movq %rax, -24(%rbp) + movq -24(%rbp), %rax + movq %rax, -32(%rbp) + cmpl $0, -8(%rbp) + je .LBB4_3 +# %bb.1: # %land.lhs.true + movl -8(%rbp), %eax + movq -32(%rbp), %rcx + cmpl (%rcx), %eax + je .LBB4_3 +# %bb.2: # %if.then + leaq .L.str.6(%rip), %rdi + callq _report_error +.LBB4_3: # %if.end + addq $32, %rsp + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Lfunc_end4: + .size _check_version, .Lfunc_end4-_check_version + .cfi_endproc + # -- End function + .globl _check_offset # -- Begin function _check_offset + .p2align 4, 0x90 + .type _check_offset,@function +_check_offset: # @_check_offset + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + subq $48, %rsp + movq %rdi, -16(%rbp) + movq %rsi, -8(%rbp) + movl %edx, -20(%rbp) + movq -16(%rbp), %rax + movslq -4(%rbp), %rdx + xorl %ecx, %ecx + # kill: def $rcx killed $ecx + subq %rdx, %rcx + addq %rcx, %rax + addq $-8, %rax + movq %rax, -32(%rbp) + movq -32(%rbp), %rax + movq %rax, -40(%rbp) + movl -4(%rbp), %eax + addl -20(%rbp), %eax + movl %eax, -44(%rbp) + movq -40(%rbp), %rax + cmpl $0, 4(%rax) + jbe .LBB5_3 +# %bb.1: # %land.lhs.true + movl -44(%rbp), %eax + movq -40(%rbp), %rcx + cmpl 4(%rcx), %eax + jbe .LBB5_3 +# %bb.2: # %if.then + leaq .L.str.7(%rip), %rdi + callq _report_error +.LBB5_3: # %if.end + addq $48, %rsp + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Lfunc_end5: + .size _check_offset, .Lfunc_end5-_check_offset + .cfi_endproc + # -- End function + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + subq $64, %rsp + movl $4, %edi + callq checked_malloc + movq %rax, -16(%rbp) + movq %rdx, -8(%rbp) + movl $6, -20(%rbp) + movq -16(%rbp), %rax + movq %rax, -40(%rbp) + movq -8(%rbp), %rax + movq %rax, -32(%rbp) + movq -40(%rbp), %rdi + movq -32(%rbp), %rsi + callq _check_version + movq -16(%rbp), %rax + movq %rax, -56(%rbp) + movq -8(%rbp), %rax + movq %rax, -48(%rbp) + movq -56(%rbp), %rdi + movq -48(%rbp), %rsi + movl $4, %edx + callq _check_offset + movl -20(%rbp), %ecx + movq -16(%rbp), %rax + movl %ecx, (%rax) + xorl %eax, %eax + addq $64, %rsp + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Lfunc_end6: + .size main, .Lfunc_end6-main + .cfi_endproc + # -- End function + .type .L.str,@object # @.str + .section .rodata.str1.1,"aMS",@progbits,1 +.L.str: + .asciz "%s" + .size .L.str, 3 + + .type .L.str.1,@object # @.str.1 +.L.str.1: + .asciz "Call stack:\n" + .size .L.str.1, 13 + + .type .L.str.2,@object # @.str.2 +.L.str.2: + .asciz "%s\n" + .size .L.str.2, 4 + + .type _new_version_number.uuid,@object # @_new_version_number.uuid + .data + .p2align 2 +_new_version_number.uuid: + .long 10000 # 0x2710 + .size _new_version_number.uuid, 4 + + .type .L.str.3,@object # @.str.3 + .section .rodata.str1.1,"aMS",@progbits,1 +.L.str.3: + .asciz "allocated size is ZERO!\n" + .size .L.str.3, 25 + + .type .L.str.4,@object # @.str.4 +.L.str.4: + .asciz "version number error when free!\n" + .size .L.str.4, 33 + + .type .L.str.5,@object # @.str.5 +.L.str.5: + .asciz "free a pointer which is not point to the head of an allocation!\n" + .size .L.str.5, 65 + + .type .L.str.6,@object # @.str.6 +.L.str.6: + .asciz "version number error!\n" + .size .L.str.6, 23 + + .type .L.str.7,@object # @.str.7 +.L.str.7: + .asciz "pointer offset exceed the allocation size!\n" + .size .L.str.7, 44 + + .ident "clang version 15.0.4 (git@gitee.com:dengxy2020/llvm-project.git deb995844840c6244907f39a1dbb2c77375cd31e)" + .section ".note.GNU-stack","",@progbits + .addrsig + .addrsig_sym _report_error + .addrsig_sym fprintf + .addrsig_sym backtrace + .addrsig_sym backtrace_symbols + .addrsig_sym free + .addrsig_sym exit + .addrsig_sym _new_version_number + .addrsig_sym checked_malloc + .addrsig_sym malloc + .addrsig_sym _check_version + .addrsig_sym _check_offset + .addrsig_sym stderr + .addrsig_sym _new_version_number.uuid diff --git a/clang/test/BSC/Positive/FatPtr/stack.cbs b/clang/test/BSC/Positive/FatPtr/stack.cbs new file mode 100644 index 0000000000000000000000000000000000000000..c44dab32adcaab2dbf472130ea2d838c343a253c --- /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 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 0000000000000000000000000000000000000000..9c5c2136db1102528026fa7e728e2360f0bef03c --- /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; +}