[llvm] [SHT_LLVM_FUNC_MAP][ObjectYaml]Introduce function address map section and emit dynamic instruction count(ObjectYaml part) (PR #124332)
Lei Wang via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 27 15:47:52 PST 2025
https://github.com/wlei-llvm updated https://github.com/llvm/llvm-project/pull/124332
>From 2e0f4ff565c3e6f4813b39f6ee65e7d0c454f967 Mon Sep 17 00:00:00 2001
From: wlei <wlei at fb.com>
Date: Fri, 24 Jan 2025 11:40:44 -0800
Subject: [PATCH 1/2] =?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 48ae0db80f43ee..46837c402d88b6 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 87e4dbe4480910..749a3635ebf14b 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<uint8_t>(DynamicInstCount) << 0);
+ }
+
+ // Decodes from minimum bit width representation and validates no
+ // unnecessary bits are used.
+ static Expected<Features> decode(uint8_t Val) {
+ Features Feat{static_cast<bool>(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 dfdfa055d65fa6..9180135683b65c 100644
--- a/llvm/include/llvm/ObjectYAML/ELFYAML.h
+++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h
@@ -195,6 +195,13 @@ struct PGOAnalysisMapEntry {
std::optional<std::vector<PGOBBEntry>> 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<std::vector<FuncMapEntry>> Entries;
+
+ FuncMapSection() : Section(ChunkKind::FuncMap) {}
+
+ std::vector<std::pair<StringRef, bool>> getEntries() const override {
+ return {{"Entries", Entries.has_value()}};
+ };
+
+ static bool classof(const Chunk *S) { return S->Kind == ChunkKind::FuncMap; }
+};
+
struct StackSizesSection : Section {
std::optional<std::vector<StackSizeEntry>> Entries;
@@ -762,6 +782,7 @@ bool shouldAllocateFileSpace(ArrayRef<ProgramHeader> 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<ELFYAML::StackSizeEntry> {
static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel);
};
+template <> struct MappingTraits<ELFYAML::FuncMapEntry> {
+ static void mapping(IO &IO, ELFYAML::FuncMapEntry &E);
+};
+
template <> struct MappingTraits<ELFYAML::BBAddrMapEntry> {
static void mapping(IO &IO, ELFYAML::BBAddrMapEntry &E);
};
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index b6d0699ee4fe08..41c3fb4cc5e406 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 9ae76a71ede5e0..7316003a90c141 100644
--- a/llvm/lib/ObjectYAML/ELFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -287,6 +287,9 @@ template <class ELFT> 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<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
writeSectionContent(SHeader, *S, CBA);
} else if (auto S = dyn_cast<ELFYAML::BBAddrMapSection>(Sec)) {
writeSectionContent(SHeader, *S, CBA);
+ } else if (auto S = dyn_cast<ELFYAML::FuncMapSection>(Sec)) {
+ writeSectionContent(SHeader, *S, CBA);
} else {
llvm_unreachable("Unknown section type");
}
@@ -1537,6 +1542,31 @@ void ELFState<ELFT>::writeSectionContent(
}
}
+template <class ELFT>
+void ELFState<ELFT>::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<int>(E.Version)
+ << "; encoding using the most recent version";
+ CBA.write(E.Version);
+ CBA.write(E.Feature);
+ SHeader.sh_size += 2;
+ }
+ CBA.write<uintX_t>(E.Address, ELFT::Endianness);
+ SHeader.sh_size += sizeof(uintX_t);
+ if (E.DynamicInstCount)
+ SHeader.sh_size += CBA.writeULEB128(E.DynamicInstCount);
+ }
+}
+
template <class ELFT>
void ELFState<ELFT>::writeSectionContent(
Elf_Shdr &SHeader, const ELFYAML::LinkerOptionsSection &Section,
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 539834fc8d4dbf..077fd3aed2d242 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -723,6 +723,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHT>::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<std::unique_ptr<ELFYAML::Chunk>>::mapping(
Section.reset(new ELFYAML::BBAddrMapSection());
sectionMapping(IO, *cast<ELFYAML::BBAddrMapSection>(Section.get()));
break;
+ case ELF::SHT_LLVM_FUNC_MAP:
+ if (!IO.outputting())
+ Section.reset(new ELFYAML::FuncMapSection());
+ sectionMapping(IO, *cast<ELFYAML::FuncMapSection>(Section.get()));
+ break;
+
default:
if (!IO.outputting()) {
StringRef Name;
@@ -1848,6 +1861,15 @@ void MappingTraits<ELFYAML::StackSizeEntry>::mapping(
IO.mapRequired("Size", E.Size);
}
+void MappingTraits<ELFYAML::FuncMapEntry>::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<ELFYAML::BBAddrMapEntry>::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 00000000000000..b21b637026f549
--- /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=<none>]]
+ 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 00000000000000..9fee4038b79312
--- /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=<none>]]
+ Size: [[SIZE=<none>]]
+
+## 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 b1c8032ea21929..ad2eb50937c210 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<ELFYAML::BBAddrMapSection *>
dumpBBAddrMapSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::FuncMapSection *> dumpFuncMapSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::RawContentSection *>
dumpPlaceholderSection(const Elf_Shdr *Shdr);
@@ -629,6 +630,8 @@ ELFDumper<ELFT>::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<ELFT>::dumpBBAddrMapSection(const Elf_Shdr *Shdr) {
return S.release();
}
+template <class ELFT>
+Expected<ELFYAML::FuncMapSection *>
+ELFDumper<ELFT>::dumpFuncMapSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::FuncMapSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ if (Content.empty())
+ return S.release();
+
+ DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4);
+
+ std::vector<ELFYAML::FuncMapEntry> 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 <class ELFT>
Expected<ELFYAML::AddrsigSection *>
ELFDumper<ELFT>::dumpAddrsigSection(const Elf_Shdr *Shdr) {
>From d2b1895dcc080c758510b822d755f4c5c7adeefb Mon Sep 17 00:00:00 2001
From: wlei <wlei at fb.com>
Date: Mon, 27 Jan 2025 15:46:38 -0800
Subject: [PATCH 2/2] 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 ea267842cdc353..bcba9d4a216455 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","", at 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 749a3635ebf14b..4ff4ec5f1b2c43 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<uint8_t>(DynamicInstCount) << 0);
- }
-
- // Decodes from minimum bit width representation and validates no
- // unnecessary bits are used.
- static Expected<Features> decode(uint8_t Val) {
- Features Feat{static_cast<bool>(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 9180135683b65c..f302bb03c6b203 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 7316003a90c141..06efac75c6e660 100644
--- a/llvm/lib/ObjectYAML/ELFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -1550,20 +1550,17 @@ void ELFState<ELFT>::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<int>(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<uintX_t>(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 077fd3aed2d242..737ce965e69b05 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -1865,9 +1865,8 @@ void MappingTraits<ELFYAML::FuncMapEntry>::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<ELFYAML::BBAddrMapEntry>::mapping(
diff --git a/llvm/test/tools/obj2yaml/ELF/func-map.yaml b/llvm/test/tools/obj2yaml/ELF/func-map.yaml
index b21b637026f549..4b0476b939b453 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=<none>]]
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 9fee4038b79312..2a515936a47ad5 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 ad2eb50937c210..0c7e6a4ade24f1 100644
--- a/llvm/tools/obj2yaml/elf2yaml.cpp
+++ b/llvm/tools/obj2yaml/elf2yaml.cpp
@@ -1012,22 +1012,18 @@ ELFDumper<ELFT>::dumpFuncMapSection(const Elf_Shdr *Shdr) {
std::vector<ELFYAML::FuncMapEntry> 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<int>(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) {
More information about the llvm-commits
mailing list