diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h --- a/llvm/lib/Target/BPF/BTFDebug.h +++ b/llvm/lib/Target/BPF/BTFDebug.h @@ -48,6 +48,7 @@ virtual ~BTFTypeBase() = default; void setId(uint32_t Id) { this->Id = Id; } uint32_t getId() { return Id; } + uint32_t getKind() { return Kind; } uint32_t roundupToBytes(uint32_t NumBits) { return (NumBits + 7) >> 3; } /// Get the size of this BTF type entry. virtual uint32_t getSize() { return BTF::CommonTypeSize; } @@ -68,10 +69,12 @@ public: BTFTypeDerived(const DIDerivedType *Ty, unsigned Tag, bool NeedsFixup); - BTFTypeDerived(unsigned NextTypeId, unsigned Tag, StringRef Name); + BTFTypeDerived(unsigned NextTypeId, enum BTF::TypeKinds Kind, + StringRef Name = StringRef()); void completeType(BTFDebug &BDebug) override; void emitType(MCStreamer &OS) override; void setPointeeType(uint32_t PointeeType); + uint32_t getPointeeType(); }; /// Handle struct or union forward declaration. @@ -240,6 +243,8 @@ BTFTypeTypeTag(uint32_t NextTypeId, StringRef Tag); BTFTypeTypeTag(const DIDerivedType *DTy, StringRef Tag); void completeType(BTFDebug &BDebug) override; + uint32_t getNextTypeId(); + StringRef getTag(); }; /// String table. @@ -285,6 +290,20 @@ uint32_t RelocKind; ///< What to patch the instruction }; +/// Used for de-duplication for types annotated with btf_type_tag annotation, +/// See comment at BTFDebug.cpp:addType() for details. +struct BTFTypeDedupKey { + const DIType *CanonTy; + + BTFTypeDedupKey(const DIType *CanonTy) : CanonTy(CanonTy) {} + + bool operator==(const BTFTypeDedupKey &Other) const; + + struct Hash { + size_t operator()(BTFTypeDedupKey const &Key) const; + }; +}; + /// Collect and emit BTF information. class BTFDebug : public DebugHandlerBase { MCStreamer &OS; @@ -296,6 +315,8 @@ BTFStringTable StringTable; std::vector> TypeEntries; std::unordered_map DIToIdMap; + std::unordered_map + DIDedupMap; std::map> FuncInfoTable; std::map> LineInfoTable; std::map> FieldRelocTable; @@ -311,11 +332,17 @@ /// Add types to TypeEntries. /// @{ /// Add types to TypeEntries and DIToIdMap. - uint32_t addType(std::unique_ptr TypeEntry, const DIType *Ty); + uint32_t addType(std::unique_ptr TypeEntry, const DIType *Ty, + uint32_t *RealId = nullptr); /// Add types to TypeEntries only and return type id. uint32_t addType(std::unique_ptr TypeEntry); + uint32_t replaceType(uint32_t Id, std::unique_ptr TypeEntry); /// @} + BTFTypeBase *getType(uint32_t Id); + + std::optional lookupType(const DIType *Ty); + /// IR type visiting functions. /// @{ void visitTypeEntry(const DIType *Ty); @@ -368,7 +395,7 @@ /// the base type of DTy. Return the type id of the first BTF type_tag /// in the chain. If no type_tag's are generated, a negative value /// is returned. - int genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId); + uint32_t genBTFTypeTags(const DIType *Ty, uint32_t BaseId); /// Generate one field relocation record. void generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId, @@ -390,6 +417,16 @@ /// Emit the .BTF.ext section. void emitBTFExtSection(); + uint32_t skipBTFTypeTags(uint32_t Id); + + /// BTF post processing phase rewriting type chains like below: + /// CONST -> TYPE_TAG '...' -> ... + /// To: + /// TYPE_TAG '...' -> CONST -> ... + void moveTypeTagsBeforeCVR(); + class QualifiedTypesCache; + void rebuildTypeTagsChain(uint32_t Id, QualifiedTypesCache &Cache); + protected: /// Gather pre-function debug information. void beginFunctionImpl(const MachineFunction *MF) override; diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp --- a/llvm/lib/Target/BPF/BTFDebug.cpp +++ b/llvm/lib/Target/BPF/BTFDebug.cpp @@ -14,6 +14,8 @@ #include "BPF.h" #include "BPFCORE.h" #include "MCTargetDesc/BPFMCTargetDesc.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineModuleInfo.h" @@ -69,11 +71,10 @@ BTFType.Info = Kind << 24; } -/// Used by DW_TAG_pointer_type only. -BTFTypeDerived::BTFTypeDerived(unsigned NextTypeId, unsigned Tag, +BTFTypeDerived::BTFTypeDerived(unsigned NextTypeId, enum BTF::TypeKinds _Kind, StringRef Name) : DTy(nullptr), NeedsFixup(false), Name(Name) { - Kind = BTF::BTF_KIND_PTR; + Kind = _Kind; BTFType.Info = Kind << 24; BTFType.Type = NextTypeId; } @@ -106,6 +107,8 @@ BTFType.Type = PointeeType; } +uint32_t BTFTypeDerived::getPointeeType() { return BTFType.Type; } + /// Represent a struct/union forward declaration. BTFTypeFwd::BTFTypeFwd(StringRef Name, bool IsUnion) : Name(Name) { Kind = BTF::BTF_KIND_FWD; @@ -490,6 +493,10 @@ } } +uint32_t BTFTypeTypeTag::getNextTypeId() { return BTFType.Type; } + +StringRef BTFTypeTypeTag::getTag() { return Tag; } + uint32_t BTFStringTable::addString(StringRef S) { // Check whether the string already exists. for (auto &OffsetM : OffsetToIdMap) { @@ -511,13 +518,265 @@ addString("\0"); } +static DINodeArray lookupAnnotations(const DIType *Ty) { + DINodeArray Annots = {}; + if (auto *SubTy = dyn_cast(Ty)) + Annots = SubTy->getAnnotations(); + else if (auto *SubTy = dyn_cast(Ty)) + Annots = SubTy->getAnnotations(); + else if (auto *SubTy = dyn_cast(Ty)) + Annots = SubTy->getAnnotations(); + else if (auto *SubTy = dyn_cast(Ty)) + Annots = SubTy->getAnnotations(); + return Annots; +} + +static void collectBTFTypeTags(const DIType *Ty, + SmallVectorImpl &Tags) { + DINodeArray Annots = lookupAnnotations(Ty); + if (!Annots) + return; + + for (const Metadata *Annotations : Annots->operands()) { + const MDNode *MD = cast(Annotations); + if (MD->getNumOperands() != 2) + continue; + const MDString *Name = dyn_cast(MD->getOperand(0)); + if (!Name) + continue; + if (!Name->getString().equals("btf:type_tag")) + continue; + // For type with "int __tag1 __tag2 *p", the Tags will have + // content: [__tag1, __tag2]. + Tags.push_back(cast(MD->getOperand(1))); + } +} + +/// Generate btf_type_tag chains. +uint32_t BTFDebug::genBTFTypeTags(const DIType *Ty, uint32_t BaseId) { + SmallVector MDStrs; + collectBTFTypeTags(Ty, MDStrs); + // With MDStrs [__tag1, __tag2], the output type chain looks like + // PTR -> __tag2 -> __tag1 -> BaseType + // In the below, we construct BTF types with the order of __tag1, __tag2 + // and PTR. + for (unsigned I = 0; I < MDStrs.size(); I++) { + const MDString *Value = MDStrs[I]; + auto TagEntry = + std::make_unique(BaseId, Value->getString()); + BaseId = addType(std::move(TagEntry)); + } + return BaseId; +} + +static hash_code hashElements(const DICompositeType *Ty) { + hash_code Hash = hash_value(Ty->getElements().size()); + for (auto *Node : Ty->getElements()) { + if (Node->getTag() == dwarf::DW_TAG_member) { + auto *Member = cast(Node); + Hash = hash_combine(Hash, Member->getName()); + } + if (Node->getTag() == dwarf::DW_TAG_enumerator) { + auto *Member = cast(Node); + Hash = hash_combine(Hash, Member->getName()); + } + } + return Hash; +} + +static bool compareElements(const DICompositeType *A, + const DICompositeType *B) { + if (A->getElements().size() != B->getElements().size()) + return false; + + auto AI = A->getElements().begin(); + auto AE = A->getElements().end(); + auto BI = B->getElements().begin(); + for (; AI != AE; ++AI, ++BI) { + if ((*AI)->getTag() != (*BI)->getTag()) + return false; + + if ((*AI)->getTag() == dwarf::DW_TAG_member) { + auto *MA = cast(*AI); + auto *MB = cast(*BI); + if (!MA->getName().equals(MB->getName()) || + MA->getBaseType() != MB->getBaseType() || + MA->getOffsetInBits() != MB->getOffsetInBits()) + return false; + } + + if ((*AI)->getTag() == dwarf::DW_TAG_enumerator) { + auto *MA = cast(*AI); + auto *MB = cast(*BI); + if (!MA->getName().equals(MB->getName()) || + MA->isUnsigned() != MB->isUnsigned() || + MA->getValue().getZExtValue() != MB->getValue().getZExtValue()) + return false; + } + } + + return true; +} + +bool BTFTypeDedupKey::operator==(const BTFTypeDedupKey &Other) const { + auto *OtherTy = Other.CanonTy; + + if (CanonTy->getTag() != OtherTy->getTag()) + return false; + + if (!CanonTy->getName().equals(OtherTy->getName())) + return false; + + switch (CanonTy->getTag()) { + case dwarf::DW_TAG_base_type: { + auto *A = cast(CanonTy); + auto *B = cast(OtherTy); + return A->getEncoding() == B->getEncoding() && + A->getOffsetInBits() == B->getOffsetInBits() && + A->getSizeInBits() == B->getSizeInBits(); + } + + case dwarf::DW_TAG_typedef: { + auto *A = cast(CanonTy); + auto *B = cast(OtherTy); + return A->getBaseType() == B->getBaseType(); + } + + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_union_type: { + auto *A = cast(CanonTy); + auto *B = cast(OtherTy); + return A->isForwardDecl() == B->isForwardDecl() && compareElements(A, B); + } + + case dwarf::DW_TAG_subroutine_type: { + auto *A = cast(CanonTy); + auto *B = cast(OtherTy); + return std::equal(A->getTypeArray().begin(), A->getTypeArray().end(), + B->getTypeArray().begin()); + } + + default: + llvm_unreachable("Comparing unexpected dedup key"); + } +} + +size_t BTFTypeDedupKey::Hash::operator()(BTFTypeDedupKey const &Key) const { + auto *Ty = Key.CanonTy; + hash_code Hash = + hash_combine(Ty->getTag(), Ty->getName(), Ty->getSizeInBits()); + + switch (Ty->getTag()) { + case dwarf::DW_TAG_base_type: { + auto *BTy = cast(Ty); + Hash = hash_combine(BTy->getEncoding(), BTy->getOffsetInBits(), + BTy->getSizeInBits()); + break; + } + + case dwarf::DW_TAG_typedef: + // Nothing to be done, name & tag suffice + break; + + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_enumeration_type: + Hash = hash_combine(Hash, hashElements(cast(Key.CanonTy))); + break; + + case dwarf::DW_TAG_subroutine_type: { + auto *STy = cast(Key.CanonTy); + Hash = hash_combine(Hash, STy->getTypeArray().size()); + for (DIType *Param : STy->getTypeArray()) + Hash = hash_combine(Hash, + Param ? hash_value(Param->getName()) : hash_value(0)); + break; + } + + default: + llvm_unreachable("Hashing unexpected dedup key"); + }; + + return Hash; +} + +static std::optional makeDedupKey(const DIType *Ty) { + if (Ty == nullptr) + return std::nullopt; + + switch (Ty->getTag()) { + case dwarf::DW_TAG_base_type: + case dwarf::DW_TAG_typedef: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_subroutine_type: + return std::optional(BTFTypeDedupKey(Ty)); + default: + return std::nullopt; + } +} + +// `btf_type_tag`s are encoded in DI classes as `annotations` fields, +// this might lead to some DI info duplication. +// For example, the following C code: +// +// #define __tag1 __attribute__((btf_type_tag("tag1"))) +// +// struct foo {}; +// struct bar { +// struct foo __tag1 a; +// struct foo b; +// } g; +// +// Generates the following DI representation: +// +// !5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", +// ..., elements: !6) +// !6 = !{!7, !8} +// !7 = !DIDerivedType(tag: DW_TAG_member, name: "a", ..., baseType: !10) +// !8 = !DIDerivedType(tag: DW_TAG_member, name: "b", ..., baseType: !9) +// !9 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", ...) +// !10 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", ..., +// annotations: !11) +// !11 = !{!12} +// !12 = !{!"btf:type_tag", !"tag1"} +// +// Note two instances of structure "foo", one with annotations, one without. +// +// In order to avoid such duplication in generated BTF two things are used: +// - a map `BTFDebug::DIDedupMap` +// - special logic in `BTFDebug::addType` +// +// The key of `BTFDebug::DIDedupMap` is `DIType*` wrapped in an auxiliary +// type that provides custom hashing and equality operations. +// +// The value of `BTFDebug::DIDedupMap` is BTF id of the type w/o type tags. +// +// Hashing and equality functions of `BTFDebug::DIDedupMap` ignore +// `annotations` field on a first level and compare all other fields +// significant for BTF generation. +// +// The `BTFDebug::addType(..., DIType *Ty, ...)` operates as follows: +// - establishes a base type: +// - if a type similar to `Ty` could be found in `BTFDebug::DIDedupMap` +// it is used as base; +// - otherwise a fresh BTF type is used as base; +// - adds btf type tag wrappers: +// - a series of new BTF_TYPE_TAG types is generated wrapping one +// another, with first type wrapping the base type. uint32_t BTFDebug::addType(std::unique_ptr TypeEntry, - const DIType *Ty) { - TypeEntry->setId(TypeEntries.size() + 1); - uint32_t Id = TypeEntry->getId(); - DIToIdMap[Ty] = Id; - TypeEntries.push_back(std::move(TypeEntry)); - return Id; + const DIType *Ty, uint32_t *RealId) { + uint32_t Id = TypeEntry ? addType(std::move(TypeEntry)) : 0; + auto Key = makeDedupKey(Ty); + if (Key.has_value()) + DIDedupMap[*Key] = Id; + uint32_t TagId = genBTFTypeTags(Ty, Id); + DIToIdMap[Ty] = TagId; + if (RealId) + *RealId = Id; + return TagId; } uint32_t BTFDebug::addType(std::unique_ptr TypeEntry) { @@ -527,7 +786,37 @@ return Id; } +uint32_t BTFDebug::replaceType(uint32_t Id, + std::unique_ptr TypeEntry) { + TypeEntry->setId(Id); + TypeEntries[Id - 1] = std::move(TypeEntry); + return Id; +} + +std::optional BTFDebug::lookupType(const DIType *Ty) { + if (DIToIdMap.find(Ty) != DIToIdMap.end()) + return std::optional(DIToIdMap[Ty]); + + auto Key = makeDedupKey(Ty); + if (!Key.has_value() || (DIDedupMap.find(*Key) == DIDedupMap.end())) + return std::nullopt; + + SmallVector MDStrs; + collectBTFTypeTags(Ty, MDStrs); + auto TagId = genBTFTypeTags(Ty, DIDedupMap[*Key]); + DIToIdMap[Ty] = TagId; + return TagId; +} + void BTFDebug::visitBasicType(const DIBasicType *BTy, uint32_t &TypeId) { + // Such "void" entries might arise from use btf_type_tag, e.g.: + // void __attribute__((btf_type_tag("foo"))) *p; + if (BTy->getTag() == dwarf::DW_TAG_unspecified_type && + BTy->getName() == "void") { + TypeId = addType(nullptr, BTy); + return; + } + // Only int and binary floating point types are supported in BTF. uint32_t Encoding = BTy->getEncoding(); std::unique_ptr TypeEntry; @@ -617,46 +906,6 @@ return FuncId; } -/// Generate btf_type_tag chains. -int BTFDebug::genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId) { - SmallVector MDStrs; - DINodeArray Annots = DTy->getAnnotations(); - if (Annots) { - // For type with "int __tag1 __tag2 *p", the MDStrs will have - // content: [__tag1, __tag2]. - for (const Metadata *Annotations : Annots->operands()) { - const MDNode *MD = cast(Annotations); - const MDString *Name = cast(MD->getOperand(0)); - if (!Name->getString().equals("btf_type_tag")) - continue; - MDStrs.push_back(cast(MD->getOperand(1))); - } - } - - if (MDStrs.size() == 0) - return -1; - - // With MDStrs [__tag1, __tag2], the output type chain looks like - // PTR -> __tag2 -> __tag1 -> BaseType - // In the below, we construct BTF types with the order of __tag1, __tag2 - // and PTR. - unsigned TmpTypeId; - std::unique_ptr TypeEntry; - if (BaseTypeId >= 0) - TypeEntry = - std::make_unique(BaseTypeId, MDStrs[0]->getString()); - else - TypeEntry = std::make_unique(DTy, MDStrs[0]->getString()); - TmpTypeId = addType(std::move(TypeEntry)); - - for (unsigned I = 1; I < MDStrs.size(); I++) { - const MDString *Value = MDStrs[I]; - TypeEntry = std::make_unique(TmpTypeId, Value->getString()); - TmpTypeId = addType(std::move(TypeEntry)); - } - return TmpTypeId; -} - /// Handle structure/union types. void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct, uint32_t &TypeId) { @@ -678,17 +927,18 @@ auto TypeEntry = std::make_unique(CTy, IsStruct, HasBitField, VLen); StructTypes.push_back(TypeEntry.get()); - TypeId = addType(std::move(TypeEntry), CTy); + uint32_t StructId; + TypeId = addType(std::move(TypeEntry), CTy, &StructId); // Check struct/union annotations - processDeclAnnotations(CTy->getAnnotations(), TypeId, -1); + processDeclAnnotations(CTy->getAnnotations(), StructId, -1); // Visit all struct members. int FieldNo = 0; for (const auto *Element : Elements) { const auto Elem = cast(Element); visitTypeEntry(Elem); - processDeclAnnotations(Elem->getAnnotations(), TypeId, FieldNo); + processDeclAnnotations(Elem->getAnnotations(), StructId, FieldNo); FieldNo++; } } @@ -821,23 +1071,14 @@ } } - if (Tag == dwarf::DW_TAG_pointer_type) { - int TmpTypeId = genBTFTypeTags(DTy, -1); - if (TmpTypeId >= 0) { - auto TypeDEntry = - std::make_unique(TmpTypeId, Tag, DTy->getName()); - TypeId = addType(std::move(TypeDEntry), DTy); - } else { - auto TypeEntry = std::make_unique(DTy, Tag, false); - TypeId = addType(std::move(TypeEntry), DTy); - } - } else if (Tag == dwarf::DW_TAG_typedef || Tag == dwarf::DW_TAG_const_type || - Tag == dwarf::DW_TAG_volatile_type || - Tag == dwarf::DW_TAG_restrict_type) { + if (Tag == dwarf::DW_TAG_pointer_type || Tag == dwarf::DW_TAG_typedef || + Tag == dwarf::DW_TAG_const_type || Tag == dwarf::DW_TAG_volatile_type || + Tag == dwarf::DW_TAG_restrict_type) { auto TypeEntry = std::make_unique(DTy, Tag, false); - TypeId = addType(std::move(TypeEntry), DTy); + uint32_t RealId = 0; + TypeId = addType(std::move(TypeEntry), DTy, &RealId); if (Tag == dwarf::DW_TAG_typedef) - processDeclAnnotations(DTy->getAnnotations(), TypeId, -1); + processDeclAnnotations(DTy->getAnnotations(), RealId, -1); } else if (Tag != dwarf::DW_TAG_member) { return; } @@ -860,8 +1101,13 @@ /// will be generated. void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId, bool CheckPointer, bool SeenPointer) { - if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) { + if (!Ty) { TypeId = DIToIdMap[Ty]; + return; + } + + if (auto OptTypeId = lookupType(Ty)) { + TypeId = *OptTypeId; // To handle the case like the following: // struct t; @@ -892,14 +1138,14 @@ // We will traverse const/ptr/volatile which already have corresponding // BTF types and generate type for 'struct' which might be in Fixup // state. - if (Ty && (!CheckPointer || !SeenPointer)) { + if (!CheckPointer || !SeenPointer) { if (const auto *DTy = dyn_cast(Ty)) { while (DTy) { const DIType *BaseTy = DTy->getBaseType(); if (!BaseTy) break; - if (DIToIdMap.find(BaseTy) != DIToIdMap.end()) { + if (lookupType(BaseTy)) { DTy = dyn_cast(BaseTy); } else { if (CheckPointer && DTy->getTag() == dwarf::DW_TAG_pointer_type) { @@ -1243,6 +1489,26 @@ SecNameOff = 0; } +BTFTypeBase *BTFDebug::getType(uint32_t Id) { + return TypeEntries[Id - 1].get(); +} + +uint32_t BTFDebug::skipBTFTypeTags(uint32_t Id) { + for (;;) { + if (Id == 0) + break; + + BTFTypeBase *Ty = getType(Id); + if (Ty->getKind() != BTF::BTF_KIND_TYPE_TAG) + break; + + auto *TagTy = static_cast(Ty); + Id = TagTy->getNextTypeId(); + } + + return Id; +} + /// On-demand populate types as requested from abstract member /// accessing or preserve debuginfo type. unsigned BTFDebug::populateType(const DIType *Ty) { @@ -1250,7 +1516,10 @@ visitTypeEntry(Ty, Id, false, false); for (const auto &TypeEntry : TypeEntries) TypeEntry->completeType(*this); - return Id; + + // Skip type tags, libbpf expects each relocation entry to point to + // struct/union/enum. + return skipBTFTypeTags(Id); } /// Generate a struct member field relocation. @@ -1450,6 +1719,9 @@ break; } + // Kernel does not handle VARs with type 'TYPE_TAG -> something' + GVTypeId = skipBTFTypeTags(GVTypeId); + // Only support the following globals: // . static variables // . non-static weak or non-weak global variables @@ -1583,6 +1855,196 @@ } } +// Cache IDs for BTF types with shape: +// - (TYPE_TAG ) +// - (CONST ) +// - (VOLATILE ) +// - (RESTRICT ) +class BTFDebug::QualifiedTypesCache { +public: + struct Entry { + const StringRef Tag; + const uint32_t Id; + const BTF::TypeKinds Kind; + + Entry(uint32_t Id, StringRef Tag) + : Tag(Tag), Id(Id), Kind(BTF::BTF_KIND_TYPE_TAG) {} + Entry(uint32_t Id, BTF::TypeKinds Kind) + : Tag(StringRef()), Id(Id), Kind(Kind) {} + }; + + struct EntryHash { + std::size_t operator()(Entry const &E) const { + return hash_combine(E.Tag, E.Id, E.Kind); + } + }; + + struct EntryEq { + bool operator()(const Entry &LHS, const Entry &RHS) const { + return std::tie(LHS.Tag, LHS.Id, LHS.Kind) == + std::tie(RHS.Tag, RHS.Id, RHS.Kind); + } + }; + +private: + std::unordered_map Cache; + + uint32_t lookup(const Entry &Entry) { + auto Cached = Cache.find(Entry); + if (Cached != Cache.end()) + return Cached->second; + return 0; + } + +public: + void add(uint32_t Id, BTFTypeDerived *Type) { + Cache[Entry(Type->getPointeeType(), (BTF::TypeKinds)Type->getKind())] = Id; + } + + void add(uint32_t Id, BTFTypeTypeTag *Type) { + Cache[Entry(Type->getNextTypeId(), Type->getTag())] = Id; + } + + uint32_t lookupQualified(uint32_t Id, BTF::TypeKinds Kind) { + return lookup(Entry(Id, Kind)); + } + + uint32_t lookupTypeTag(uint32_t Id, StringRef Tag) { + return lookup(Entry(Id, Tag)); + } +}; + +// Convert BTF type chain of shape: +// CONST -> VOLATILE -> RESTRICT -> TYPE_TAG -> ... +// ^ +// '- TopId +// +// To: +// TYPE_TAG -> CONST -> VOLATILE -> RESTRICT -> ... +// ^ +// '- TopId +void BTFDebug::rebuildTypeTagsChain(uint32_t TopId, + QualifiedTypesCache &Cache) { + SmallSet Qualifiers; + SmallVector Tags; + uint32_t Id = TopId; + + // First, get to the bottom of the chain, accumulating CVR + // qualifiers and type tags + for (;;) { + if (Id == 0) + break; + auto *Type = getType(Id); + if (!Type) + break; + switch (Type->getKind()) { + case BTF::BTF_KIND_CONST: + case BTF::BTF_KIND_VOLATILE: + case BTF::BTF_KIND_RESTRICT: + Qualifiers.insert((BTF::TypeKinds)Type->getKind()); + Id = static_cast(Type)->getPointeeType(); + continue; + case BTF::BTF_KIND_TYPE_TAG: + Tags.push_back(static_cast(Type)->getTag()); + Id = static_cast(Type)->getNextTypeId(); + continue; + } + break; + } + + if (Qualifiers.empty() || Tags.empty()) + return; + + // Next, rebuild the chain using shape (Tags (Qualifiers Id)). + // Track intermediate result in 'Id'. + for (auto Kind : + {BTF::BTF_KIND_RESTRICT, BTF::BTF_KIND_VOLATILE, BTF::BTF_KIND_CONST}) { + if (!Qualifiers.contains(Kind)) + continue; + if (uint32_t CachedId = Cache.lookupQualified(Id, Kind)) { + Id = CachedId; + continue; + } + auto Type = std::make_unique(Id, Kind); + auto *TypePtr = Type.get(); + Type->completeType(*this); + Id = addType(std::move(Type)); + Cache.add(Id, TypePtr); + } + + // All but last type tags could use cache + for (int I = Tags.size() - 1; I > 0; --I) { + if (uint32_t CachedId = Cache.lookupTypeTag(Id, Tags[I])) { + Id = CachedId; + continue; + } + auto Type = std::make_unique(Id, Tags[I]); + auto *TypePtr = Type.get(); + Type->completeType(*this); + Id = addType(std::move(Type)); + Cache.add(Id, TypePtr); + } + + // Last type tag has to be built anew because it needs to replace + // entry at TopId + auto Type = std::make_unique(Id, Tags[0]); + Type->completeType(*this); + Cache.add(TopId, Type.get()); + replaceType(TopId, std::move(Type)); +} + +// Linux Kernel expects type tags to precede CVR qualifiers, but this +// is not guaranteed by the way BTF is generated from DWARF. +// Use a post-processing step to ensure this property. +// +// Convert each chain of shape: +// +// CONST -> VOLATILE -> TYPE_TAG -> ... +// ^ ^ ^ +// '- Id_A '- Id_B '- Id_C +// +// To a set of chains: +// +// TYPE_TAG -> CONST -> VOLATILE -> ... +// ^ \ / +// '- Id_A new ids +// | +// TYPE_TAG -> VOLATILE -> ... +// ^ +// '- Id_B +// +// TYPE_TAG -> ... +// ^ +// '- Id_C +// +// Here TYPE_TAG entries for Id_A and Id_B are newly created and might +// be redundant, however removing such redundancy would require deletion +// of Id_B and Id_C entries and update of all BTF ids and references. +// +// For now, assume that this should not lead to significant BTF +// increase in practice and keep redundant entries. +void BTFDebug::moveTypeTagsBeforeCVR() { + const size_t N = TypeEntries.size() + 1; + QualifiedTypesCache Cache; + + for (uint32_t Id = 1; Id < N; ++Id) { + BTFTypeBase *Type = getType(Id); + switch (Type->getKind()) { + case BTF::BTF_KIND_CONST: + case BTF::BTF_KIND_VOLATILE: + case BTF::BTF_KIND_RESTRICT: + Cache.add(Id, static_cast(Type)); + break; + case BTF::BTF_KIND_TYPE_TAG: + Cache.add(Id, static_cast(Type)); + break; + } + } + + for (uint32_t Id = 1; Id < N; ++Id) + rebuildTypeTagsChain(Id, Cache); +} + void BTFDebug::endModule() { // Collect MapDef globals if not collected yet. if (MapDefNotCollected) { @@ -1604,27 +2066,17 @@ // Search through struct types uint32_t StructTypeId = 0; - for (const auto &StructType : StructTypes) { - if (StructType->getName() == TypeName) { - StructTypeId = StructType->getId(); - break; - } - } + if (auto OptId = lookupType(CTy)) + StructTypeId = *OptId; if (StructTypeId == 0) { auto FwdTypeEntry = std::make_unique(TypeName, IsUnion); - StructTypeId = addType(std::move(FwdTypeEntry)); + StructTypeId = addType(std::move(FwdTypeEntry), CTy); } for (auto &TypeInfo : Fixup.second) { - const DIDerivedType *DTy = TypeInfo.first; BTFTypeDerived *BDType = TypeInfo.second; - - int TmpTypeId = genBTFTypeTags(DTy, StructTypeId); - if (TmpTypeId >= 0) - BDType->setPointeeType(TmpTypeId); - else - BDType->setPointeeType(StructTypeId); + BDType->setPointeeType(StructTypeId); } } @@ -1632,6 +2084,9 @@ for (const auto &TypeEntry : TypeEntries) TypeEntry->completeType(*this); + // BTF to BTF transformations + moveTypeTagsBeforeCVR(); + // Emit BTF sections. emitBTFSection(); emitBTFExtSection(); diff --git a/llvm/test/CodeGen/BPF/BTF/print_btf.py b/llvm/test/CodeGen/BPF/BTF/print_btf.py new file mode 100755 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/print_btf.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 + +# Ad-hoc script to print BTF file in a readable format. +# Follows the same printing conventions as bpftool with format 'raw'. +# Usage: +# +# ./print_btf.py +# +# Parameters: +# +# :: a file name or '-' to read from stdin. +# +# Intended usage: +# +# llvm-objcopy --dump-section .BTF=- | ./print_btf.py - +# +# Kernel documentation contains detailed format description: +# https://www.kernel.org/doc/html/latest/bpf/btf.html + +import struct +import ctypes +import sys + +class SafeDict(dict): + def __getitem__(self, key): + try: + return dict.__getitem__(self, key) + except KeyError: + return f'' + +KINDS = SafeDict({ + 0: 'UNKN', + 1: 'INT', + 2: 'PTR', + 3: 'ARRAY', + 4: 'STRUCT', + 5: 'UNION', + 6: 'ENUM', + 7: 'FWD', + 8: 'TYPEDEF', + 9: 'VOLATILE', + 10: 'CONST', + 11: 'RESTRICT', + 12: 'FUNC', + 13: 'FUNC_PROTO', + 14: 'VAR', + 15: 'DATASEC', + 16: 'FLOAT', + 17: 'DECL_TAG', + 18: 'TYPE_TAG', + 19: 'ENUM64', +}) + +INT_ENCODING = SafeDict({ + 0 << 0: '(none)', + 1 << 0: 'SIGNED', + 1 << 1: 'CHAR', + 1 << 2: 'BOOL' +}) + +ENUM_ENCODING = SafeDict({ + 0: 'UNSIGNED', + 1: 'SIGNED' +}) + +FUNC_LINKAGE = SafeDict({ + 0: 'static', + 1: 'global', + 2: 'extern' +}) + +VAR_LINKAGE = SafeDict({ + 0: 'static', + 1: 'global', +}) + +FWD_KIND = SafeDict({ + 0: 'struct', + 1: 'union', +}) + +for val, name in KINDS.items(): + globals()['BTF_KIND_' + name] = val + +def warn(message): + print(message, file=sys.stderr) + +def print_btf(filename): + if filename == '-': + buf = sys.stdin.buffer.read() + else: + with open(filename, 'rb') as file: + buf = file.read() + + fmt_cache = {} + endian_pfx = '' + off = 0 + + def unpack(fmt): + nonlocal off, endian_pfx + fmt = endian_pfx + fmt + if fmt not in fmt_cache: + fmt_cache[fmt] = struct.Struct(fmt) + st = fmt_cache[fmt] + r = st.unpack_from(buf, off) + off += st.size + return r + + # Use magic number at the header start to determine endianness + magic, = unpack('H') + if magic == 0xeb9f: + endian_pfx = '<' + elif magic == 0x9feb: + endian_pfx = '>' + else: + warn(f'Unexpected BTF magic: {magic:02x}') + return + + # Rest of the header + version, flags, hdr_len = unpack('BBI') + type_off, type_len, str_off, str_len = unpack('IIII') + + # Offsets in the header are relative to the end of a header + type_off += hdr_len + str_off += hdr_len + off = hdr_len + type_end = type_off + type_len + + def string(rel_off): + try: + start = str_off + rel_off + end = buf.index(b"\0", start); + if start == end: + return '(anon)' + return buf[start:end].decode('utf8') + except ValueError as e: + warn(f"Can't get string at offset {str_off} + {rel_off}: {e}") + return f'' + + idx = 1 + while off < type_end: + name_off, info, size = unpack('III') + kind = (info >> 24) & 0x1f + vlen = info & 0xffff + kflag = info >> 31 + kind_name = KINDS[kind] + name = string(name_off) + + def warn_nonzero(val, name): + nonlocal idx + if val != 0: + warn(f'<{idx}> {name} should be 0 but is {val}') + + if kind == BTF_KIND_INT: + info, = unpack('I') + encoding = (info & 0x0f000000) >> 24 + offset = (info & 0x00ff0000) >> 16 + bits = info & 0x000000ff + enc_name = INT_ENCODING[encoding] + print(f"[{idx}] {kind_name} '{name}' size={size} " + f"bits_offset={offset} " + f"nr_bits={bits} encoding={enc_name}") + warn_nonzero(kflag, 'kflag') + warn_nonzero(vlen, 'vlen') + + elif kind in [BTF_KIND_PTR, BTF_KIND_CONST, BTF_KIND_VOLATILE, + BTF_KIND_RESTRICT]: + print(f"[{idx}] {kind_name} '{name}' type_id={size}") + warn_nonzero(name_off, 'name_off') + warn_nonzero(kflag, 'kflag') + warn_nonzero(vlen, 'vlen') + + elif kind == BTF_KIND_ARRAY: + warn_nonzero(name_off, 'name_off') + warn_nonzero(kflag, 'kflag') + warn_nonzero(vlen, 'vlen') + warn_nonzero(size, 'size') + type, index_type, nelems = unpack('III') + print(f"[{idx}] {kind_name} '{name}' type_id={type} " + f"index_type_id={index_type} nr_elems={nelems}") + + elif kind in [BTF_KIND_STRUCT, BTF_KIND_UNION]: + print(f"[{idx}] {kind_name} '{name}' size={size} vlen={vlen}") + if kflag not in [0, 1]: + warn(f'<{idx}> kflag should 0 or 1: {kflag}') + for _ in range(0, vlen): + name_off, type, offset = unpack('III') + if kflag == 0: + print(f"\t'{string(name_off)}' type_id={type} " + f"bits_offset={offset}") + else: + bits_offset = offset & 0xffffff + bitfield_size = offset >> 24 + print(f"\t'{string(name_off)}' type_id={type} " + f"bits_offset={bits_offset} " + f"bitfield_size={bitfield_size}") + + elif kind == BTF_KIND_ENUM: + encoding=ENUM_ENCODING[kflag] + print(f"[{idx}] {kind_name} '{name}' encoding={encoding} " + f"size={size} vlen={vlen}") + for _ in range(0, vlen): + name_off, = unpack('I') + val, = unpack('i' if kflag == 1 else 'I') + print(f"\t'{string(name_off)}' val={val}") + + elif kind == BTF_KIND_ENUM64: + encoding=ENUM_ENCODING[kflag] + print(f"[{idx}] {kind_name} '{name}' encoding={encoding} " + f"size={size} vlen={vlen}") + for _ in range(0, vlen): + name_off, lo, hi = unpack('III') + val = hi << 32 | lo + if kflag == 1: + val = ctypes.c_long(val).value + print(f"\t'{string(name_off)}' val={val}LL") + + elif kind == BTF_KIND_FWD: + print(f"[{idx}] {kind_name} '{name}' fwd_kind={FWD_KIND[kflag]}") + warn_nonzero(vlen, 'vlen') + warn_nonzero(size, 'size') + + elif kind in [BTF_KIND_TYPEDEF, BTF_KIND_TYPE_TAG]: + print(f"[{idx}] {kind_name} '{name}' type_id={size}") + warn_nonzero(kflag, 'kflag') + warn_nonzero(kflag, 'vlen') + + elif kind == BTF_KIND_FUNC: + linkage = FUNC_LINKAGE[vlen] + print(f"[{idx}] {kind_name} '{name}' type_id={size} " + f"linkage={linkage}") + warn_nonzero(kflag, 'kflag') + + elif kind == BTF_KIND_FUNC_PROTO: + print(f"[{idx}] {kind_name} '{name}' ret_type_id={size} " + f"vlen={vlen}") + warn_nonzero(name_off, 'name_off') + warn_nonzero(kflag, 'kflag') + for _ in range(0, vlen): + name_off, type = unpack('II') + print(f"\t'{string(name_off)}' type_id={type}") + + elif kind == BTF_KIND_VAR: + linkage, = unpack('I') + linkage = VAR_LINKAGE[linkage] + print(f"[{idx}] {kind_name} '{name}' type_id={size}, " + f"linkage={linkage}") + warn_nonzero(kflag, 'kflag') + warn_nonzero(vlen, 'vlen') + + elif kind == BTF_KIND_DATASEC: + print(f"[{idx}] {kind_name} '{name}' size={size} vlen={vlen}") + warn_nonzero(kflag, 'kflag') + warn_nonzero(size, 'size') + for _ in range(0, vlen): + type, offset, size = unpack('III') + print(f"\ttype_id={type} offset={offset} size={size}") + + elif kind == BTF_KIND_FLOAT: + print(f"[{idx}] {kind_name} '{name}' size={size}") + warn_nonzero(kflag, 'kflag') + warn_nonzero(vlen, 'vlen') + + elif kind == BTF_KIND_DECL_TAG: + component_idx, = unpack('i') + print(f"[{idx}] {kind_name} '{name}' type_id={size} " + + f"component_idx={component_idx}") + warn_nonzero(kflag, 'kflag') + warn_nonzero(vlen, 'vlen') + + else: + warn(f'<{idx}> Unexpected entry: kind={kind_name} ' + f'name_off={name_off} ' + f'vlen={vlen} kflag={kflag} size={size}') + + idx += 1 + +if __name__ == '__main__': + if len(sys.argv) != 2: + warn('Usage: {sys.argv[0]} ') + sys.exit(1) + print_btf(sys.argv[1]) diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags-reuse.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags-reuse.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags-reuse.ll @@ -0,0 +1,63 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; +; const volatile __tag1 __tag2 int a; +; const volatile __tag1 int b; +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + + +@a = dso_local constant i32 0, align 4, !dbg !0 +@b = dso_local constant i32 0, align 4, !dbg !5 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!17, !18, !19, !20} +!llvm.ident = !{!21} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 4, type: !12, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!4 = !{!0, !5} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true) +!7 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !8) +!8 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !9) +!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !10) +!10 = !{!11} +!11 = !{!"btf:type_tag", !"tag1"} +!12 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !13) +!13 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !14) +!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !15) +!15 = !{!11, !16} +!16 = !{!"btf:type_tag", !"tag2"} +!17 = !{i32 7, !"Dwarf Version", i32 5} +!18 = !{i32 2, !"Debug Info Version", i32 3} +!19 = !{i32 1, !"wchar_size", i32 4} +!20 = !{i32 7, !"frame-pointer", i32 2} +!21 = !{!"clang, some version"} + +; CHECK: [1] TYPE_TAG 'tag2' type_id=14 +; CHECK: [2] TYPE_TAG 'tag2' type_id=15 +; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK: [4] TYPE_TAG 'tag1' type_id=3 +; CHECK: [5] TYPE_TAG 'tag2' type_id=4 +; CHECK: [6] VAR 'a' type_id=1, linkage=global +; CHECK: [7] TYPE_TAG 'tag1' type_id=13 +; CHECK: [8] TYPE_TAG 'tag1' type_id=12 +; CHECK: [9] TYPE_TAG 'tag1' type_id=3 +; CHECK: [10] VAR 'b' type_id=7, linkage=global +; CHECK: [11] DATASEC '.rodata' size=0 vlen=2 +; CHECK: type_id=6 offset=0 size=4 +; CHECK: type_id=10 offset=0 size=4 +; CHECK: [12] VOLATILE '(anon)' type_id=3 +; CHECK: [13] CONST '(anon)' type_id=12 +; CHECK: [14] TYPE_TAG 'tag1' type_id=13 +; CHECK: [15] TYPE_TAG 'tag1' type_id=12 diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags.ll @@ -0,0 +1,50 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; +; const volatile __tag1 __tag2 int a; +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + + +@a = dso_local constant i32 0, align 4, !dbg !0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!11, !12, !13, !14} +!llvm.ident = !{!15} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 4, type: !5, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!4 = !{!0} +!5 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !6) +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !8) +!8 = !{!9, !10} +!9 = !{!"btf:type_tag", !"tag1"} +!10 = !{!"btf:type_tag", !"tag2"} +!11 = !{i32 7, !"Dwarf Version", i32 5} +!12 = !{i32 2, !"Debug Info Version", i32 3} +!13 = !{i32 1, !"wchar_size", i32 4} +!14 = !{i32 7, !"frame-pointer", i32 2} +!15 = !{!"clang, some version"} + +; CHECK: [1] TYPE_TAG 'tag2' type_id=10 +; CHECK: [2] TYPE_TAG 'tag2' type_id=11 +; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK: [4] TYPE_TAG 'tag1' type_id=3 +; CHECK: [5] TYPE_TAG 'tag2' type_id=4 +; CHECK: [6] VAR 'a' type_id=1, linkage=global +; CHECK: [7] DATASEC '.rodata' size=0 vlen=1 +; CHECK: type_id=6 offset=0 size=4 +; CHECK: [8] VOLATILE '(anon)' type_id=3 +; CHECK: [9] CONST '(anon)' type_id=8 +; CHECK: [10] TYPE_TAG 'tag1' type_id=9 +; CHECK: [11] TYPE_TAG 'tag1' type_id=8 diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse-2.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse-2.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse-2.ll @@ -0,0 +1,51 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; +; const volatile __tag1 int a; +; const volatile __tag1 int b; +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + + +@a = dso_local constant i32 0, align 4, !dbg !0 +@b = dso_local constant i32 0, align 4, !dbg !5 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!12, !13, !14, !15} +!llvm.ident = !{!16} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 3, type: !7, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!4 = !{!0, !5} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 4, type: !7, isLocal: false, isDefinition: true) +!7 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !8) +!8 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !9) +!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !10) +!10 = !{!11} +!11 = !{!"btf:type_tag", !"tag1"} +!12 = !{i32 7, !"Dwarf Version", i32 5} +!13 = !{i32 2, !"Debug Info Version", i32 3} +!14 = !{i32 1, !"wchar_size", i32 4} +!15 = !{i32 7, !"frame-pointer", i32 2} +!16 = !{!"clang, some version"} + +; CHECK: [1] TYPE_TAG 'tag1' type_id=9 +; CHECK: [2] TYPE_TAG 'tag1' type_id=8 +; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK: [4] TYPE_TAG 'tag1' type_id=3 +; CHECK: [5] VAR 'a' type_id=1, linkage=global +; CHECK: [6] VAR 'b' type_id=1, linkage=global +; CHECK: [7] DATASEC '.rodata' size=0 vlen=2 +; CHECK: type_id=5 offset=0 size=4 +; CHECK: type_id=6 offset=0 size=4 +; CHECK: [8] VOLATILE '(anon)' type_id=3 +; CHECK: [9] CONST '(anon)' type_id=8 diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse.ll @@ -0,0 +1,54 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; +; const volatile __tag1 int a; +; volatile int b; +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + + +@a = dso_local constant i32 0, align 4, !dbg !0 +@b = dso_local global i32 0, align 4, !dbg !5 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!14, !15, !16, !17} +!llvm.ident = !{!18} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 3, type: !9, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!4 = !{!0, !5} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 4, type: !7, isLocal: false, isDefinition: true) +!7 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !8) +!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !10) +!10 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !11) +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !12) +!12 = !{!13} +!13 = !{!"btf:type_tag", !"tag1"} +!14 = !{i32 7, !"Dwarf Version", i32 5} +!15 = !{i32 2, !"Debug Info Version", i32 3} +!16 = !{i32 1, !"wchar_size", i32 4} +!17 = !{i32 7, !"frame-pointer", i32 2} +!18 = !{!"clang, some version"} + +; CHECK: [1] TYPE_TAG 'tag1' type_id=10 +; CHECK: [2] TYPE_TAG 'tag1' type_id=6 +; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK: [4] TYPE_TAG 'tag1' type_id=3 +; CHECK: [5] VAR 'a' type_id=1, linkage=global +; CHECK: [6] VOLATILE '(anon)' type_id=3 +; CHECK: [7] VAR 'b' type_id=6, linkage=global +; CHECK: [8] DATASEC '.bss' size=0 vlen=1 +; CHECK: type_id=7 offset=0 size=4 +; CHECK: [9] DATASEC '.rodata' size=0 vlen=1 +; CHECK: type_id=5 offset=0 size=4 +; CHECK: [10] CONST '(anon)' type_id=6 diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-simple.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-simple.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-simple.ll @@ -0,0 +1,44 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; +; const volatile __tag1 int a; +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + +@a = dso_local constant i32 0, align 4, !dbg !0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!10, !11, !12, !13} +!llvm.ident = !{!14} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 3, type: !5, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!4 = !{!0} +!5 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !6) +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !8) +!8 = !{!9} +!9 = !{!"btf:type_tag", !"tag1"} +!10 = !{i32 7, !"Dwarf Version", i32 5} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{i32 7, !"frame-pointer", i32 2} +!14 = !{!"clang, some version"} + +; CHECK: [1] TYPE_TAG 'tag1' type_id=8 +; CHECK: [2] TYPE_TAG 'tag1' type_id=7 +; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK: [4] TYPE_TAG 'tag1' type_id=3 +; CHECK: [5] VAR 'a' type_id=1, linkage=global +; CHECK: [6] DATASEC '.rodata' size=0 vlen=1 +; CHECK: type_id=5 offset=0 size=4 +; CHECK: [7] VOLATILE '(anon)' type_id=3 +; CHECK: [8] CONST '(anon)' type_id=7 diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-decl-tag-typedef.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-decl-tag-typedef.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-decl-tag-typedef.ll @@ -0,0 +1,66 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; #define __dtag1 __attribute__((btf_decl_tag("dtag1"))) +; #define __dtag2 __attribute__((btf_decl_tag("dtag2"))) +; +; typedef int __tag1 __dtag1 foo; +; typedef foo __tag2 __dtag2 bar; +; struct buz { +; bar a; +; } g; +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + +; Verify that for both 'foo' and 'bar' btf_decl_tag applies to 'typedef' ID. + +%struct.buz = type { i32 } + +@g = dso_local global %struct.buz zeroinitializer, align 4, !dbg !0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!18, !19, !20, !21} +!llvm.ident = !{!22} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 10, type: !5, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!4 = !{!0} +!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "buz", file: !3, line: 8, size: 32, elements: !6) +!6 = !{!7} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !5, file: !3, line: 9, baseType: !8, size: 32) +!8 = !DIDerivedType(tag: DW_TAG_typedef, name: "bar", file: !3, line: 7, baseType: !9, annotations: !16) +!9 = !DIDerivedType(tag: DW_TAG_typedef, name: "foo", file: !3, line: 6, baseType: !10, annotations: !13) +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !11) +!11 = !{!12} +!12 = !{!"btf:type_tag", !"tag1"} +!13 = !{!14, !15} +!14 = !{!"btf:type_tag", !"tag2"} +!15 = !{!"btf_decl_tag", !"dtag1"} +!16 = !{!17} +!17 = !{!"btf_decl_tag", !"dtag2"} +!18 = !{i32 7, !"Dwarf Version", i32 5} +!19 = !{i32 2, !"Debug Info Version", i32 3} +!20 = !{i32 1, !"wchar_size", i32 4} +!21 = !{i32 7, !"frame-pointer", i32 2} +!22 = !{!"clang, some version"} + +; CHECK: [1] STRUCT 'buz' size=4 vlen=1 +; CHECK: 'a' type_id=2 bits_offset=0 +; CHECK: [2] TYPEDEF 'bar' type_id=5 +; CHECK: [3] DECL_TAG 'dtag2' type_id=2 component_idx=-1 +; CHECK: [4] TYPEDEF 'foo' type_id=8 +; CHECK: [5] TYPE_TAG 'tag2' type_id=4 +; CHECK: [6] DECL_TAG 'dtag1' type_id=4 component_idx=-1 +; CHECK: [7] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK: [8] TYPE_TAG 'tag1' type_id=7 +; CHECK: [9] VAR 'g' type_id=1, linkage=global +; CHECK: [10] DATASEC '.bss' size=0 vlen=1 +; CHECK: type_id=9 offset=0 size=4 diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-enum.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-enum.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-enum.ll @@ -0,0 +1,84 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; +; enum foo { FOO }; +; +; struct bar { +; enum foo __tag1 aa; +; enum foo __tag2 bb; +; enum foo cc; +; }; +; +; void root(struct bar *bar) {} +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + +; CHECK: [[[#]]] STRUCT 'bar' size=12 vlen=3 +; CHECK-NEXT: 'aa' type_id=[[#tag1:]] bits_offset=0 +; CHECK-NEXT: 'bb' type_id=[[#tag2:]] bits_offset=32 +; CHECK-NEXT: 'cc' type_id=[[#foo:]] bits_offset=64 +; CHECK-NEXT: [[[#foo]]] ENUM 'foo' encoding=UNSIGNED size=4 vlen=1 +; CHECK-NEXT: 'FOO' val=0 +; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=4 +; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=4 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @root(ptr noundef %bar) #0 !dbg !15 { +entry: + %bar.addr = alloca ptr, align 8 + store ptr %bar, ptr %bar.addr, align 8 + call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !31, metadata !DIExpression()), !dbg !32 + ret void, !dbg !33 +} + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +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 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!7, !8, !9, !10, !11, !12, !13} +!llvm.ident = !{!14} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git a90f030ce8ad757f5e5e122774569b00a5df0ae3)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!2 = !{!3} +!3 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "foo", file: !1, line: 4, baseType: !4, size: 32, elements: !5) +!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!5 = !{!6} +!6 = !DIEnumerator(name: "FOO", value: 0) +!7 = !{i32 7, !"Dwarf Version", i32 5} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 8, !"PIC Level", i32 2} +!11 = !{i32 7, !"PIE Level", i32 2} +!12 = !{i32 7, !"uwtable", i32 2} +!13 = !{i32 7, !"frame-pointer", i32 2} +!14 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git a90f030ce8ad757f5e5e122774569b00a5df0ae3)"} +!15 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !16, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !30) +!16 = !DISubroutineType(types: !17) +!17 = !{null, !18} +!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) +!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 6, size: 96, elements: !20) +!20 = !{!21, !25, !29} +!21 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !19, file: !1, line: 7, baseType: !22, size: 32) +!22 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "foo", file: !1, line: 4, baseType: !4, size: 32, elements: !5, annotations: !23) +!23 = !{!24} +!24 = !{!"btf:type_tag", !"tag1"} +!25 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !19, file: !1, line: 8, baseType: !26, size: 32, offset: 32) +!26 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "foo", file: !1, line: 4, baseType: !4, size: 32, elements: !5, annotations: !27) +!27 = !{!28} +!28 = !{!"btf:type_tag", !"tag2"} +!29 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !19, file: !1, line: 9, baseType: !3, size: 32, offset: 64) +!30 = !{} +!31 = !DILocalVariable(name: "bar", arg: 1, scope: !15, file: !1, line: 12, type: !18) +!32 = !DILocation(line: 12, column: 23, scope: !15) +!33 = !DILocation(line: 12, column: 29, scope: !15) diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-struct.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-struct.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-struct.ll @@ -0,0 +1,79 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; +; struct foo {}; +; +; struct bar { +; struct foo __tag1 aa; +; struct foo __tag2 bb; +; struct foo cc; +; }; +; +; void root(struct bar *bar) {} +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + +; CHECK: [[[#]]] STRUCT 'bar' size=0 vlen=3 +; CHECK-NEXT: 'aa' type_id=[[#tag1:]] bits_offset=0 +; CHECK-NEXT: 'bb' type_id=[[#tag2:]] bits_offset=0 +; CHECK-NEXT: 'cc' type_id=[[#foo:]] bits_offset=0 +; CHECK-NEXT: [[[#foo]]] STRUCT 'foo' size=0 vlen=0 +; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=[[#foo]] +; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=[[#foo]] + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @root(ptr noundef %bar) #0 !dbg !10 { +entry: + %bar.addr = alloca ptr, align 8 + store ptr %bar, ptr %bar.addr, align 8 + call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !27, metadata !DIExpression()), !dbg !28 + ret void, !dbg !29 +} + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +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 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git a90f030ce8ad757f5e5e122774569b00a5df0ae3)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"frame-pointer", i32 2} +!9 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git a90f030ce8ad757f5e5e122774569b00a5df0ae3)"} +!10 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !18) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 6, elements: !15) +!15 = !{!16, !21, !25} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !14, file: !1, line: 7, baseType: !17) +!17 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, elements: !18, annotations: !19) +!18 = !{} +!19 = !{!20} +!20 = !{!"btf:type_tag", !"tag1"} +!21 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !14, file: !1, line: 8, baseType: !22) +!22 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, elements: !18, annotations: !23) +!23 = !{!24} +!24 = !{!"btf:type_tag", !"tag2"} +!25 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !14, file: !1, line: 9, baseType: !26) +!26 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, elements: !18) +!27 = !DILocalVariable(name: "bar", arg: 1, scope: !10, file: !1, line: 12, type: !13) +!28 = !DILocation(line: 12, column: 23, scope: !10) +!29 = !DILocation(line: 12, column: 29, scope: !10) diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-subroutine.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-subroutine.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-subroutine.ll @@ -0,0 +1,105 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; #define __tag3 __attribute__((btf_type_tag("tag3"))) +; +; struct bar { +; void (__tag1 *aa)(int, int); +; void (__tag2 *bb)(int, int); +; void (*cc)(int, int); +; int (__tag3 *dd)(int, int); +; }; +; +; void root(struct bar *bar) {} +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + +; CHECK: [[[#]]] STRUCT 'bar' size=32 vlen=4 +; CHECK-NEXT: 'aa' type_id=[[#ptag1:]] bits_offset=0 +; CHECK-NEXT: 'bb' type_id=[[#ptag2:]] bits_offset=64 +; CHECK-NEXT: 'cc' type_id=[[#pfunc:]] bits_offset=128 +; CHECK-NEXT: 'dd' type_id=[[#ptag3:]] bits_offset=192 +; CHECK-NEXT: [[[#ptag1]]] PTR '(anon)' type_id=[[#tag1:]] +; CHECK-NEXT: [[[#func:]]] FUNC_PROTO '(anon)' ret_type_id=0 vlen=2 +; CHECK-NEXT: '(anon)' type_id=[[#int:]] +; CHECK-NEXT: '(anon)' type_id=[[#int]] +; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=[[#func]] +; CHECK-NEXT: [[[#int]]] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK-NEXT: [[[#ptag2]]] PTR '(anon)' type_id=[[#tag2:]] +; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=[[#func]] +; CHECK-NEXT: [[[#pfunc]]] PTR '(anon)' type_id=[[#func]] +; CHECK-NEXT: [[[#ptag3]]] PTR '(anon)' type_id=[[#tag3:]] +; CHECK-NEXT: [[[#func2:]]] FUNC_PROTO '(anon)' ret_type_id=[[#int]] vlen=2 +; CHECK-NEXT: '(anon)' type_id=[[#int]] +; CHECK-NEXT: '(anon)' type_id=[[#int]] +; CHECK-NEXT: [[[#tag3]]] TYPE_TAG 'tag1' type_id=[[#func2]] + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @root(ptr noundef %bar) #0 !dbg !10 { +entry: + %bar.addr = alloca ptr, align 8 + store ptr %bar, ptr %bar.addr, align 8 + call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !40, metadata !DIExpression()), !dbg !41 + ret void, !dbg !42 +} + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +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 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git a2924ede5b9d936900b4d6bf00ee3567ffadd483)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test2.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "9b641c74e61a0c077fbe39d41f932190") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"frame-pointer", i32 2} +!9 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git a2924ede5b9d936900b4d6bf00ee3567ffadd483)"} +!10 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !39) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 5, size: 256, elements: !15) +!15 = !{!16, !25, !32, !35} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !14, file: !1, line: 6, baseType: !17, size: 64) +!17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64, annotations: !23) +!18 = !DISubroutineType(types: !19, annotations: !21) +!19 = !{null, !20, !20} +!20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!21 = !{!22} +!22 = !{!"btf:type_tag", !"tag1"} +!23 = !{!24} +!24 = !{!"btf_type_tag", !"tag1"} +!25 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !14, file: !1, line: 7, baseType: !26, size: 64, offset: 64) +!26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64, annotations: !30) +!27 = !DISubroutineType(types: !19, annotations: !28) +!28 = !{!29} +!29 = !{!"btf:type_tag", !"tag2"} +!30 = !{!31} +!31 = !{!"btf_type_tag", !"tag2"} +!32 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !14, file: !1, line: 8, baseType: !33, size: 64, offset: 128) +!33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !34, size: 64) +!34 = !DISubroutineType(types: !19) +!35 = !DIDerivedType(tag: DW_TAG_member, name: "dd", scope: !14, file: !1, line: 9, baseType: !36, size: 64, offset: 192) +!36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64, annotations: !23) +!37 = !DISubroutineType(types: !38, annotations: !21) +!38 = !{!20, !20, !20} +!39 = !{} +!40 = !DILocalVariable(name: "bar", arg: 1, scope: !10, file: !1, line: 12, type: !13) +!41 = !DILocation(line: 12, column: 23, scope: !10) +!42 = !DILocation(line: 12, column: 29, scope: !10) diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-typedef.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-typedef.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-typedef.ll @@ -0,0 +1,81 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; +; typedef int foo; +; +; struct bar { +; foo __tag1 aa; +; foo __tag2 bb; +; foo cc; +; }; +; +; void root(struct bar *bar) {} +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + +; CHECK: [[[#]]] STRUCT 'bar' size=12 vlen=3 +; CHECK-NEXT: 'aa' type_id=[[#tag1:]] bits_offset=0 +; CHECK-NEXT: 'bb' type_id=[[#tag2:]] bits_offset=32 +; CHECK-NEXT: 'cc' type_id=[[#foo:]] bits_offset=64 +; CHECK-NEXT: [[[#foo]]] TYPEDEF 'foo' type_id=6 +; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=[[#foo]] +; CHECK-NEXT: [[[#]]] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=[[#foo]] + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @root(ptr noundef %bar) #0 !dbg !10 { +entry: + %bar.addr = alloca ptr, align 8 + store ptr %bar, ptr %bar.addr, align 8 + call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !28, metadata !DIExpression()), !dbg !29 + ret void, !dbg !30 +} + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +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 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git a90f030ce8ad757f5e5e122774569b00a5df0ae3)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"frame-pointer", i32 2} +!9 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git a90f030ce8ad757f5e5e122774569b00a5df0ae3)"} +!10 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !27) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 6, size: 96, elements: !15) +!15 = !{!16, !21, !25} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !14, file: !1, line: 7, baseType: !17, size: 32) +!17 = !DIDerivedType(tag: DW_TAG_typedef, name: "foo", file: !1, line: 4, baseType: !18, annotations: !19) +!18 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!19 = !{!20} +!20 = !{!"btf:type_tag", !"tag1"} +!21 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !14, file: !1, line: 8, baseType: !22, size: 32, offset: 32) +!22 = !DIDerivedType(tag: DW_TAG_typedef, name: "foo", file: !1, line: 4, baseType: !18, annotations: !23) +!23 = !{!24} +!24 = !{!"btf:type_tag", !"tag2"} +!25 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !14, file: !1, line: 9, baseType: !26, size: 32, offset: 64) +!26 = !DIDerivedType(tag: DW_TAG_typedef, name: "foo", file: !1, line: 4, baseType: !18) +!27 = !{} +!28 = !DILocalVariable(name: "bar", arg: 1, scope: !10, file: !1, line: 12, type: !13) +!29 = !DILocation(line: 12, column: 23, scope: !10) +!30 = !DILocation(line: 12, column: 29, scope: !10) diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-union.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-union.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-union.ll @@ -0,0 +1,79 @@ +; RUN: llc -march=bpfel -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; RUN: llc -march=bpfeb -filetype=obj -o - %s \ +; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; +; union foo {}; +; +; struct bar { +; union foo __tag1 aa; +; union foo __tag2 bb; +; union foo cc; +; }; +; +; void root(struct bar *bar) {} +; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + +; CHECK: [[[#]]] STRUCT 'bar' size=0 vlen=3 +; CHECK-NEXT: 'aa' type_id=[[#tag1:]] bits_offset=0 +; CHECK-NEXT: 'bb' type_id=[[#tag2:]] bits_offset=0 +; CHECK-NEXT: 'cc' type_id=[[#foo:]] bits_offset=0 +; CHECK-NEXT: [[[#foo]]] UNION 'foo' size=0 vlen=0 +; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=[[#foo]] +; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=[[#foo]] + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @root(ptr noundef %bar) #0 !dbg !10 { +entry: + %bar.addr = alloca ptr, align 8 + store ptr %bar, ptr %bar.addr, align 8 + call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !27, metadata !DIExpression()), !dbg !28 + ret void, !dbg !29 +} + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +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 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git a90f030ce8ad757f5e5e122774569b00a5df0ae3)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"frame-pointer", i32 2} +!9 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git a90f030ce8ad757f5e5e122774569b00a5df0ae3)"} +!10 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !18) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 6, elements: !15) +!15 = !{!16, !21, !25} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !14, file: !1, line: 7, baseType: !17) +!17 = !DICompositeType(tag: DW_TAG_union_type, name: "foo", file: !1, line: 4, elements: !18, annotations: !19) +!18 = !{} +!19 = !{!20} +!20 = !{!"btf:type_tag", !"tag1"} +!21 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !14, file: !1, line: 8, baseType: !22) +!22 = !DICompositeType(tag: DW_TAG_union_type, name: "foo", file: !1, line: 4, elements: !18, annotations: !23) +!23 = !{!24} +!24 = !{!"btf:type_tag", !"tag2"} +!25 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !14, file: !1, line: 9, baseType: !26) +!26 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "foo", file: !1, line: 4, elements: !18) +!27 = !DILocalVariable(name: "bar", arg: 1, scope: !10, file: !1, line: 12, type: !13) +!28 = !DILocation(line: 12, column: 23, scope: !10) +!29 = !DILocation(line: 12, column: 29, scope: !10) diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-field-relo.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-field-relo.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-field-relo.ll @@ -0,0 +1,134 @@ +; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck %s +; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck %s +; +; Source: +; +; #define __pai __attribute__((preserve_access_index)); +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; +; struct alpha { +; int zulu; +; } __pai; +; +; struct bravo { +; struct alpha __tag1 *yankee; +; } __pai; +; +; int func(struct bravo *xray) { +; return xray->yankee->zulu; +; } +; +; Compilation command: +; +; cat test.c | clang -x c -target bpf -O2 -g -emit-llvm -S - -o - +; +; The relocation entry for zulu should point to STRUCT 'alpha', +; not TYPE_TAG 'tag1' -> STRUCT 'alpha'. + +@"llvm.alpha:0:0$0:0" = external global i64, !llvm.preserve.access.index !0 #0 +@"llvm.bravo:0:0$0:0" = external global i64, !llvm.preserve.access.index !8 #0 + +; Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none) +define dso_local i32 @func(ptr noundef readonly %xray) local_unnamed_addr #1 !dbg !22 { +entry: + call void @llvm.dbg.value(metadata ptr %xray, metadata !27, metadata !DIExpression()), !dbg !28 + %0 = load i64, ptr @"llvm.bravo:0:0$0:0", align 8 + %1 = getelementptr i8, ptr %xray, i64 %0 + %2 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 1, ptr %1) + %3 = load ptr, ptr %2, align 8, !dbg !29, !tbaa !30 + %4 = load i64, ptr @"llvm.alpha:0:0$0:0", align 8 + %5 = getelementptr i8, ptr %3, i64 %4 + %6 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 0, ptr %5) + %7 = load i32, ptr %6, align 4, !dbg !35, !tbaa !36 + ret i32 %7, !dbg !39 +} + +; Function Attrs: nofree nosync nounwind memory(none) +declare ptr @llvm.bpf.passthrough.p0.p0(i32, ptr) #2 + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare void @llvm.dbg.value(metadata, metadata, metadata) #3 + +attributes #0 = { "btf_ama" } +attributes #1 = { nofree nosync nounwind memory(read, inaccessiblemem: none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #2 = { nofree nosync nounwind memory(none) } +attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } + +!llvm.dbg.cu = !{!14} +!llvm.module.flags = !{!17, !18, !19, !20} +!llvm.ident = !{!21} + +!0 = !DICompositeType(tag: DW_TAG_structure_type, name: "alpha", file: !1, line: 4, size: 32, elements: !2, annotations: !6) +!1 = !DIFile(filename: "", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "89810ba04b039111ea709cb54ed653bc") +!2 = !{!3} +!3 = !DIDerivedType(tag: DW_TAG_member, name: "zulu", scope: !4, file: !1, line: 5, baseType: !5, size: 32) +!4 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "alpha", file: !1, line: 4, size: 32, elements: !2) +!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!6 = !{!7} +!7 = !{!"btf:type_tag", !"tag1"} +!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bravo", file: !1, line: 8, size: 64, elements: !9) +!9 = !{!10} +!10 = !DIDerivedType(tag: DW_TAG_member, name: "yankee", scope: !8, file: !1, line: 9, baseType: !11, size: 64) +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !0, size: 64, annotations: !12) +!12 = !{!13} +!13 = !{!"btf_type_tag", !"tag1"} +!14 = distinct !DICompileUnit(language: DW_LANG_C11, file: !15, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git 034dbeecee00be5c47fc265333c6d58bb4801240)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !16, splitDebugInlining: false, nameTableKind: None) +!15 = !DIFile(filename: "-", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "89810ba04b039111ea709cb54ed653bc") +!16 = !{!0} +!17 = !{i32 7, !"Dwarf Version", i32 5} +!18 = !{i32 2, !"Debug Info Version", i32 3} +!19 = !{i32 1, !"wchar_size", i32 4} +!20 = !{i32 7, !"frame-pointer", i32 2} +!21 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git 034dbeecee00be5c47fc265333c6d58bb4801240)"} +!22 = distinct !DISubprogram(name: "func", scope: !1, file: !1, line: 12, type: !23, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !14, retainedNodes: !26) +!23 = !DISubroutineType(types: !24) +!24 = !{!5, !25} +!25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) +!26 = !{!27} +!27 = !DILocalVariable(name: "xray", arg: 1, scope: !22, file: !1, line: 12, type: !25) +!28 = !DILocation(line: 0, scope: !22) +!29 = !DILocation(line: 13, column: 16, scope: !22) +!30 = !{!31, !32, i64 0} +!31 = !{!"bravo", !32, i64 0} +!32 = !{!"any pointer", !33, i64 0} +!33 = !{!"omnipotent char", !34, i64 0} +!34 = !{!"Simple C/C++ TBAA"} +!35 = !DILocation(line: 13, column: 24, scope: !22) +!36 = !{!37, !38, i64 0} +!37 = !{!"alpha", !38, i64 0} +!38 = !{!"int", !33, i64 0} +!39 = !DILocation(line: 13, column: 3, scope: !22) + +; CHECK: [[L1:.Ltmp[0-9]+]]: +; CHECK: r1 = *(u64 *)(r1 + 0) +; CHECK: r0 = *(u32 *)(r1 + 0) + +; CHECK: .long 1 # BTF_KIND_STRUCT(id = 2) +; CHECK: .long 66 # BTF_KIND_STRUCT(id = 7) +; CHECK: .long 77 # BTF_KIND_TYPE_TAG(id = 8) +; +; CHECK: .ascii "bravo" # string offset=1 +; CHECK: .ascii "0:0" # string offset=34 +; CHECK: .ascii "alpha" # string offset=66 + +; CHECK: .long 16 # FieldReloc +; CHECK-NEXT: .long 28 # Field reloc section string offset=28 +; CHECK-NEXT: .long 2 +; +; Step #1: xray->yankee +; CHECK-NEXT: .long [[L1]] +; CHECK-NEXT: .long 2 +; ^^^ id 2 -> STRUCT 'bravo' +; CHECK-NEXT: .long 34 +; ^^^^ relocation spec '0:0' +; CHECK-NEXT: .long 0 +; +; Step #2: yankee->zulu +; CHECK-NEXT: .long .Ltmp[[#]] +; CHECK-NEXT: .long 7 +; ^^^ id 7 -> STRUCT 'alpha' +; The gist of the test-case is to check that +; it is not id 8, corresponding to TYPE_TAG. +; CHECK-NEXT: .long 34 +; ^^^^ relocation spec '0:0' +; CHECK-NEXT: .long 0 diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd.ll --- a/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd.ll +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd.ll @@ -18,81 +18,78 @@ ; Compilation flag: ; clang -target bpf -O2 -g -S -emit-llvm test.c -%struct.map_value = type { %struct.foo* } -%struct.foo = type opaque +%struct.map_value = type { ptr } ; Function Attrs: nounwind define dso_local void @test() local_unnamed_addr #0 !dbg !7 { entry: %v = alloca %struct.map_value, align 8 - %0 = bitcast %struct.map_value* %v to i8*, !dbg !20 - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) #4, !dbg !20 - call void @llvm.dbg.declare(metadata %struct.map_value* %v, metadata !11, metadata !DIExpression()), !dbg !21 - %1 = bitcast %struct.map_value* %v to i64*, !dbg !21 - store i64 0, i64* %1, align 8, !dbg !21 - call void @func(%struct.map_value* noundef nonnull %v) #4, !dbg !22 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) #4, !dbg !23 - ret void, !dbg !23 + call void @llvm.lifetime.start.p0(i64 8, ptr nonnull %v) #4, !dbg !23 + call void @llvm.dbg.declare(metadata ptr %v, metadata !11, metadata !DIExpression()), !dbg !24 + store i64 0, ptr %v, align 8, !dbg !24 + call void @func(ptr noundef nonnull %v) #4, !dbg !25 + call void @llvm.lifetime.end.p0(i64 8, ptr nonnull %v) #4, !dbg !26 + ret void, !dbg !26 } -; CHECK: .long 0 # BTF_KIND_FUNC_PROTO(id = 1) -; CHECK-NEXT: .long 218103808 # 0xd000000 -; CHECK-NEXT: .long 0 -; CHECK-NEXT: .long 1 # BTF_KIND_FUNC(id = 2) -; CHECK-NEXT: .long 201326593 # 0xc000001 -; CHECK-NEXT: .long 1 -; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 3) -; CHECK-NEXT: .long 218103809 # 0xd000001 -; CHECK-NEXT: .long 0 -; CHECK-NEXT: .long 0 -; CHECK-NEXT: .long 4 -; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 4) -; CHECK-NEXT: .long 33554432 # 0x2000000 -; CHECK-NEXT: .long 5 -; CHECK-NEXT: .long 62 # BTF_KIND_STRUCT(id = 5) -; CHECK-NEXT: .long 67108865 # 0x4000001 -; CHECK-NEXT: .long 8 -; CHECK-NEXT: .long 72 -; CHECK-NEXT: .long 8 -; CHECK-NEXT: .long 0 # 0x0 -; CHECK-NEXT: .long 76 # BTF_KIND_TYPE_TAG(id = 6) -; CHECK-NEXT: .long 301989888 # 0x12000000 -; CHECK-NEXT: .long 9 -; CHECK-NEXT: .long 81 # BTF_KIND_TYPE_TAG(id = 7) -; CHECK-NEXT: .long 301989888 # 0x12000000 -; CHECK-NEXT: .long 6 -; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 8) -; CHECK-NEXT: .long 33554432 # 0x2000000 -; CHECK-NEXT: .long 7 -; CHECK-NEXT: .long 86 # BTF_KIND_FWD(id = 9) -; CHECK-NEXT: .long 117440512 # 0x7000000 -; CHECK-NEXT: .long 0 -; CHECK-NEXT: .long 90 # BTF_KIND_FUNC(id = 10) -; CHECK-NEXT: .long 201326594 # 0xc000002 -; CHECK-NEXT: .long 3 +; CHECK: .long 0 # BTF_KIND_FUNC_PROTO(id = 1) +; CHECK-NEXT: .long 218103808 # 0xd000000 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 # BTF_KIND_FUNC(id = 2) +; CHECK-NEXT: .long 201326593 # 0xc000001 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 3) +; CHECK-NEXT: .long 218103809 # 0xd000001 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 4) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 5 +; CHECK-NEXT: .long 35 # BTF_KIND_STRUCT(id = 5) +; CHECK-NEXT: .long 67108865 # 0x4000001 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 45 +; CHECK-NEXT: .long 6 +; CHECK-NEXT: .long 0 # 0x0 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 6) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 9 +; CHECK-NEXT: .long 49 # BTF_KIND_FWD(id = 7) +; CHECK-NEXT: .long 117440512 # 0x7000000 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 53 # BTF_KIND_TYPE_TAG(id = 8) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 7 +; CHECK-NEXT: .long 58 # BTF_KIND_TYPE_TAG(id = 9) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 63 # BTF_KIND_FUNC(id = 10) +; CHECK-NEXT: .long 201326594 # 0xc000002 +; CHECK-NEXT: .long 3 -; CHECK: .ascii "test" # string offset=1 -; CHECK: .ascii "map_value" # string offset=62 -; CHECK: .ascii "ptr" # string offset=72 -; CHECK: .ascii "tag2" # string offset=76 -; CHECK: .ascii "tag1" # string offset=81 -; CHECK: .ascii "foo" # string offset=86 -; CHECK: .ascii "func" # string offset=90 +; CHECK: .ascii "test" # string offset=1 +; CHECK: .ascii "map_value" # string offset=35 +; CHECK: .ascii "ptr" # string offset=45 +; CHECK: .ascii "foo" # string offset=49 +; CHECK: .ascii "tag2" # string offset=53 +; CHECK: .ascii "tag1" # string offset=58 +; CHECK: .ascii "func" # string offset=63 -; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1 -; Function Attrs: mustprogress nofree nosync nounwind readnone speculatable willreturn +; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 -declare !dbg !24 dso_local void @func(%struct.map_value* noundef) local_unnamed_addr #3 +declare !dbg !27 dso_local void @func(ptr noundef) local_unnamed_addr #3 -; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1 -attributes #0 = { nounwind "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } -attributes #1 = { argmemonly mustprogress nofree nosync nounwind willreturn } -attributes #2 = { mustprogress nofree nosync nounwind readnone speculatable willreturn } +attributes #0 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #2 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #3 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } attributes #4 = { nounwind } @@ -100,13 +97,13 @@ !llvm.module.flags = !{!2, !3, !4, !5} !llvm.ident = !{!6} -!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 15.0.0 (https://github.com/llvm/llvm-project.git 25e8505f515bc9ef6c13527ffc4a902bae3a9071)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) -!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm/btf_tag_type", checksumkind: CSK_MD5, checksum: "7735a89e98603fee29d352a8e9db5acb") +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git 5aa6dd1e09616a455377f3066d2034d3e8a073ba)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/") !2 = !{i32 7, !"Dwarf Version", i32 5} !3 = !{i32 2, !"Debug Info Version", i32 3} !4 = !{i32 1, !"wchar_size", i32 4} !5 = !{i32 7, !"frame-pointer", i32 2} -!6 = !{!"clang version 15.0.0 (https://github.com/llvm/llvm-project.git 25e8505f515bc9ef6c13527ffc4a902bae3a9071)"} +!6 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git 5aa6dd1e09616a455377f3066d2034d3e8a073ba)"} !7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 9, type: !8, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) !8 = !DISubroutineType(types: !9) !9 = !{null} @@ -115,17 +112,21 @@ !12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_value", file: !1, line: 5, size: 64, elements: !13) !13 = !{!14} !14 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !12, file: !1, line: 6, baseType: !15, size: 64) -!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64, annotations: !17) -!16 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, flags: DIFlagFwdDecl) +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64, annotations: !20) +!16 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, flags: DIFlagFwdDecl, annotations: !17) !17 = !{!18, !19} -!18 = !{!"btf_type_tag", !"tag2"} -!19 = !{!"btf_type_tag", !"tag1"} -!20 = !DILocation(line: 11, column: 9, scope: !7) -!21 = !DILocation(line: 11, column: 26, scope: !7) -!22 = !DILocation(line: 12, column: 9, scope: !7) -!23 = !DILocation(line: 13, column: 1, scope: !7) -!24 = !DISubprogram(name: "func", scope: !1, file: !1, line: 8, type: !25, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !28) -!25 = !DISubroutineType(types: !26) -!26 = !{null, !27} -!27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) -!28 = !{} +!18 = !{!"btf:type_tag", !"tag2"} +!19 = !{!"btf:type_tag", !"tag1"} +!20 = !{!21, !22} +!21 = !{!"btf_type_tag", !"tag2"} +!22 = !{!"btf_type_tag", !"tag1"} +!23 = !DILocation(line: 11, column: 9, scope: !7) +!24 = !DILocation(line: 11, column: 26, scope: !7) +!25 = !DILocation(line: 12, column: 9, scope: !7) +!26 = !DILocation(line: 13, column: 1, scope: !7) +!27 = !DISubprogram(name: "func", scope: !1, file: !1, line: 8, type: !28, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !31) +!28 = !DISubroutineType(types: !29) +!29 = !{null, !30} +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!31 = !{!32} +!32 = !DILocalVariable(arg: 1, scope: !27, file: !1, line: 8, type: !30) diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved.ll --- a/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved.ll +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved.ll @@ -20,95 +20,92 @@ ; Compilation flag: ; clang -target bpf -O2 -g -S -emit-llvm test.c -%struct.map_value = type { %struct.foo* } -%struct.foo = type { i32 } +%struct.map_value = type { ptr } ; Function Attrs: nounwind define dso_local void @test() local_unnamed_addr #0 !dbg !7 { entry: %v = alloca %struct.map_value, align 8 - %0 = bitcast %struct.map_value* %v to i8*, !dbg !23 - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) #4, !dbg !23 - call void @llvm.dbg.declare(metadata %struct.map_value* %v, metadata !11, metadata !DIExpression()), !dbg !24 - %1 = bitcast %struct.map_value* %v to i64*, !dbg !24 - store i64 0, i64* %1, align 8, !dbg !24 - call void @func(%struct.map_value* noundef nonnull %v, %struct.foo* noundef null) #4, !dbg !25 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) #4, !dbg !26 - ret void, !dbg !26 + call void @llvm.lifetime.start.p0(i64 8, ptr nonnull %v) #4, !dbg !27 + call void @llvm.dbg.declare(metadata ptr %v, metadata !11, metadata !DIExpression()), !dbg !28 + store i64 0, ptr %v, align 8, !dbg !28 + call void @func(ptr noundef nonnull %v, ptr noundef null) #4, !dbg !29 + call void @llvm.lifetime.end.p0(i64 8, ptr nonnull %v) #4, !dbg !30 + ret void, !dbg !30 } -; CHECK: .long 0 # BTF_KIND_FUNC_PROTO(id = 1) -; CHECK-NEXT: .long 218103808 # 0xd000000 -; CHECK-NEXT: .long 0 -; CHECK-NEXT: .long 1 # BTF_KIND_FUNC(id = 2) -; CHECK-NEXT: .long 201326593 # 0xc000001 -; CHECK-NEXT: .long 1 -; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 3) -; CHECK-NEXT: .long 218103810 # 0xd000002 -; CHECK-NEXT: .long 0 -; CHECK-NEXT: .long 0 -; CHECK-NEXT: .long 4 -; CHECK-NEXT: .long 0 -; CHECK-NEXT: .long 7 -; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 4) -; CHECK-NEXT: .long 33554432 # 0x2000000 -; CHECK-NEXT: .long 5 -; CHECK-NEXT: .long 63 # BTF_KIND_STRUCT(id = 5) -; CHECK-NEXT: .long 67108865 # 0x4000001 -; CHECK-NEXT: .long 8 -; CHECK-NEXT: .long 73 -; CHECK-NEXT: .long 6 -; CHECK-NEXT: .long 0 # 0x0 -; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 6) -; CHECK-NEXT: .long 33554432 # 0x2000000 -; CHECK-NEXT: .long 12 -; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 7) -; CHECK-NEXT: .long 33554432 # 0x2000000 -; CHECK-NEXT: .long 8 -; CHECK-NEXT: .long 77 # BTF_KIND_STRUCT(id = 8) -; CHECK-NEXT: .long 67108865 # 0x4000001 -; CHECK-NEXT: .long 4 -; CHECK-NEXT: .long 81 -; CHECK-NEXT: .long 9 -; CHECK-NEXT: .long 0 # 0x0 -; CHECK-NEXT: .long 83 # BTF_KIND_INT(id = 9) -; CHECK-NEXT: .long 16777216 # 0x1000000 -; CHECK-NEXT: .long 4 -; CHECK-NEXT: .long 16777248 # 0x1000020 -; CHECK-NEXT: .long 87 # BTF_KIND_FUNC(id = 10) -; CHECK-NEXT: .long 201326594 # 0xc000002 -; CHECK-NEXT: .long 3 -; CHECK-NEXT: .long 92 # BTF_KIND_TYPE_TAG(id = 11) -; CHECK-NEXT: .long 301989888 # 0x12000000 -; CHECK-NEXT: .long 8 -; CHECK-NEXT: .long 97 # BTF_KIND_TYPE_TAG(id = 12) -; CHECK-NEXT: .long 301989888 # 0x12000000 -; CHECK-NEXT: .long 11 +; CHECK: .long 0 # BTF_KIND_FUNC_PROTO(id = 1) +; CHECK-NEXT: .long 218103808 # 0xd000000 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 # BTF_KIND_FUNC(id = 2) +; CHECK-NEXT: .long 201326593 # 0xc000001 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 3) +; CHECK-NEXT: .long 218103810 # 0xd000002 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 7 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 4) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 5 +; CHECK-NEXT: .long 35 # BTF_KIND_STRUCT(id = 5) +; CHECK-NEXT: .long 67108865 # 0x4000001 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 45 +; CHECK-NEXT: .long 6 +; CHECK-NEXT: .long 0 # 0x0 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 6) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 12 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 7) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 49 # BTF_KIND_STRUCT(id = 8) +; CHECK-NEXT: .long 67108865 # 0x4000001 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 53 +; CHECK-NEXT: .long 9 +; CHECK-NEXT: .long 0 # 0x0 +; CHECK-NEXT: .long 55 # BTF_KIND_INT(id = 9) +; CHECK-NEXT: .long 16777216 # 0x1000000 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 16777248 # 0x1000020 +; CHECK-NEXT: .long 59 # BTF_KIND_FUNC(id = 10) +; CHECK-NEXT: .long 201326594 # 0xc000002 +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .long 64 # BTF_KIND_TYPE_TAG(id = 11) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 69 # BTF_KIND_TYPE_TAG(id = 12) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 11 -; CHECK: .ascii "test" # string offset=1 -; CHECK: .ascii "map_value" # string offset=63 -; CHECK: .ascii "ptr" # string offset=73 -; CHECK: .ascii "foo" # string offset=77 -; CHECK: .byte 105 # string offset=81 -; CHECK: .ascii "int" # string offset=83 -; CHECK: .ascii "func" # string offset=87 -; CHECK: .ascii "tag2" # string offset=92 -; CHECK: .ascii "tag1" # string offset=97 +; CHECK: .ascii "test" # string offset=1 +; CHECK: .ascii "map_value" # string offset=35 +; CHECK: .ascii "ptr" # string offset=45 +; CHECK: .ascii "foo" # string offset=49 +; CHECK: .byte 105 # string offset=53 +; CHECK: .ascii "int" # string offset=55 +; CHECK: .ascii "func" # string offset=59 +; CHECK: .ascii "tag2" # string offset=64 +; CHECK: .ascii "tag1" # string offset=69 -; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1 -; Function Attrs: mustprogress nofree nosync nounwind readnone speculatable willreturn +; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 -declare !dbg !27 dso_local void @func(%struct.map_value* noundef, %struct.foo* noundef) local_unnamed_addr #3 +declare !dbg !31 dso_local void @func(ptr noundef, ptr noundef) local_unnamed_addr #3 -; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1 -attributes #0 = { nounwind "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } -attributes #1 = { argmemonly mustprogress nofree nosync nounwind willreturn } -attributes #2 = { mustprogress nofree nosync nounwind readnone speculatable willreturn } +attributes #0 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #2 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #3 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } attributes #4 = { nounwind } @@ -116,13 +113,13 @@ !llvm.module.flags = !{!2, !3, !4, !5} !llvm.ident = !{!6} -!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 15.0.0 (https://github.com/llvm/llvm-project.git 25e8505f515bc9ef6c13527ffc4a902bae3a9071)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) -!1 = !DIFile(filename: "test.c", directory: "/tmp//home/yhs/work/tests/llvm/btf_tag_type", checksumkind: CSK_MD5, checksum: "8b3b8281c3b4240403467e0c9461251d") +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git 5aa6dd1e09616a455377f3066d2034d3e8a073ba)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/") !2 = !{i32 7, !"Dwarf Version", i32 5} !3 = !{i32 2, !"Debug Info Version", i32 3} !4 = !{i32 1, !"wchar_size", i32 4} !5 = !{i32 7, !"frame-pointer", i32 2} -!6 = !{!"clang version 15.0.0 (https://github.com/llvm/llvm-project.git 25e8505f515bc9ef6c13527ffc4a902bae3a9071)"} +!6 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git 5aa6dd1e09616a455377f3066d2034d3e8a073ba)"} !7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 11, type: !8, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) !8 = !DISubroutineType(types: !9) !9 = !{null} @@ -131,21 +128,27 @@ !12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_value", file: !1, line: 7, size: 64, elements: !13) !13 = !{!14} !14 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !12, file: !1, line: 8, baseType: !15, size: 64) -!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64, annotations: !20) -!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, size: 32, elements: !17) +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64, annotations: !24) +!16 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, size: 32, elements: !17, annotations: !21) !17 = !{!18} -!18 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !16, file: !1, line: 5, baseType: !19, size: 32) -!19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) -!20 = !{!21, !22} -!21 = !{!"btf_type_tag", !"tag2"} -!22 = !{!"btf_type_tag", !"tag1"} -!23 = !DILocation(line: 13, column: 9, scope: !7) -!24 = !DILocation(line: 13, column: 26, scope: !7) -!25 = !DILocation(line: 14, column: 9, scope: !7) -!26 = !DILocation(line: 15, column: 1, scope: !7) -!27 = !DISubprogram(name: "func", scope: !1, file: !1, line: 10, type: !28, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !32) -!28 = !DISubroutineType(types: !29) -!29 = !{null, !30, !31} -!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) -!31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64) -!32 = !{} +!18 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !1, line: 5, baseType: !20, size: 32) +!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, size: 32, elements: !17) +!20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!21 = !{!22, !23} +!22 = !{!"btf:type_tag", !"tag2"} +!23 = !{!"btf:type_tag", !"tag1"} +!24 = !{!25, !26} +!25 = !{!"btf_type_tag", !"tag2"} +!26 = !{!"btf_type_tag", !"tag1"} +!27 = !DILocation(line: 13, column: 9, scope: !7) +!28 = !DILocation(line: 13, column: 26, scope: !7) +!29 = !DILocation(line: 14, column: 9, scope: !7) +!30 = !DILocation(line: 15, column: 1, scope: !7) +!31 = !DISubprogram(name: "func", scope: !1, file: !1, line: 10, type: !32, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !36) +!32 = !DISubroutineType(types: !33) +!33 = !{null, !34, !35} +!34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) +!36 = !{!37, !38} +!37 = !DILocalVariable(arg: 1, scope: !31, file: !1, line: 10, type: !34) +!38 = !DILocalVariable(arg: 2, scope: !31, file: !1, line: 10, type: !35) diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-skip-var.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-skip-var.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-skip-var.ll @@ -0,0 +1,74 @@ +; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; int __tag1 *aa; +; int __tag1 bb; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll +; +; BTF VAR entry for `b` should be stripped of type tag. + +@aa = dso_local global ptr null, align 8, !dbg !0 +@bb = dso_local global i32 0, align 4, !dbg !5 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!13, !14, !15, !16, !17, !18, !19} +!llvm.ident = !{!20} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "aa", scope: !2, file: !3, line: 3, type: !10, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git 034dbeecee00be5c47fc265333c6d58bb4801240)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.c", directory: "/some/dir", checksumkind: CSK_MD5, checksum: "6abf72a79fc44e271d8676233a1f5ef5") +!4 = !{!0, !5} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "bb", scope: !2, file: !3, line: 4, type: !7, isLocal: false, isDefinition: true) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !8) +!8 = !{!9} +!9 = !{!"btf:type_tag", !"tag1"} +!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64, annotations: !11) +!11 = !{!12} +!12 = !{!"btf_type_tag", !"tag1"} +!13 = !{i32 7, !"Dwarf Version", i32 5} +!14 = !{i32 2, !"Debug Info Version", i32 3} +!15 = !{i32 1, !"wchar_size", i32 4} +!16 = !{i32 8, !"PIC Level", i32 2} +!17 = !{i32 7, !"PIE Level", i32 2} +!18 = !{i32 7, !"uwtable", i32 2} +!19 = !{i32 7, !"frame-pointer", i32 2} +!20 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git 034dbeecee00be5c47fc265333c6d58bb4801240)"} + +; CHECK: .long 0 # BTF_KIND_PTR(id = 1) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 3 +; +; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2) +; CHECK-NEXT: .long 16777216 # 0x1000000 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 16777248 # 0x1000020 +; +; CHECK-NEXT: .long 5 # BTF_KIND_TYPE_TAG(id = 3) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 2 +; +; Variable 'aa': +; - name_off 10 corresponds to string "aa" +; - type 1 corresponds to int __tag1* +; CHECK-NEXT: .long 10 # BTF_KIND_VAR(id = 4) +; CHECK-NEXT: .long 234881024 # 0xe000000 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 1 +; +; Variable 'bb': +; - name_off 13 corresponds to string "bb" +; - type 2 corresponds to int, type tag is not referenced +; CHECK-NEXT: .long 13 # BTF_KIND_VAR(id = 5) +; CHECK-NEXT: .long 234881024 # 0xe000000 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long 1 + +; CHECK: .ascii "int" # string offset=1 +; CHECK: .ascii "tag1" # string offset=5 +; CHECK: .ascii "aa" # string offset=10 +; CHECK: .ascii "bb" # string offset=13 diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-var.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-var.ll --- a/llvm/test/CodeGen/BPF/BTF/type-tag-var.ll +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-var.ll @@ -11,53 +11,62 @@ @g = dso_local local_unnamed_addr global ptr null, align 8, !dbg !0 !llvm.dbg.cu = !{!2} -!llvm.module.flags = !{!12, !13, !14, !15} -!llvm.ident = !{!16} +!llvm.module.flags = !{!15, !16, !17, !18} +!llvm.ident = !{!19} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 3, type: !5, isLocal: false, isDefinition: true) -!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 14.0.0 (https://github.com/llvm/llvm-project.git 077b2e0cf1e97c4d97ca5ceab3ec0192ed11c66e)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) -!3 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm/btf_tag_type") +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git 5aa6dd1e09616a455377f3066d2034d3e8a073ba)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "9ae91fe3dd8e44985841816e35923786") !4 = !{!0} -!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64, annotations: !10) -!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64, annotations: !8) -!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64, annotations: !13) +!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64, annotations: !10) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !8) !8 = !{!9} -!9 = !{!"btf_type_tag", !"tag1"} -!10 = !{!9, !11} -!11 = !{!"btf_type_tag", !"tag2"} +!9 = !{!"btf:type_tag", !"tag1"} +!10 = !{!9, !11, !12} +!11 = !{!"btf:type_tag", !"tag2"} +!12 = !{!"btf_type_tag", !"tag1"} +!13 = !{!12, !14} +!14 = !{!"btf_type_tag", !"tag2"} -; CHECK: .long 1 # BTF_KIND_TYPE_TAG(id = 1) -; CHECK-NEXT: .long 301989888 # 0x12000000 -; CHECK-NEXT: .long 5 -; CHECK-NEXT: .long 6 # BTF_KIND_TYPE_TAG(id = 2) -; CHECK-NEXT: .long 301989888 # 0x12000000 -; CHECK-NEXT: .long 1 -; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 3) +; CHECK: .long 0 # BTF_KIND_PTR(id = 1) ; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 2) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 6 +; CHECK-NEXT: .long 1 # BTF_KIND_TYPE_TAG(id = 3) +; CHECK-NEXT: .long 301989888 # 0x12000000 ; CHECK-NEXT: .long 2 -; CHECK-NEXT: .long 1 # BTF_KIND_TYPE_TAG(id = 4) +; CHECK-NEXT: .long 6 # BTF_KIND_TYPE_TAG(id = 4) ; CHECK-NEXT: .long 301989888 # 0x12000000 -; CHECK-NEXT: .long 6 -; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 5) -; CHECK-NEXT: .long 33554432 # 0x2000000 -; CHECK-NEXT: .long 4 -; CHECK-NEXT: .long 11 # BTF_KIND_INT(id = 6) +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .long 11 # BTF_KIND_INT(id = 5) ; CHECK-NEXT: .long 16777216 # 0x1000000 ; CHECK-NEXT: .long 4 ; CHECK-NEXT: .long 16777248 # 0x1000020 +; CHECK-NEXT: .long 1 # BTF_KIND_TYPE_TAG(id = 6) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 5 ; CHECK-NEXT: .long 15 # BTF_KIND_VAR(id = 7) ; CHECK-NEXT: .long 234881024 # 0xe000000 -; CHECK-NEXT: .long 3 ; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 17 # BTF_KIND_DATASEC(id = 8) +; CHECK-NEXT: .long 251658241 # 0xf000001 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 7 +; CHECK-NEXT: .long g +; CHECK-NEXT: .long 8 ; CHECK: .ascii "tag1" # string offset=1 ; CHECK: .ascii "tag2" # string offset=6 ; CHECK: .ascii "int" # string offset=11 ; CHECK: .byte 103 # string offset=15 -!12 = !{i32 7, !"Dwarf Version", i32 4} -!13 = !{i32 2, !"Debug Info Version", i32 3} -!14 = !{i32 1, !"wchar_size", i32 4} -!15 = !{i32 7, !"frame-pointer", i32 2} -!16 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 077b2e0cf1e97c4d97ca5ceab3ec0192ed11c66e)"} +!15 = !{i32 7, !"Dwarf Version", i32 5} +!16 = !{i32 2, !"Debug Info Version", i32 3} +!17 = !{i32 1, !"wchar_size", i32 4} +!18 = !{i32 7, !"frame-pointer", i32 2} +!19 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git 5aa6dd1e09616a455377f3066d2034d3e8a073ba)"} diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-void.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-void.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-void.ll @@ -0,0 +1,48 @@ +; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; void __tag1 *foo; +; Compilation flag: +; clang -S -g -emit-llvm test.c -o test.ll + +@foo = dso_local local_unnamed_addr global ptr null, align 8, !dbg !0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!11, !12, !13, !14, !15, !16} +!llvm.ident = !{!17} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "foo", scope: !2, file: !3, line: 2, type: !5, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git b0d4d11b535f4c3b730222013f4da5b0cbc4558a)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "d51e2505a6f475a2c1811fd5e5fa9c49") +!4 = !{!0} +!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64, annotations: !9) +!6 = !DIBasicType(tag: DW_TAG_unspecified_type, name: "void", annotations: !7) +!7 = !{!8} +!8 = !{!"btf:type_tag", !"tag1"} +!9 = !{!10} +!10 = !{!"btf_type_tag", !"tag1"} +!11 = !{i32 7, !"Dwarf Version", i32 5} +!12 = !{i32 2, !"Debug Info Version", i32 3} +!13 = !{i32 1, !"wchar_size", i32 4} +!14 = !{i32 8, !"PIC Level", i32 2} +!15 = !{i32 7, !"PIE Level", i32 2} +!16 = !{i32 7, !"uwtable", i32 2} +!17 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git b0d4d11b535f4c3b730222013f4da5b0cbc4558a)"} + +; CHECK: .long 0 # BTF_KIND_PTR(id = 1) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long 1 # BTF_KIND_TYPE_TAG(id = 2) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 0 +; ^^^ void type id +; CHECK-NEXT: .long 6 # BTF_KIND_VAR(id = 3) +; CHECK-NEXT: .long 234881024 # 0xe000000 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 1 + +; CHECK: .ascii "tag1" # string offset=1 +; CHECK: .ascii "foo" # string offset=6