Skip to content

[MC] Emit a jump table size section #101962

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 6, 2024
Merged

[MC] Emit a jump table size section #101962

merged 10 commits into from
Sep 6, 2024

Conversation

omern1
Copy link
Member

@omern1 omern1 commented Aug 5, 2024

This patch will make LLVM emit a jump table size section containing tuples of (jump table address, entry count) in object files. This section is useful for tools that need to statically reconstruct the control flow of executables.

The name of the new section is .debug_llvm_jump_table_sizes because that makes both llvm-strip and GNU strip remove it.

At the moment this is only enabled by default for the PS5 target.

This patch will make LLVM emit a jump table size section containing
tuples of (jump table address, entry count) in object files.
This section is useful for tools that need to statically reconstruct
the control flow of executables.

The name of the new section is .debug_llvm_jump_table_sizes
because that makes both llvm-strip and GNU strip remove it.

At the moment this is only enabled by default for the PS5 target.
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:X86 clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' mc Machine (object) code llvm:binary-utilities labels Aug 5, 2024
@llvmbot
Copy link
Member

llvmbot commented Aug 5, 2024

@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clang-driver

@llvm/pr-subscribers-llvm-binary-utilities

Author: Nabeel Omer (omern1)

Changes

This patch will make LLVM emit a jump table size section containing tuples of (jump table address, entry count) in object files. This section is useful for tools that need to statically reconstruct the control flow of executables.

The name of the new section is .debug_llvm_jump_table_sizes because that makes both llvm-strip and GNU strip remove it.

At the moment this is only enabled by default for the PS5 target.


Full diff: https://github.com/llvm/llvm-project/pull/101962.diff

9 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/PS4CPU.cpp (+8)
  • (modified) clang/test/Driver/ps4-ps5-toolchain.c (+5)
  • (modified) llvm/docs/Extensions.rst (+6)
  • (modified) llvm/include/llvm/BinaryFormat/ELF.h (+1)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (+26)
  • (modified) llvm/lib/MC/MCParser/ELFAsmParser.cpp (+2)
  • (modified) llvm/lib/MC/MCSectionELF.cpp (+2)
  • (modified) llvm/lib/Object/ELF.cpp (+1)
  • (added) llvm/test/CodeGen/X86/jump-table-size-section.ll (+97)
diff --git a/clang/lib/Driver/ToolChains/PS4CPU.cpp b/clang/lib/Driver/ToolChains/PS4CPU.cpp
index a9e612c44da06..f9a9e995fff8e 100644
--- a/clang/lib/Driver/ToolChains/PS4CPU.cpp
+++ b/clang/lib/Driver/ToolChains/PS4CPU.cpp
@@ -265,6 +265,8 @@ void tools::PS5cpu::Linker::ConstructJob(Compilation &C, const JobAction &JA,
     CmdArgs.push_back(D.getLTOMode() == LTOK_Thin ? "--lto=thin"
                                                   : "--lto=full");
 
+  AddLTOFlag("-emit-jump-table-sizes-section");
+
   if (UseJMC)
     AddLTOFlag("-enable-jmc-instrument");
 
@@ -483,6 +485,12 @@ void toolchains::PS4PS5Base::addClangTargetOptions(
     else
       CC1Args.push_back("-fvisibility-externs-nodllstorageclass=keep");
   }
+
+  // Enable jump table sizes section for PS5.
+  if (getTriple().isPS5()) {
+    CC1Args.push_back("-mllvm");
+    CC1Args.push_back("-emit-jump-table-sizes-section");
+  }
 }
 
 // PS4 toolchain.
diff --git a/clang/test/Driver/ps4-ps5-toolchain.c b/clang/test/Driver/ps4-ps5-toolchain.c
index 444e9df24714b..c9987c2b5758b 100644
--- a/clang/test/Driver/ps4-ps5-toolchain.c
+++ b/clang/test/Driver/ps4-ps5-toolchain.c
@@ -11,3 +11,8 @@
 // RUN: %clang %s -### -target x86_64-sie-ps5 -flto 2>&1 | FileCheck %s --check-prefix=LTO
 // LTO-NOT: error:
 // LTO-NOT: unable to pass LLVM bit-code
+
+// Verify that the jump table sizes section is enabled.
+// RUN: %clang %s -target x86_64-sie-ps5 -### 2>&1 | FileCheck -check-prefix=JUMPTABLESIZES %s
+// JUMPTABLESIZES: "-mllvm" "-emit-jump-table-sizes-section"
+// JUMPTABLESIZES: "-plugin-opt=-emit-jump-table-sizes-section"
diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst
index 74ca8cb0aa687..0e209f3fe5cc0 100644
--- a/llvm/docs/Extensions.rst
+++ b/llvm/docs/Extensions.rst
@@ -554,6 +554,12 @@ time. This section is generated when the compiler enables fat LTO. This section
 has the ``SHF_EXCLUDE`` flag so that it is stripped from the final executable
 or shared library.
 
+``SHT_LLVM_JT_SIZES`` Section (Jump table addresses and sizes)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This section stores pairs of (jump table address, number of entries).
+This information is useful for tools that need to statically reconstruct
+the control flow of executables.
+
 CodeView-Dependent
 ------------------
 
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index fb39bb4b10b37..7bec01688783d 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1121,6 +1121,7 @@ enum : unsigned {
   SHT_LLVM_BB_ADDR_MAP = 0x6fff4c0a,        // LLVM Basic Block Address Map.
   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.
   // 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/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index b64fe83959eb1..05624d2728bfd 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -107,6 +107,7 @@
 #include "llvm/Pass.h"
 #include "llvm/Remarks/RemarkStreamer.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
@@ -155,6 +156,11 @@ static cl::bits<PGOMapFeaturesEnum> PgoAnalysisMapFeatures(
         "Enable extended information within the SHT_LLVM_BB_ADDR_MAP that is "
         "extracted from PGO related analysis."));
 
+static cl::opt<bool> EmitJumpTableSizesSection(
+    "emit-jump-table-sizes-section",
+    cl::desc("Emit a section containing jump table addresses and sizes"),
+    cl::Hidden, cl::init(false));
+
 STATISTIC(EmittedInsts, "Number of machine instrs printed");
 
 char AsmPrinter::ID = 0;
@@ -2764,6 +2770,26 @@ void AsmPrinter::emitJumpTableInfo() {
     for (const MachineBasicBlock *MBB : JTBBs)
       emitJumpTableEntry(MJTI, MBB, JTI);
   }
+
+  if (EmitJumpTableSizesSection && !JT.empty()) {
+    MCSymbolELF *LinkedToSym = cast<MCSymbolELF>(CurrentFnSym);
+    int Flags = F.hasComdat() ? ELF::SHF_GROUP : 0;
+    StringRef GroupName = F.hasComdat() ? F.getComdat()->getName() : "";
+
+    MCSection *JumpTableSizesSection = OutContext.getELFSection(
+        ".debug_llvm_jump_table_sizes", ELF::SHT_LLVM_JT_SIZES, Flags, 0, GroupName,
+        F.hasComdat(), MCSection::NonUniqueID, LinkedToSym);
+
+    OutStreamer->switchSection(JumpTableSizesSection);
+
+    for (unsigned JTI = 0, E = JT.size(); JTI != E; ++JTI) {
+      const std::vector<MachineBasicBlock *> &JTBBs = JT[JTI].MBBs;
+      OutStreamer->emitSymbolValue(GetJTISymbol(JTI),
+                                   TM.getProgramPointerSize());
+      OutStreamer->emitIntValue(JTBBs.size(), TM.getProgramPointerSize());
+    }
+  }
+
   if (!JTInDiffSection)
     OutStreamer->emitDataRegion(MCDR_DataRegionEnd);
 }
diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
index e8a22d3defd6e..c4536441665fa 100644
--- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
@@ -677,6 +677,8 @@ bool ELFAsmParser::ParseSectionArguments(bool IsPush, SMLoc loc) {
       Type = ELF::SHT_LLVM_OFFLOADING;
     else if (TypeName == "llvm_lto")
       Type = ELF::SHT_LLVM_LTO;
+    else if (TypeName == "llvm_jt_sizes")
+      Type = ELF::SHT_LLVM_JT_SIZES;
     else if (TypeName.getAsInteger(0, Type))
       return TokError("unknown section type");
   }
diff --git a/llvm/lib/MC/MCSectionELF.cpp b/llvm/lib/MC/MCSectionELF.cpp
index 5cd6590fb626d..25e62b70b5e2a 100644
--- a/llvm/lib/MC/MCSectionELF.cpp
+++ b/llvm/lib/MC/MCSectionELF.cpp
@@ -172,6 +172,8 @@ void MCSectionELF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
     OS << "llvm_offloading";
   else if (Type == ELF::SHT_LLVM_LTO)
     OS << "llvm_lto";
+  else if (Type == ELF::SHT_LLVM_JT_SIZES)
+    OS << "llvm_jt_sizes";
   else
     OS << "0x" << Twine::utohexstr(Type);
 
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index e47a40b8715dd..c66736fb2c919 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -319,6 +319,7 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) {
     STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_BB_ADDR_MAP);
     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_GNU_ATTRIBUTES);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_HASH);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_verdef);
diff --git a/llvm/test/CodeGen/X86/jump-table-size-section.ll b/llvm/test/CodeGen/X86/jump-table-size-section.ll
new file mode 100644
index 0000000000000..4a259aecd72b6
--- /dev/null
+++ b/llvm/test/CodeGen/X86/jump-table-size-section.ll
@@ -0,0 +1,97 @@
+; RUN: llc %s -o - -emit-jump-table-sizes-section -verify-machineinstrs --relocation-model=pic | FileCheck --check-prefix=CHECK %s
+; RUN: llc %s -o - -verify-machineinstrs --relocation-model=pic | FileCheck --check-prefix=NOFLAG %s
+
+; This test verifies the jump table size section. Currently only enabled by default on the PS5 target.
+
+$foo1 = comdat any
+
+; Ensure proper comdat handling.
+define void @foo1(i32 %x, ptr %to) comdat {
+
+; CHECK-LABEL: foo1
+; CHECK:      .section        .debug_llvm_jump_table_sizes,"G",@llvm_jt_sizes,foo1,comdat
+; CHECK-NEXT: .quad   .LJTI0_0
+; CHECK-NEXT: .quad   6
+
+; NOFLAG-LABEL: foo1
+; NOFLAG-NOT: .section        .debug_llvm_jump_table_sizes
+
+entry:
+  switch i32 %x, label %default [
+    i32 0, label %bb0
+    i32 1, label %bb1
+    i32 2, label %bb2
+    i32 3, label %bb3
+    i32 4, label %bb4
+    i32 5, label %bb4
+  ]
+bb0:
+  store i32 0, ptr %to
+  br label %exit
+bb1:
+  store i32 1, ptr %to
+  br label %exit
+bb2:
+  store i32 2, ptr %to
+  br label %exit
+bb3:
+  store i32 3, ptr %to
+  br label %exit
+bb4:
+  store i32 4, ptr %to
+  br label %exit
+exit:
+  ret void
+default:
+  unreachable
+}
+
+define void @foo2(i32 %x, ptr %to) {
+
+; CHECK-LABEL: foo2
+; CHECK:      .section        .debug_llvm_jump_table_sizes
+; CHECK-NEXT: .quad   .LJTI1_0
+; CHECK-NEXT: .quad   5
+
+; NOFLAG-LABEL: foo2
+; NOFLAG-NOT: .section        .debug_llvm_jump_table_sizes
+
+entry:
+  switch i32 %x, label %default [
+    i32 0, label %bb0
+    i32 1, label %bb1
+    i32 2, label %bb2
+    i32 3, label %bb3
+    i32 4, label %bb4
+  ]
+bb0:
+  store i32 0, ptr %to
+  br label %exit
+bb1:
+  store i32 1, ptr %to
+  br label %exit
+bb2:
+  store i32 2, ptr %to
+  br label %exit
+bb3:
+  store i32 3, ptr %to
+  br label %exit
+bb4:
+  store i32 4, ptr %to
+  br label %exit
+exit:
+  ret void
+default:
+  unreachable
+}
+
+; Ensure that the section isn't produced if there is no jump table.
+
+define void @foo3(i32 %x, ptr %to) {
+
+; CHECK-LABEL:    foo3
+; CHECK-NOT:      .section        .debug_llvm_jump_table_sizes
+
+exit:
+  ret void
+}

@llvmbot
Copy link
Member

llvmbot commented Aug 5, 2024

@llvm/pr-subscribers-backend-x86

Author: Nabeel Omer (omern1)

Changes

This patch will make LLVM emit a jump table size section containing tuples of (jump table address, entry count) in object files. This section is useful for tools that need to statically reconstruct the control flow of executables.

The name of the new section is .debug_llvm_jump_table_sizes because that makes both llvm-strip and GNU strip remove it.

At the moment this is only enabled by default for the PS5 target.


Full diff: https://github.com/llvm/llvm-project/pull/101962.diff

9 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/PS4CPU.cpp (+8)
  • (modified) clang/test/Driver/ps4-ps5-toolchain.c (+5)
  • (modified) llvm/docs/Extensions.rst (+6)
  • (modified) llvm/include/llvm/BinaryFormat/ELF.h (+1)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (+26)
  • (modified) llvm/lib/MC/MCParser/ELFAsmParser.cpp (+2)
  • (modified) llvm/lib/MC/MCSectionELF.cpp (+2)
  • (modified) llvm/lib/Object/ELF.cpp (+1)
  • (added) llvm/test/CodeGen/X86/jump-table-size-section.ll (+97)
diff --git a/clang/lib/Driver/ToolChains/PS4CPU.cpp b/clang/lib/Driver/ToolChains/PS4CPU.cpp
index a9e612c44da06..f9a9e995fff8e 100644
--- a/clang/lib/Driver/ToolChains/PS4CPU.cpp
+++ b/clang/lib/Driver/ToolChains/PS4CPU.cpp
@@ -265,6 +265,8 @@ void tools::PS5cpu::Linker::ConstructJob(Compilation &C, const JobAction &JA,
     CmdArgs.push_back(D.getLTOMode() == LTOK_Thin ? "--lto=thin"
                                                   : "--lto=full");
 
+  AddLTOFlag("-emit-jump-table-sizes-section");
+
   if (UseJMC)
     AddLTOFlag("-enable-jmc-instrument");
 
@@ -483,6 +485,12 @@ void toolchains::PS4PS5Base::addClangTargetOptions(
     else
       CC1Args.push_back("-fvisibility-externs-nodllstorageclass=keep");
   }
+
+  // Enable jump table sizes section for PS5.
+  if (getTriple().isPS5()) {
+    CC1Args.push_back("-mllvm");
+    CC1Args.push_back("-emit-jump-table-sizes-section");
+  }
 }
 
 // PS4 toolchain.
diff --git a/clang/test/Driver/ps4-ps5-toolchain.c b/clang/test/Driver/ps4-ps5-toolchain.c
index 444e9df24714b..c9987c2b5758b 100644
--- a/clang/test/Driver/ps4-ps5-toolchain.c
+++ b/clang/test/Driver/ps4-ps5-toolchain.c
@@ -11,3 +11,8 @@
 // RUN: %clang %s -### -target x86_64-sie-ps5 -flto 2>&1 | FileCheck %s --check-prefix=LTO
 // LTO-NOT: error:
 // LTO-NOT: unable to pass LLVM bit-code
+
+// Verify that the jump table sizes section is enabled.
+// RUN: %clang %s -target x86_64-sie-ps5 -### 2>&1 | FileCheck -check-prefix=JUMPTABLESIZES %s
+// JUMPTABLESIZES: "-mllvm" "-emit-jump-table-sizes-section"
+// JUMPTABLESIZES: "-plugin-opt=-emit-jump-table-sizes-section"
diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst
index 74ca8cb0aa687..0e209f3fe5cc0 100644
--- a/llvm/docs/Extensions.rst
+++ b/llvm/docs/Extensions.rst
@@ -554,6 +554,12 @@ time. This section is generated when the compiler enables fat LTO. This section
 has the ``SHF_EXCLUDE`` flag so that it is stripped from the final executable
 or shared library.
 
+``SHT_LLVM_JT_SIZES`` Section (Jump table addresses and sizes)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This section stores pairs of (jump table address, number of entries).
+This information is useful for tools that need to statically reconstruct
+the control flow of executables.
+
 CodeView-Dependent
 ------------------
 
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index fb39bb4b10b37..7bec01688783d 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1121,6 +1121,7 @@ enum : unsigned {
   SHT_LLVM_BB_ADDR_MAP = 0x6fff4c0a,        // LLVM Basic Block Address Map.
   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.
   // 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/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index b64fe83959eb1..05624d2728bfd 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -107,6 +107,7 @@
 #include "llvm/Pass.h"
 #include "llvm/Remarks/RemarkStreamer.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
@@ -155,6 +156,11 @@ static cl::bits<PGOMapFeaturesEnum> PgoAnalysisMapFeatures(
         "Enable extended information within the SHT_LLVM_BB_ADDR_MAP that is "
         "extracted from PGO related analysis."));
 
+static cl::opt<bool> EmitJumpTableSizesSection(
+    "emit-jump-table-sizes-section",
+    cl::desc("Emit a section containing jump table addresses and sizes"),
+    cl::Hidden, cl::init(false));
+
 STATISTIC(EmittedInsts, "Number of machine instrs printed");
 
 char AsmPrinter::ID = 0;
@@ -2764,6 +2770,26 @@ void AsmPrinter::emitJumpTableInfo() {
     for (const MachineBasicBlock *MBB : JTBBs)
       emitJumpTableEntry(MJTI, MBB, JTI);
   }
+
+  if (EmitJumpTableSizesSection && !JT.empty()) {
+    MCSymbolELF *LinkedToSym = cast<MCSymbolELF>(CurrentFnSym);
+    int Flags = F.hasComdat() ? ELF::SHF_GROUP : 0;
+    StringRef GroupName = F.hasComdat() ? F.getComdat()->getName() : "";
+
+    MCSection *JumpTableSizesSection = OutContext.getELFSection(
+        ".debug_llvm_jump_table_sizes", ELF::SHT_LLVM_JT_SIZES, Flags, 0, GroupName,
+        F.hasComdat(), MCSection::NonUniqueID, LinkedToSym);
+
+    OutStreamer->switchSection(JumpTableSizesSection);
+
+    for (unsigned JTI = 0, E = JT.size(); JTI != E; ++JTI) {
+      const std::vector<MachineBasicBlock *> &JTBBs = JT[JTI].MBBs;
+      OutStreamer->emitSymbolValue(GetJTISymbol(JTI),
+                                   TM.getProgramPointerSize());
+      OutStreamer->emitIntValue(JTBBs.size(), TM.getProgramPointerSize());
+    }
+  }
+
   if (!JTInDiffSection)
     OutStreamer->emitDataRegion(MCDR_DataRegionEnd);
 }
diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
index e8a22d3defd6e..c4536441665fa 100644
--- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
@@ -677,6 +677,8 @@ bool ELFAsmParser::ParseSectionArguments(bool IsPush, SMLoc loc) {
       Type = ELF::SHT_LLVM_OFFLOADING;
     else if (TypeName == "llvm_lto")
       Type = ELF::SHT_LLVM_LTO;
+    else if (TypeName == "llvm_jt_sizes")
+      Type = ELF::SHT_LLVM_JT_SIZES;
     else if (TypeName.getAsInteger(0, Type))
       return TokError("unknown section type");
   }
diff --git a/llvm/lib/MC/MCSectionELF.cpp b/llvm/lib/MC/MCSectionELF.cpp
index 5cd6590fb626d..25e62b70b5e2a 100644
--- a/llvm/lib/MC/MCSectionELF.cpp
+++ b/llvm/lib/MC/MCSectionELF.cpp
@@ -172,6 +172,8 @@ void MCSectionELF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
     OS << "llvm_offloading";
   else if (Type == ELF::SHT_LLVM_LTO)
     OS << "llvm_lto";
+  else if (Type == ELF::SHT_LLVM_JT_SIZES)
+    OS << "llvm_jt_sizes";
   else
     OS << "0x" << Twine::utohexstr(Type);
 
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index e47a40b8715dd..c66736fb2c919 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -319,6 +319,7 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) {
     STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_BB_ADDR_MAP);
     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_GNU_ATTRIBUTES);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_HASH);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_verdef);
diff --git a/llvm/test/CodeGen/X86/jump-table-size-section.ll b/llvm/test/CodeGen/X86/jump-table-size-section.ll
new file mode 100644
index 0000000000000..4a259aecd72b6
--- /dev/null
+++ b/llvm/test/CodeGen/X86/jump-table-size-section.ll
@@ -0,0 +1,97 @@
+; RUN: llc %s -o - -emit-jump-table-sizes-section -verify-machineinstrs --relocation-model=pic | FileCheck --check-prefix=CHECK %s
+; RUN: llc %s -o - -verify-machineinstrs --relocation-model=pic | FileCheck --check-prefix=NOFLAG %s
+
+; This test verifies the jump table size section. Currently only enabled by default on the PS5 target.
+
+$foo1 = comdat any
+
+; Ensure proper comdat handling.
+define void @foo1(i32 %x, ptr %to) comdat {
+
+; CHECK-LABEL: foo1
+; CHECK:      .section        .debug_llvm_jump_table_sizes,"G",@llvm_jt_sizes,foo1,comdat
+; CHECK-NEXT: .quad   .LJTI0_0
+; CHECK-NEXT: .quad   6
+
+; NOFLAG-LABEL: foo1
+; NOFLAG-NOT: .section        .debug_llvm_jump_table_sizes
+
+entry:
+  switch i32 %x, label %default [
+    i32 0, label %bb0
+    i32 1, label %bb1
+    i32 2, label %bb2
+    i32 3, label %bb3
+    i32 4, label %bb4
+    i32 5, label %bb4
+  ]
+bb0:
+  store i32 0, ptr %to
+  br label %exit
+bb1:
+  store i32 1, ptr %to
+  br label %exit
+bb2:
+  store i32 2, ptr %to
+  br label %exit
+bb3:
+  store i32 3, ptr %to
+  br label %exit
+bb4:
+  store i32 4, ptr %to
+  br label %exit
+exit:
+  ret void
+default:
+  unreachable
+}
+
+define void @foo2(i32 %x, ptr %to) {
+
+; CHECK-LABEL: foo2
+; CHECK:      .section        .debug_llvm_jump_table_sizes
+; CHECK-NEXT: .quad   .LJTI1_0
+; CHECK-NEXT: .quad   5
+
+; NOFLAG-LABEL: foo2
+; NOFLAG-NOT: .section        .debug_llvm_jump_table_sizes
+
+entry:
+  switch i32 %x, label %default [
+    i32 0, label %bb0
+    i32 1, label %bb1
+    i32 2, label %bb2
+    i32 3, label %bb3
+    i32 4, label %bb4
+  ]
+bb0:
+  store i32 0, ptr %to
+  br label %exit
+bb1:
+  store i32 1, ptr %to
+  br label %exit
+bb2:
+  store i32 2, ptr %to
+  br label %exit
+bb3:
+  store i32 3, ptr %to
+  br label %exit
+bb4:
+  store i32 4, ptr %to
+  br label %exit
+exit:
+  ret void
+default:
+  unreachable
+}
+
+; Ensure that the section isn't produced if there is no jump table.
+
+define void @foo3(i32 %x, ptr %to) {
+
+; CHECK-LABEL:    foo3
+; CHECK-NOT:      .section        .debug_llvm_jump_table_sizes
+
+exit:
+  ret void
+}

Copy link

github-actions bot commented Aug 5, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@omern1 omern1 requested a review from adibiagio August 5, 2024 12:34
@aengelke
Copy link
Contributor

aengelke commented Aug 5, 2024

Is there a previous discussion (e.g., RFC) about this? Did you consider alternative approaches? (E.g., just a spontaneous idea, adding a specially named symbol for every jump table where the symbol size is the size of the jump table; entry size (4/8) could be encoded in the name like $jt4/$jt8 (naming just for illustration).)

I'm not a fan of .debug_llvm_jump_table_sizes or adding a new section for this, but feel free to ignore my opinion. :)

@compnerd
Copy link
Member

compnerd commented Aug 5, 2024

The .debug_ prefix is for DWARF content, is this being proposed to the DWARF standard?

@omern1
Copy link
Member Author

omern1 commented Aug 8, 2024

The .debug_ prefix is for DEARF content, is this being proposed to the DWARF standard?

No, this isn't being proposed for the DWARF standard. Though, searching through the DWARF standard I couldn't find anywhere saying that the .debug_ section name prefix is reserved for DWARF information. Similarly the SysV ABI and the ELF spec don't seem to say anything like that either. I suspect this might be a namespacing convention rather than a requirement.

I feel like there's a case for this prefix as GNU strip doesn't seem to strip sections with the .llvm_ prefix and we don't people to accidentally ship binaries with this section in them.

Also, the choice of .debug_llvm_ is specifically to avoid clashes with DWARF sections.

@MaskRay
Copy link
Member

MaskRay commented Aug 8, 2024

Conventionally, .debug_* sections are reserved for DWARF, even without official standards.
GNU strip's default behavior --strip-all, emulated by llvm-objcopy's --strip-all-gnu option, is roughly to remove non-SHF_ALLOC sections that are not debug.
The .llvm prefix holds no special meaning.

When designing new sections, we should not adapt to the --strip-all-gnu behavior.

Jump tables exhibit diverse structures. For instance, consider llvm/test/CodeGen/ARM/jump-table-tbh.ll and Mach-O's .data_region.

@omern1
Copy link
Member Author

omern1 commented Aug 12, 2024

Conventionally, .debug_* sections are reserved for DWARF, even without official standards.
GNU strip's default behavior --strip-all, emulated by llvm-objcopy's --strip-all-gnu option, is roughly to remove non-SHF_ALLOC sections that are not debug.
The .llvm prefix holds no special meaning.

When designing new sections, we should not adapt to the --strip-all-gnu behavior.

Thanks, it appears that you're suggesting removing the .debug_ bit from the name of the section, is that correct?

Jump tables exhibit diverse structures. For instance, consider llvm/test/CodeGen/ARM/jump-table-tbh.ll and Mach-O's .data_region.

Jump tables of type EK_Inline aren't handled in ASM printer so I think they should be kept out of the scope of this patch. I'll look into the Macho-O data region stuff.

@@ -2764,6 +2770,27 @@ void AsmPrinter::emitJumpTableInfo() {
for (const MachineBasicBlock *MBB : JTBBs)
emitJumpTableEntry(MJTI, MBB, JTI);
}

if (EmitJumpTableSizesSection && TM.getTargetTriple().isOSBinFormatELF() &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outside of flags and section name, this is pretty generic. Why not open this to all object fully formats?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added support for COFF (hopefully correctly). I'm completely unfamiliar with MachO so I suggest that we leave that for a followup.

@@ -0,0 +1,97 @@
; RUN: llc %s -o - -emit-jump-table-sizes-section -verify-machineinstrs --relocation-model=pic | FileCheck --check-prefix=CHECK %s
; RUN: llc %s -o - -verify-machineinstrs --relocation-model=pic | FileCheck --check-prefix=NOFLAG %s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will need explicit triples - these tests may be run where the default target is not PS5 and the default object file format is not ELF.

@omern1 omern1 requested a review from compnerd August 29, 2024 22:48
COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ |
COFF::IMAGE_SCN_LNK_COMDAT | COFF::IMAGE_SCN_MEM_DISCARDABLE,
F.getComdat()->getName(), COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE);
} else
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Braces here would be nice to match the other side.

COFF::IMAGE_SCN_MEM_READ |
COFF::IMAGE_SCN_MEM_DISCARDABLE);
} else {
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change this to an early return please?

@omern1 omern1 merged commit fb6c10d into llvm:main Sep 6, 2024
9 checks passed
@omern1 omern1 deleted the jtsizes-section branch September 6, 2024 12:41
@ruotongyu
Copy link

Hi, I really like this design and am trying to apply it to more architectures, such as x86. However, it seems that the jump table is not properly mapped to the instruction that references it. Do you have any suggestions on how to correctly associate the jump table with the corresponding assembly instruction? Please let me know if I’m misunderstanding anything.

@omern1
Copy link
Member Author

omern1 commented Feb 14, 2025

Hi @ruotongyu,

and am trying to apply it to more architectures, such as x86.

I'm not sure what you mean here, this patch is really architecture independent and already works on X86. I maybe be misunderstanding you here though.

Do you have any suggestions on how to correctly associate the jump table with the corresponding assembly instruction? Please let me know if I’m misunderstanding anything.

That's unfortunately something that I did not have time to do in this patch but #112606 is trying to achieve that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:X86 clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category llvm:binary-utilities mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants