From 2e0f4ff565c3e6f4813b39f6ee65e7d0c454f967 Mon Sep 17 00:00:00 2001 From: wlei Date: Fri, 24 Jan 2025 11:40:44 -0800 Subject: [PATCH 1/7] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?= =?UTF-8?q?itial=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.6-beta.1 --- llvm/include/llvm/BinaryFormat/ELF.h | 1 + llvm/include/llvm/Object/ELFTypes.h | 38 ++++++ llvm/include/llvm/ObjectYAML/ELFYAML.h | 25 ++++ llvm/lib/Object/ELF.cpp | 1 + llvm/lib/ObjectYAML/ELFEmitter.cpp | 30 +++++ llvm/lib/ObjectYAML/ELFYAML.cpp | 22 ++++ llvm/test/tools/obj2yaml/ELF/func-map.yaml | 139 +++++++++++++++++++++ llvm/test/tools/yaml2obj/ELF/func-map.yaml | 116 +++++++++++++++++ llvm/tools/obj2yaml/elf2yaml.cpp | 52 ++++++++ 9 files changed, 424 insertions(+) create mode 100644 llvm/test/tools/obj2yaml/ELF/func-map.yaml create mode 100644 llvm/test/tools/yaml2obj/ELF/func-map.yaml diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 48ae0db80f43e..46837c402d88b 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1139,6 +1139,7 @@ enum : unsigned { SHT_LLVM_OFFLOADING = 0x6fff4c0b, // LLVM device offloading data. SHT_LLVM_LTO = 0x6fff4c0c, // .llvm.lto for fat LTO. SHT_LLVM_JT_SIZES = 0x6fff4c0d, // LLVM jump tables sizes. + SHT_LLVM_FUNC_MAP = 0x6fff4c0e, // LLVM function address map. // Android's experimental support for SHT_RELR sections. // https://android.googlesource.com/platform/bionic/+/b7feec74547f84559a1467aca02708ff61346d2a/libc/include/elf.h#512 SHT_ANDROID_RELR = 0x6fffff00, // Relocation entries; only offsets. diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h index 87e4dbe448091..749a3635ebf14 100644 --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -1027,6 +1027,44 @@ struct PGOAnalysisMap { } }; +// Struct representing the FuncMap for one function. +struct FuncMap { + + // Bitfield of optional features to control the extra information + // emitted/encoded in the the section. + struct Features { + bool DynamicInstCount : 1; + + // Encodes to minimum bit width representation. + uint8_t encode() const { + return (static_cast(DynamicInstCount) << 0); + } + + // Decodes from minimum bit width representation and validates no + // unnecessary bits are used. + static Expected decode(uint8_t Val) { + Features Feat{static_cast(Val & (1 << 0))}; + if (Feat.encode() != Val) + return createStringError(std::error_code(), + "invalid encoding for FuncMap::Features: 0x%x", + Val); + return Feat; + } + + bool operator==(const Features &Other) const { + return DynamicInstCount == Other.DynamicInstCount; + } + }; + + uint64_t FunctionAddress = 0; // Function entry address. + uint64_t DynamicInstCount = 0; // Dynamic instruction count for this function + + // Flags to indicate if each feature was enabled in this function + Features FeatEnable; + + uint64_t getFunctionAddress() const { return FunctionAddress; } +}; + } // end namespace object. } // end namespace llvm. diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h index dfdfa055d65fa..9180135683b65 100644 --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -195,6 +195,13 @@ struct PGOAnalysisMapEntry { std::optional> PGOBBEntries; }; +struct FuncMapEntry { + uint8_t Version; + llvm::yaml::Hex8 Feature; + llvm::yaml::Hex64 Address; + llvm::yaml::Hex64 DynamicInstCount; +}; + struct StackSizeEntry { llvm::yaml::Hex64 Address; llvm::yaml::Hex64 Size; @@ -229,6 +236,7 @@ struct Chunk { DependentLibraries, CallGraphProfile, BBAddrMap, + FuncMap, // Special chunks. SpecialChunksStart, @@ -355,6 +363,18 @@ struct BBAddrMapSection : Section { } }; +struct FuncMapSection : Section { + std::optional> Entries; + + FuncMapSection() : Section(ChunkKind::FuncMap) {} + + std::vector> getEntries() const override { + return {{"Entries", Entries.has_value()}}; + }; + + static bool classof(const Chunk *S) { return S->Kind == ChunkKind::FuncMap; } +}; + struct StackSizesSection : Section { std::optional> Entries; @@ -762,6 +782,7 @@ bool shouldAllocateFileSpace(ArrayRef Phdrs, } // end namespace llvm LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::FuncMapEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry::BBEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry::BBRangeEntry) @@ -929,6 +950,10 @@ template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::FuncMapEntry &E); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::BBAddrMapEntry &E); }; diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp index b6d0699ee4fe0..41c3fb4cc5e40 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -321,6 +321,7 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) { STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_OFFLOADING); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_LTO); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_JT_SIZES) + STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_FUNC_MAP); STRINGIFY_ENUM_CASE(ELF, SHT_GNU_ATTRIBUTES); STRINGIFY_ENUM_CASE(ELF, SHT_GNU_HASH); STRINGIFY_ENUM_CASE(ELF, SHT_GNU_verdef); diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index 9ae76a71ede5e..7316003a90c14 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -287,6 +287,9 @@ template class ELFState { void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::BBAddrMapSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::FuncMapSection &Section, + ContiguousBlobAccumulator &CBA); void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::HashSection &Section, ContiguousBlobAccumulator &CBA); @@ -894,6 +897,8 @@ void ELFState::initSectionHeaders(std::vector &SHeaders, writeSectionContent(SHeader, *S, CBA); } else if (auto S = dyn_cast(Sec)) { writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast(Sec)) { + writeSectionContent(SHeader, *S, CBA); } else { llvm_unreachable("Unknown section type"); } @@ -1537,6 +1542,31 @@ void ELFState::writeSectionContent( } } +template +void ELFState::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::FuncMapSection &Section, + ContiguousBlobAccumulator &CBA) { + if (!Section.Entries) + return; + + for (const auto &[Idx, E] : llvm::enumerate(*Section.Entries)) { + // Write version and feature values. + if (Section.Type == llvm::ELF::SHT_LLVM_FUNC_MAP) { + if (E.Version > 1) + WithColor::warning() << "unsupported SHT_LLVM_FUNC_MAP version: " + << static_cast(E.Version) + << "; encoding using the most recent version"; + CBA.write(E.Version); + CBA.write(E.Feature); + SHeader.sh_size += 2; + } + CBA.write(E.Address, ELFT::Endianness); + SHeader.sh_size += sizeof(uintX_t); + if (E.DynamicInstCount) + SHeader.sh_size += CBA.writeULEB128(E.DynamicInstCount); + } +} + template void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::LinkerOptionsSection &Section, diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp index 539834fc8d4db..077fd3aed2d24 100644 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -723,6 +723,7 @@ void ScalarEnumerationTraits::enumeration( ECase(SHT_LLVM_PART_PHDR); ECase(SHT_LLVM_BB_ADDR_MAP_V0); ECase(SHT_LLVM_BB_ADDR_MAP); + ECase(SHT_LLVM_FUNC_MAP); ECase(SHT_LLVM_OFFLOADING); ECase(SHT_LLVM_LTO); ECase(SHT_GNU_ATTRIBUTES); @@ -1432,6 +1433,12 @@ static void sectionMapping(IO &IO, ELFYAML::BBAddrMapSection &Section) { IO.mapOptional("PGOAnalyses", Section.PGOAnalyses); } +static void sectionMapping(IO &IO, ELFYAML::FuncMapSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Entries", Section.Entries); +} + static void sectionMapping(IO &IO, ELFYAML::StackSizesSection &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Entries", Section.Entries); @@ -1725,6 +1732,12 @@ void MappingTraits>::mapping( Section.reset(new ELFYAML::BBAddrMapSection()); sectionMapping(IO, *cast(Section.get())); break; + case ELF::SHT_LLVM_FUNC_MAP: + if (!IO.outputting()) + Section.reset(new ELFYAML::FuncMapSection()); + sectionMapping(IO, *cast(Section.get())); + break; + default: if (!IO.outputting()) { StringRef Name; @@ -1848,6 +1861,15 @@ void MappingTraits::mapping( IO.mapRequired("Size", E.Size); } +void MappingTraits::mapping(IO &IO, + ELFYAML::FuncMapEntry &E) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapRequired("Version", E.Version); + IO.mapOptional("Feature", E.Feature, Hex8(0)); + IO.mapOptional("Address", E.Address, Hex64(0)); + IO.mapOptional("DynInstCnt", E.DynamicInstCount, Hex64(0)); +} + void MappingTraits::mapping( IO &IO, ELFYAML::BBAddrMapEntry &E) { assert(IO.getContext() && "The IO context is not initialized"); diff --git a/llvm/test/tools/obj2yaml/ELF/func-map.yaml b/llvm/test/tools/obj2yaml/ELF/func-map.yaml new file mode 100644 index 0000000000000..b21b637026f54 --- /dev/null +++ b/llvm/test/tools/obj2yaml/ELF/func-map.yaml @@ -0,0 +1,139 @@ +## Check how obj2yaml produces YAML .llvm_func_map descriptions. + +## Check that obj2yaml uses the "Entries" tag to describe an .llvm_func_map section. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: obj2yaml %t1 | FileCheck %s --check-prefix=VALID + +# VALID: --- !ELF +# VALID-NEXT: FileHeader: +# VALID-NEXT: Class: ELFCLASS64 +# VALID-NEXT: Data: ELFDATA2LSB +# VALID-NEXT: Type: ET_EXEC +# VALID-NEXT: Sections: +# VALID-NEXT: - Name: .llvm_func_map +# VALID-NEXT: Type: SHT_LLVM_FUNC_MAP +# VALID-NEXT: Entries: +# VALID-NEXT: - Version: 1 +# VALID-NEXT: Feature: 0x1 +## The 'Address' field is omitted when it's zero. +# VALID-NEXT: DynInstCnt: 0x10 +# VALID-NEXT: - Version: 1 +## The 'Feature' field is omitted when it's zero. +# VALID-NEXT: Address: 0x1 +# VALID-NEXT: - Version: 1 +# VALID-NEXT: Feature: 0x1 +# VALID-NEXT: Address: 0xFFFFFFFFFFFFFFF1 +# VALID-NEXT: DynInstCnt: 0xFFFFFFFFFFFFFFF2 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm_func_map + Type: SHT_LLVM_FUNC_MAP + ShSize: [[SIZE=]] + Entries: + - Version: 1 + Feature: 0x1 + Address: 0x0 + DynInstCnt: 0x10 + - Version: 1 + Feature: 0x0 + Address: 0x1 + - Version: 1 + Feature: 0x1 + Address: 0xFFFFFFFFFFFFFFF1 + DynInstCnt: 0xFFFFFFFFFFFFFFF2 + +## Check obj2yaml can dump empty .llvm_func_map sections. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: obj2yaml %t2 | FileCheck %s --check-prefix=EMPTY + +# EMPTY: --- !ELF +# EMPTY-NEXT: FileHeader: +# EMPTY-NEXT: Class: ELFCLASS64 +# EMPTY-NEXT: Data: ELFDATA2LSB +# EMPTY-NEXT: Type: ET_EXEC +# EMPTY-NEXT: Sections: +# EMPTY-NEXT: - Name: .llvm_func_map +# EMPTY-NEXT: Type: SHT_LLVM_FUNC_MAP +# EMPTY-NOT: Content: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm_func_map + Type: SHT_LLVM_FUNC_MAP + Content: "" + +## Check obj2yaml can dump multiple .llvm_func_map sections. + +# RUN: yaml2obj --docnum=3 %s -o %t3 +# RUN: obj2yaml %t3 | FileCheck %s --check-prefix=MULTI + +# MULTI: --- !ELF +# MULTI-NEXT: FileHeader: +# MULTI-NEXT: Class: ELFCLASS64 +# MULTI-NEXT: Data: ELFDATA2LSB +# MULTI-NEXT: Type: ET_EXEC +# MULTI-NEXT: Sections: +# MULTI-NEXT: - Name: .llvm_func_map +# MULTI-NEXT: Type: SHT_LLVM_FUNC_MAP +# MULTI-NEXT: Entries: +# MULTI-NEXT: - Version: 1 +# MULTI-NEXT: Feature: 0x1 +# MULTI-NEXT: Address: 0x2 +# MULTI-NEXT: DynInstCnt: 0x3 +# MULTI-NEXT: - Name: '.llvm_func_map (1)' +# MULTI-NEXT: Type: SHT_LLVM_FUNC_MAP +# MULTI-NEXT: Entries: +# MULTI-NEXT: - Version: 1 +# MULTI-NEXT: Feature: 0x1 +# MULTI-NEXT: Address: 0xA +# MULTI-NEXT: DynInstCnt: 0xB + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm_func_map + Type: SHT_LLVM_FUNC_MAP + Entries: + - Version: 1 + Feature: 0x1 + Address: 0x2 + DynInstCnt: 0x3 + - Name: '.llvm_func_map (1)' + Type: SHT_LLVM_FUNC_MAP + Entries: + - Version: 1 + Feature: 0x1 + Address: 0xA + DynInstCnt: 0xB + +## Check that obj2yaml uses the "Content" tag to describe an .llvm_func_map section +## when it can't extract the entries, for example, when the section is truncated. + +# RUN: yaml2obj --docnum=1 -DSIZE=0x8 %s -o %t4 +# RUN: obj2yaml %t4 | FileCheck %s --check-prefixes=TRUNCATED,INVALID + + +# INVALID: --- !ELF +# INVALID-NEXT: FileHeader: +# INVALID-NEXT: Class: ELFCLASS64 +# INVALID-NEXT: Data: ELFDATA2LSB +# INVALID-NEXT: Type: ET_EXEC +# INVALID-NEXT: Sections: +# INVALID-NEXT: - Name: .llvm_func_map +# INVALID-NEXT: Type: SHT_LLVM_FUNC_MAP +# BADNUM-NEXT: Content: {{([[:xdigit:]]+)}}{{$}} +# TRUNCATED-NEXT: Content: '{{([[:xdigit:]]{16})}}'{{$}} diff --git a/llvm/test/tools/yaml2obj/ELF/func-map.yaml b/llvm/test/tools/yaml2obj/ELF/func-map.yaml new file mode 100644 index 0000000000000..9fee4038b7931 --- /dev/null +++ b/llvm/test/tools/yaml2obj/ELF/func-map.yaml @@ -0,0 +1,116 @@ +## Check how yaml2obj produces .llvm_func_map sections. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-readobj --sections --section-data %t1 | FileCheck %s + +## Case 1: Specify content. +# CHECK: Section { +# CHECK: Index: 1 +# CHECK-NEXT: Name: .llvm_func_map (1) +# CHECK-NEXT: Type: SHT_LLVM_FUNC_MAP (0x6FFF4C0E) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x0 +# CHECK-NEXT: Offset: 0x40 +# CHECK-NEXT: Size: 13 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 0 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: SectionData ( +# CHECK-NEXT: 0000: 00000000 00000000 01010203 04 +# CHECK-NEXT: ) +# CHECK-NEXT: } + +## Case 2: Empty. +# CHECK: Name: .llvm_func_map (1) +# CHECK: Size: +# CHECK-SAME: {{^ 0$}} + +## Case 3: Specify Size only. +# CHECK: Name: .llvm_func_map (1) +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 00000000 00000000 +# CHECK-NEXT: ) + +# Case 4: Specify Entries. +# CHECK: Name: .llvm_func_map (1) +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 01012222 02000000 000010 +# CHECK-NEXT: ) + + + + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + +## Test the following cases: + +## 1) We can produce an .llvm_func_map section from a description with section +## content. +## Specify Content. + - Name: '.llvm_func_map (1)' + Type: SHT_LLVM_FUNC_MAP + Content: "00000000000000000101020304" + +# 2) We can produce an empty .llvm_func_map section from a description +# with empty section content. + - Name: '.llvm_func_map (2)' + Type: SHT_LLVM_FUNC_MAP + +## 3) We can produce a zero .llvm_func_map section of a specific size when +## we specify the size only. + - Name: '.llvm_func_map (3)' + Type: SHT_LLVM_FUNC_MAP + Size: 8 + +## 4) We can produce an .llvm_func_map section from a description with +## Entries. + - Name: '.llvm_func_map (4)' + Type: SHT_LLVM_FUNC_MAP + Entries: + - Version: 1 + Feature: 0x1 + Address: 0x22222 + DynInstCnt: 0x10 + +## Check we can't use Entries at the same time as either Content or Size. +# RUN: not yaml2obj --docnum=2 -DCONTENT="00" %s 2>&1 | FileCheck %s --check-prefix=INVALID +# RUN: not yaml2obj --docnum=2 -DSIZE="0" %s 2>&1 | FileCheck %s --check-prefix=INVALID + +# INVALID: error: "Entries" cannot be used with "Content" or "Size" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: +## Specify Content and Size + - Name: '.llvm_func_map' + Type: SHT_LLVM_FUNC_MAP + Entries: [] + Content: [[CONTENT=]] + Size: [[SIZE=]] + +## Check that yaml2obj generates a warning when we use unsupported versions. +# RUN: yaml2obj --docnum=3 %s 2>&1 | FileCheck %s --check-prefix=INVALID-VERSION + +# INVALID-VERSION: warning: unsupported SHT_LLVM_FUNC_MAP version: 2; encoding using the most recent version + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: '.llvm_func_map' + Type: SHT_LLVM_FUNC_MAP + Entries: +# Specify unsupported version + - Version: 2 diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index b1c8032ea2192..ad2eb50937c21 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -97,6 +97,7 @@ class ELFDumper { dumpStackSizesSection(const Elf_Shdr *Shdr); Expected dumpBBAddrMapSection(const Elf_Shdr *Shdr); + Expected dumpFuncMapSection(const Elf_Shdr *Shdr); Expected dumpPlaceholderSection(const Elf_Shdr *Shdr); @@ -629,6 +630,8 @@ ELFDumper::dumpSections() { [this](const Elf_Shdr *S) { return dumpCallGraphProfileSection(S); }; case ELF::SHT_LLVM_BB_ADDR_MAP: return [this](const Elf_Shdr *S) { return dumpBBAddrMapSection(S); }; + case ELF::SHT_LLVM_FUNC_MAP: + return [this](const Elf_Shdr *S) { return dumpFuncMapSection(S); }; case ELF::SHT_STRTAB: case ELF::SHT_SYMTAB: case ELF::SHT_DYNSYM: @@ -989,6 +992,55 @@ ELFDumper::dumpBBAddrMapSection(const Elf_Shdr *Shdr) { return S.release(); } +template +Expected +ELFDumper::dumpFuncMapSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + ArrayRef Content = *ContentOrErr; + if (Content.empty()) + return S.release(); + + DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4); + + std::vector Entries; + DataExtractor::Cursor Cur(0); + uint8_t Version = 0; + uint8_t Feature = 0; + uint64_t Address = 0; + while (Cur && Cur.tell() < Content.size()) { + if (Shdr->sh_type == ELF::SHT_LLVM_FUNC_MAP) { + Version = Data.getU8(Cur); + Feature = Data.getU8(Cur); + } + auto FeatureOrErr = llvm::object::FuncMap::Features::decode(Feature); + if (!FeatureOrErr) + return FeatureOrErr.takeError(); + + Address = Data.getAddress(Cur); + + uint64_t DynamicInstCount = + FeatureOrErr->DynamicInstCount ? Data.getULEB128(Cur) : 0; + Entries.push_back({Version, Feature, Address, DynamicInstCount}); + } + + if (!Cur) { + // If the section cannot be decoded, we dump it as an array of bytes. + consumeError(Cur.takeError()); + S->Content = yaml::BinaryRef(Content); + } else { + S->Entries = std::move(Entries); + } + + return S.release(); +} + template Expected ELFDumper::dumpAddrsigSection(const Elf_Shdr *Shdr) { From d2b1895dcc080c758510b822d755f4c5c7adeefb Mon Sep 17 00:00:00 2001 From: wlei Date: Mon, 27 Jan 2025 15:46:38 -0800 Subject: [PATCH 2/7] addressing comments Created using spr 1.3.6-beta.1 --- llvm/docs/Extensions.rst | 21 +++++++++++++- llvm/include/llvm/Object/ELFTypes.h | 32 +--------------------- llvm/include/llvm/ObjectYAML/ELFYAML.h | 3 +- llvm/lib/ObjectYAML/ELFEmitter.cpp | 7 ++--- llvm/lib/ObjectYAML/ELFYAML.cpp | 3 +- llvm/test/tools/obj2yaml/ELF/func-map.yaml | 29 +++++++------------- llvm/test/tools/yaml2obj/ELF/func-map.yaml | 3 +- llvm/tools/obj2yaml/elf2yaml.cpp | 16 ++++------- 8 files changed, 42 insertions(+), 72 deletions(-) diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst index ea267842cdc35..bcba9d4a21645 100644 --- a/llvm/docs/Extensions.rst +++ b/llvm/docs/Extensions.rst @@ -535,6 +535,26 @@ Example of BBAddrMap with PGO data: .uleb128 1000 # BB_3 basic block frequency (only when enabled) .uleb128 0 # BB_3 successors count (only enabled with branch probabilities) +``SHT_LLVM_FUNC_MAP`` Section (function address map) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +This section stores the mapping from the binary address of function to its +related metadata features. It is used to emit function-level analysis data and +can be enabled through ``--func-map`` option. The fields are encoded in the +following format: + +#. A version number byte used for backward compatibility +#. The function's entry address. +#. Dynamic Instruction Count, which is calculated as the total PGO counts for all + instructions within the function. + +Example: + +.. code-block:: gas + .section ".llvm_func_map","",@llvm_func_map + .byte 1 # version number + .quad .Lfunc_begin1 # function address + .uleb128 333 # dynamic instruction count + ``SHT_LLVM_OFFLOADING`` Section (offloading data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This section stores the binary data used to perform offloading device linking @@ -725,4 +745,3 @@ follows: add x16, x16, :lo12:__chkstk blr x16 sub sp, sp, x15, lsl #4 - diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h index 749a3635ebf14..4ff4ec5f1b2c4 100644 --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -1029,38 +1029,8 @@ struct PGOAnalysisMap { // Struct representing the FuncMap for one function. struct FuncMap { - - // Bitfield of optional features to control the extra information - // emitted/encoded in the the section. - struct Features { - bool DynamicInstCount : 1; - - // Encodes to minimum bit width representation. - uint8_t encode() const { - return (static_cast(DynamicInstCount) << 0); - } - - // Decodes from minimum bit width representation and validates no - // unnecessary bits are used. - static Expected decode(uint8_t Val) { - Features Feat{static_cast(Val & (1 << 0))}; - if (Feat.encode() != Val) - return createStringError(std::error_code(), - "invalid encoding for FuncMap::Features: 0x%x", - Val); - return Feat; - } - - bool operator==(const Features &Other) const { - return DynamicInstCount == Other.DynamicInstCount; - } - }; - uint64_t FunctionAddress = 0; // Function entry address. - uint64_t DynamicInstCount = 0; // Dynamic instruction count for this function - - // Flags to indicate if each feature was enabled in this function - Features FeatEnable; + uint64_t DynamicInstCount = 0; // Dynamic instruction count for this function. uint64_t getFunctionAddress() const { return FunctionAddress; } }; diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h index 9180135683b65..f302bb03c6b20 100644 --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -197,9 +197,8 @@ struct PGOAnalysisMapEntry { struct FuncMapEntry { uint8_t Version; - llvm::yaml::Hex8 Feature; llvm::yaml::Hex64 Address; - llvm::yaml::Hex64 DynamicInstCount; + uint64_t DynamicInstCount; }; struct StackSizeEntry { diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index 7316003a90c14..06efac75c6e66 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -1550,20 +1550,17 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, return; for (const auto &[Idx, E] : llvm::enumerate(*Section.Entries)) { - // Write version and feature values. if (Section.Type == llvm::ELF::SHT_LLVM_FUNC_MAP) { if (E.Version > 1) WithColor::warning() << "unsupported SHT_LLVM_FUNC_MAP version: " << static_cast(E.Version) << "; encoding using the most recent version"; CBA.write(E.Version); - CBA.write(E.Feature); - SHeader.sh_size += 2; + SHeader.sh_size += 1; } CBA.write(E.Address, ELFT::Endianness); SHeader.sh_size += sizeof(uintX_t); - if (E.DynamicInstCount) - SHeader.sh_size += CBA.writeULEB128(E.DynamicInstCount); + SHeader.sh_size += CBA.writeULEB128(E.DynamicInstCount); } } diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp index 077fd3aed2d24..737ce965e69b0 100644 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -1865,9 +1865,8 @@ void MappingTraits::mapping(IO &IO, ELFYAML::FuncMapEntry &E) { assert(IO.getContext() && "The IO context is not initialized"); IO.mapRequired("Version", E.Version); - IO.mapOptional("Feature", E.Feature, Hex8(0)); IO.mapOptional("Address", E.Address, Hex64(0)); - IO.mapOptional("DynInstCnt", E.DynamicInstCount, Hex64(0)); + IO.mapOptional("DynInstCnt", E.DynamicInstCount, 0); } void MappingTraits::mapping( diff --git a/llvm/test/tools/obj2yaml/ELF/func-map.yaml b/llvm/test/tools/obj2yaml/ELF/func-map.yaml index b21b637026f54..4b0476b939b45 100644 --- a/llvm/test/tools/obj2yaml/ELF/func-map.yaml +++ b/llvm/test/tools/obj2yaml/ELF/func-map.yaml @@ -15,16 +15,14 @@ # VALID-NEXT: Type: SHT_LLVM_FUNC_MAP # VALID-NEXT: Entries: # VALID-NEXT: - Version: 1 -# VALID-NEXT: Feature: 0x1 ## The 'Address' field is omitted when it's zero. -# VALID-NEXT: DynInstCnt: 0x10 +# VALID-NEXT: DynInstCnt: 16 +## The 'DynInstCnt' field is omitted when it's zero. # VALID-NEXT: - Version: 1 -## The 'Feature' field is omitted when it's zero. # VALID-NEXT: Address: 0x1 # VALID-NEXT: - Version: 1 -# VALID-NEXT: Feature: 0x1 # VALID-NEXT: Address: 0xFFFFFFFFFFFFFFF1 -# VALID-NEXT: DynInstCnt: 0xFFFFFFFFFFFFFFF2 +# VALID-NEXT: DynInstCnt: 100001 --- !ELF FileHeader: @@ -37,16 +35,14 @@ Sections: ShSize: [[SIZE=]] Entries: - Version: 1 - Feature: 0x1 Address: 0x0 - DynInstCnt: 0x10 + DynInstCnt: 16 - Version: 1 - Feature: 0x0 Address: 0x1 + DynInstCnt: 0 - Version: 1 - Feature: 0x1 Address: 0xFFFFFFFFFFFFFFF1 - DynInstCnt: 0xFFFFFFFFFFFFFFF2 + DynInstCnt: 100001 ## Check obj2yaml can dump empty .llvm_func_map sections. @@ -88,16 +84,14 @@ Sections: # MULTI-NEXT: Type: SHT_LLVM_FUNC_MAP # MULTI-NEXT: Entries: # MULTI-NEXT: - Version: 1 -# MULTI-NEXT: Feature: 0x1 # MULTI-NEXT: Address: 0x2 -# MULTI-NEXT: DynInstCnt: 0x3 +# MULTI-NEXT: DynInstCnt: 3 # MULTI-NEXT: - Name: '.llvm_func_map (1)' # MULTI-NEXT: Type: SHT_LLVM_FUNC_MAP # MULTI-NEXT: Entries: # MULTI-NEXT: - Version: 1 -# MULTI-NEXT: Feature: 0x1 # MULTI-NEXT: Address: 0xA -# MULTI-NEXT: DynInstCnt: 0xB +# MULTI-NEXT: DynInstCnt: 100 --- !ELF FileHeader: @@ -109,16 +103,14 @@ Sections: Type: SHT_LLVM_FUNC_MAP Entries: - Version: 1 - Feature: 0x1 Address: 0x2 - DynInstCnt: 0x3 + DynInstCnt: 3 - Name: '.llvm_func_map (1)' Type: SHT_LLVM_FUNC_MAP Entries: - Version: 1 - Feature: 0x1 Address: 0xA - DynInstCnt: 0xB + DynInstCnt: 100 ## Check that obj2yaml uses the "Content" tag to describe an .llvm_func_map section ## when it can't extract the entries, for example, when the section is truncated. @@ -135,5 +127,4 @@ Sections: # INVALID-NEXT: Sections: # INVALID-NEXT: - Name: .llvm_func_map # INVALID-NEXT: Type: SHT_LLVM_FUNC_MAP -# BADNUM-NEXT: Content: {{([[:xdigit:]]+)}}{{$}} # TRUNCATED-NEXT: Content: '{{([[:xdigit:]]{16})}}'{{$}} diff --git a/llvm/test/tools/yaml2obj/ELF/func-map.yaml b/llvm/test/tools/yaml2obj/ELF/func-map.yaml index 9fee4038b7931..2a515936a47ad 100644 --- a/llvm/test/tools/yaml2obj/ELF/func-map.yaml +++ b/llvm/test/tools/yaml2obj/ELF/func-map.yaml @@ -36,7 +36,7 @@ # Case 4: Specify Entries. # CHECK: Name: .llvm_func_map (1) # CHECK: SectionData ( -# CHECK-NEXT: 0000: 01012222 02000000 000010 +# CHECK-NEXT: 0000: 01222202 00000000 0010 # CHECK-NEXT: ) @@ -75,7 +75,6 @@ Sections: Type: SHT_LLVM_FUNC_MAP Entries: - Version: 1 - Feature: 0x1 Address: 0x22222 DynInstCnt: 0x10 diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index ad2eb50937c21..0c7e6a4ade24f 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -1012,22 +1012,18 @@ ELFDumper::dumpFuncMapSection(const Elf_Shdr *Shdr) { std::vector Entries; DataExtractor::Cursor Cur(0); uint8_t Version = 0; - uint8_t Feature = 0; uint64_t Address = 0; while (Cur && Cur.tell() < Content.size()) { if (Shdr->sh_type == ELF::SHT_LLVM_FUNC_MAP) { Version = Data.getU8(Cur); - Feature = Data.getU8(Cur); + if (Cur && Version > 1) + return createStringError(errc::invalid_argument, + "invalid SHT_LLVM_FUNC_MAP section version: " + + Twine(static_cast(Version))); } - auto FeatureOrErr = llvm::object::FuncMap::Features::decode(Feature); - if (!FeatureOrErr) - return FeatureOrErr.takeError(); - Address = Data.getAddress(Cur); - - uint64_t DynamicInstCount = - FeatureOrErr->DynamicInstCount ? Data.getULEB128(Cur) : 0; - Entries.push_back({Version, Feature, Address, DynamicInstCount}); + uint64_t DynamicInstCount = Data.getULEB128(Cur); + Entries.push_back({Version, Address, DynamicInstCount}); } if (!Cur) { From 5841112f5fa1366b8abcc2589798a58b1f273552 Mon Sep 17 00:00:00 2001 From: wlei Date: Mon, 27 Jan 2025 15:49:22 -0800 Subject: [PATCH 3/7] addressing comments Created using spr 1.3.6-beta.1 --- llvm/docs/Extensions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst index bcba9d4a21645..8fb31453fca54 100644 --- a/llvm/docs/Extensions.rst +++ b/llvm/docs/Extensions.rst @@ -542,7 +542,7 @@ related metadata features. It is used to emit function-level analysis data and can be enabled through ``--func-map`` option. The fields are encoded in the following format: -#. A version number byte used for backward compatibility +#. A version number byte used for backward compatibility. #. The function's entry address. #. Dynamic Instruction Count, which is calculated as the total PGO counts for all instructions within the function. From cc113063e04fdf5454cc3ea0c8b97072be8f423d Mon Sep 17 00:00:00 2001 From: wlei Date: Mon, 27 Jan 2025 16:04:04 -0800 Subject: [PATCH 4/7] fix doc format Created using spr 1.3.6-beta.1 --- llvm/docs/Extensions.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst index 8fb31453fca54..1ebea4a96eaaf 100644 --- a/llvm/docs/Extensions.rst +++ b/llvm/docs/Extensions.rst @@ -550,10 +550,11 @@ following format: Example: .. code-block:: gas + .section ".llvm_func_map","",@llvm_func_map .byte 1 # version number .quad .Lfunc_begin1 # function address - .uleb128 333 # dynamic instruction count + .uleb128 1000 # dynamic instruction count ``SHT_LLVM_OFFLOADING`` Section (offloading data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d2d627ec2088b9410d33ab937a4ed5edcf5eb268 Mon Sep 17 00:00:00 2001 From: wlei Date: Tue, 11 Feb 2025 21:37:28 -0800 Subject: [PATCH 5/7] fix doc format Created using spr 1.3.6-beta.1 --- llvm/docs/Extensions.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst index ab0261c177d7f..8efb3dde2b1c2 100644 --- a/llvm/docs/Extensions.rst +++ b/llvm/docs/Extensions.rst @@ -550,6 +550,7 @@ following format: Example: .. code-block:: gas + .section ".llvm_func_map","",@llvm_func_map .byte 1 # version number .quad .Lfunc_begin1 # function address From 970042e09b4e08c0f53391b1ad134a56d7838bc2 Mon Sep 17 00:00:00 2001 From: wlei Date: Wed, 12 Feb 2025 11:58:43 -0800 Subject: [PATCH 6/7] Use fixed size instead of ULEBs Created using spr 1.3.6-beta.1 --- llvm/docs/Extensions.rst | 2 +- llvm/lib/ObjectYAML/ELFEmitter.cpp | 3 ++- llvm/test/tools/yaml2obj/ELF/func-map.yaml | 3 ++- llvm/tools/obj2yaml/elf2yaml.cpp | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst index 8efb3dde2b1c2..43c0582cc3ced 100644 --- a/llvm/docs/Extensions.rst +++ b/llvm/docs/Extensions.rst @@ -554,7 +554,7 @@ Example: .section ".llvm_func_map","",@llvm_func_map .byte 1 # version number .quad .Lfunc_begin1 # function address - .uleb128 1000 # dynamic instruction count + .quad 1000 # dynamic instruction count ``SHT_LLVM_OFFLOADING`` Section (offloading data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index 06efac75c6e66..f86af4d81d21a 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -1560,7 +1560,8 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, } CBA.write(E.Address, ELFT::Endianness); SHeader.sh_size += sizeof(uintX_t); - SHeader.sh_size += CBA.writeULEB128(E.DynamicInstCount); + CBA.write(E.DynamicInstCount, ELFT::Endianness); + SHeader.sh_size += 8; } } diff --git a/llvm/test/tools/yaml2obj/ELF/func-map.yaml b/llvm/test/tools/yaml2obj/ELF/func-map.yaml index 2a515936a47ad..ea3d6f9b74611 100644 --- a/llvm/test/tools/yaml2obj/ELF/func-map.yaml +++ b/llvm/test/tools/yaml2obj/ELF/func-map.yaml @@ -36,7 +36,8 @@ # Case 4: Specify Entries. # CHECK: Name: .llvm_func_map (1) # CHECK: SectionData ( -# CHECK-NEXT: 0000: 01222202 00000000 0010 +# CHECK-NEXT: 0000: 01222202 00000000 00100000 00000000 +# CHECK-NEXT: 0010: 00 # CHECK-NEXT: ) diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index 0c7e6a4ade24f..647129a89e11b 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -1022,7 +1022,7 @@ ELFDumper::dumpFuncMapSection(const Elf_Shdr *Shdr) { Twine(static_cast(Version))); } Address = Data.getAddress(Cur); - uint64_t DynamicInstCount = Data.getULEB128(Cur); + uint64_t DynamicInstCount = Data.getU64(Cur); Entries.push_back({Version, Address, DynamicInstCount}); } From 3703e53776ab3634654d799f3944fc7c277f18fd Mon Sep 17 00:00:00 2001 From: wlei Date: Fri, 14 Feb 2025 21:10:57 -0800 Subject: [PATCH 7/7] set entry size Created using spr 1.3.6-beta.1 --- llvm/lib/ObjectYAML/ELFEmitter.cpp | 10 +++++++--- llvm/test/tools/obj2yaml/ELF/func-map.yaml | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index f86af4d81d21a..6f8e6851c30a5 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -1550,18 +1550,22 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, return; for (const auto &[Idx, E] : llvm::enumerate(*Section.Entries)) { + unsigned Size = 0; if (Section.Type == llvm::ELF::SHT_LLVM_FUNC_MAP) { if (E.Version > 1) WithColor::warning() << "unsupported SHT_LLVM_FUNC_MAP version: " << static_cast(E.Version) << "; encoding using the most recent version"; CBA.write(E.Version); - SHeader.sh_size += 1; + Size += 1; } CBA.write(E.Address, ELFT::Endianness); - SHeader.sh_size += sizeof(uintX_t); + Size += sizeof(uintX_t); CBA.write(E.DynamicInstCount, ELFT::Endianness); - SHeader.sh_size += 8; + Size += 8; + + SHeader.sh_size += Size; + SHeader.sh_entsize = Size; } } diff --git a/llvm/test/tools/obj2yaml/ELF/func-map.yaml b/llvm/test/tools/obj2yaml/ELF/func-map.yaml index 4b0476b939b45..dc5da50059ece 100644 --- a/llvm/test/tools/obj2yaml/ELF/func-map.yaml +++ b/llvm/test/tools/obj2yaml/ELF/func-map.yaml @@ -13,6 +13,7 @@ # VALID-NEXT: Sections: # VALID-NEXT: - Name: .llvm_func_map # VALID-NEXT: Type: SHT_LLVM_FUNC_MAP +# VALID-NEXT: EntSize: 0x11 # VALID-NEXT: Entries: # VALID-NEXT: - Version: 1 ## The 'Address' field is omitted when it's zero. @@ -82,12 +83,14 @@ Sections: # MULTI-NEXT: Sections: # MULTI-NEXT: - Name: .llvm_func_map # MULTI-NEXT: Type: SHT_LLVM_FUNC_MAP +# MULTI-NEXT: EntSize: 0x11 # MULTI-NEXT: Entries: # MULTI-NEXT: - Version: 1 # MULTI-NEXT: Address: 0x2 # MULTI-NEXT: DynInstCnt: 3 # MULTI-NEXT: - Name: '.llvm_func_map (1)' # MULTI-NEXT: Type: SHT_LLVM_FUNC_MAP +# MULTI-NEXT: EntSize: 0x11 # MULTI-NEXT: Entries: # MULTI-NEXT: - Version: 1 # MULTI-NEXT: Address: 0xA @@ -127,4 +130,5 @@ Sections: # INVALID-NEXT: Sections: # INVALID-NEXT: - Name: .llvm_func_map # INVALID-NEXT: Type: SHT_LLVM_FUNC_MAP +# INVALID-NEXT: EntSize: 0x11 # TRUNCATED-NEXT: Content: '{{([[:xdigit:]]{16})}}'{{$}}