Skip to content

[LowerTypeTests] Add debug info to jump table entries#194493

Merged
vitalybuka merged 9 commits into
llvm:mainfrom
vitalybuka:lower
May 2, 2026
Merged

[LowerTypeTests] Add debug info to jump table entries#194493
vitalybuka merged 9 commits into
llvm:mainfrom
vitalybuka:lower

Conversation

@vitalybuka

@vitalybuka vitalybuka commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

[LowerTypeTests] Add debug info to jump table entries (#192736)

When Control Flow Integrity (CFI) is enabled, jump tables are used to
redirect indirect calls. Previously, these jump table entries lacked
debug information, making it difficult for profilers and debuggers to
attribute execution time correctly.

Now stack trace, when stopped on jump table entry will looks like this:

#0: c::c() (.cfi_jt) at sanitizer/ubsan_interface.h:0:0
#1: __ubsan_check_cfi_icall_jt at sanitizer/ubsan_interface.h:0

Following up on previous attempts #192736 and #193670, this PR is
essentially #192736 but with the (.cfi_jt) and __ubsan_check_cfi_icall_jt
frames swapped. While the specific order of __ubsan_check_cfi_icall_jt
isn't strictly necessary, swapping them helps maintain existing diagnostics
behavior.
Additionally, the diagnostics must remove ubsan_interface.h to allow
for a fallback to printing the module name.
See "Commits" tab for details.

@github-actions

github-actions Bot commented Apr 27, 2026

Copy link
Copy Markdown

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

@github-actions

github-actions Bot commented Apr 28, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 201773 tests passed
  • 6452 tests skipped

✅ The build succeeded and all tests passed.

@github-actions

github-actions Bot commented Apr 28, 2026

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 135738 tests passed
  • 4560 tests skipped

✅ The build succeeded and all tests passed.

@vitalybuka
vitalybuka requested review from fmayer, pcc and thurstond April 28, 2026 23:32
@vitalybuka
vitalybuka marked this pull request as ready for review April 28, 2026 23:32
@llvmbot

llvmbot commented Apr 28, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-compiler-rt-sanitizer

@llvm/pr-subscribers-llvm-transforms

Author: Vitaly Buka (vitalybuka)

Changes

[LowerTypeTests] Add debug info to jump table entries (#192736)

When Control Flow Integrity (CFI) is enabled, jump tables are used to
redirect indirect calls. Previously, these jump table entries lacked
debug information, making it difficult for profilers and debuggers to
attribute execution time correctly.

Now stack trace, when stopped on jump table entry will looks like this:

#<!-- -->0: c::c() (.cfi_jt) at sanitizer/ubsan_interface.h:0:0
#<!-- -->1: __ubsan_check_cfi_icall_jt at sanitizer/ubsan_interface.h:0
#<!-- -->2: .cfi.jumptable.81 at sanitizer/ubsan_interface.h:0:0

Following up on previous attempts #192736 and #193670, this PR is
essentially #192736 but with the (.cfi_jt) and __ubsan_check_cfi_icall_jt
frames swapped. While the specific order of __ubsan_check_cfi_icall_jt
isn't strictly necessary, swapping them helps maintain existing diagnostics
behavior.
Additionally, the diagnostics must remove ubsan_interface.h to allow
for a fallback to printing the module name.
See "Commits" tab for details.


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

8 Files Affected:

  • (modified) compiler-rt/lib/ubsan/ubsan_handlers.cpp (+25-1)
  • (modified) compiler-rt/test/cfi/cross-dso/icall/diag.cpp (+1-1)
  • (modified) compiler-rt/test/cfi/cross-dso/icall/icall-from-dso.cpp (+1-1)
  • (modified) compiler-rt/test/cfi/cross-dso/icall/icall.cpp (+1-1)
  • (modified) compiler-rt/test/cfi/mfcall.cpp (+2-2)
  • (modified) llvm/lib/Transforms/IPO/LowerTypeTests.cpp (+67-4)
  • (modified) llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll (+15-4)
  • (modified) llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll (+30-8)
diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
index 63319f46734a4..4adcd4737b375 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -855,6 +855,28 @@ void __ubsan::__ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data,
   Die();
 }
 
+// Returns true if this is a special location created by
+// `LowerTypeTestsModule::createJumpTable`.
+static bool isArtificialStack(const SymbolizedStack *S) {
+  static constexpr char kSuffix[] = "ubsan_interface.h";
+  if (!S || !S->info.function || !S->info.file)
+    return false;
+  const char *File = S->info.file;
+  File += internal_strlen(File) - internal_strlen(kSuffix);
+  return internal_strcmp(File, kSuffix) == 0;
+}
+
+// Preserve behavior before introducing artificial debug location. It's was not
+// ideal, but better than printing in `ubsan_interface.h` as location. Drop file
+// name, so `Diag` will print a module name.
+static SymbolizedStack *filterOutArtifialStack(SymbolizedStack *FS) {
+  for (SymbolizedStack *S = FS; S; S = S->next) {
+    if (isArtificialStack(S))
+      S->info.file = nullptr;
+  }
+  return FS;
+}
+
 static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
                               ReportOptions Opts) {
   if (Data->CheckKind != CFITCK_ICall && Data->CheckKind != CFITCK_NVMFCall)
@@ -875,7 +897,9 @@ static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
        "control flow integrity check for type %0 failed during %1")
       << Data->Type << CheckKindStr;
 
-  SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
+  SymbolizedStackHolder FLoc(
+    filterOutArtifialStack(getSymbolizedLocation(Function)));
+
   const char *FName = FLoc.get()->info.function;
   if (!FName)
     FName = "(unknown)";
diff --git a/compiler-rt/test/cfi/cross-dso/icall/diag.cpp b/compiler-rt/test/cfi/cross-dso/icall/diag.cpp
index a8c7f36608980..fadbdefd066ce 100644
--- a/compiler-rt/test/cfi/cross-dso/icall/diag.cpp
+++ b/compiler-rt/test/cfi/cross-dso/icall/diag.cpp
@@ -113,7 +113,7 @@ int main(int argc, char *argv[]) {
   void *p;
   if (argv[1][0] == 'i') {
     // ICALL-DIAG: runtime error: control flow integrity check for type 'void *(int)' failed during indirect function call
-    // ICALL-DIAG-NEXT: dynamic.so+0x{{[[:xdigit:]]+}}): note: create_B() defined here
+    // ICALL-DIAG-NEXT: dynamic.so+0x{{[[:xdigit:]]+}}): note: create_B() {{(\(.cfi_jt\) )?}}defined here
     // ICALL-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during indirect function call
     p = ((void *(*)(int))create_B)(42);
   } else {
diff --git a/compiler-rt/test/cfi/cross-dso/icall/icall-from-dso.cpp b/compiler-rt/test/cfi/cross-dso/icall/icall-from-dso.cpp
index d9f128ddf1026..3364d54fdfadb 100644
--- a/compiler-rt/test/cfi/cross-dso/icall/icall-from-dso.cpp
+++ b/compiler-rt/test/cfi/cross-dso/icall/icall-from-dso.cpp
@@ -18,7 +18,7 @@ void f() {
   // CHECK: =2=
   fprintf(stderr, "=2=\n");
   // CFI-DIAG: runtime error: control flow integrity check for type 'void (int)' failed during indirect function call
-  // CFI-DIAG-NEXT: ({{.*}}exe+0x{{[[:xdigit:]]+}}): note: g() defined here
+  // CFI-DIAG-NEXT: ({{.*}}exe+0x{{[[:xdigit:]]+}}): note: g() {{(\(.cfi_jt\) )?}}defined here
   ((void (*)(int))g)(42); // UB here
   // CHECK-DIAG: =3=
   // CHECK-NOT: =3=
diff --git a/compiler-rt/test/cfi/cross-dso/icall/icall.cpp b/compiler-rt/test/cfi/cross-dso/icall/icall.cpp
index 479047a6b05d6..671d6f1d7fdb7 100644
--- a/compiler-rt/test/cfi/cross-dso/icall/icall.cpp
+++ b/compiler-rt/test/cfi/cross-dso/icall/icall.cpp
@@ -21,7 +21,7 @@ int main() {
   // CHECK: =2=
   fprintf(stderr, "=2=\n");
   // CFI-DIAG: runtime error: control flow integrity check for type 'void (int)' failed during indirect function call
-  // CFI-DIAG-NEXT: ({{.*}}dynamic.so+0x{{[[:xdigit:]]+}}): note: f() defined here
+  // CFI-DIAG-NEXT: ({{.*}}dynamic.so+0x{{[[:xdigit:]]+}}): note: f() {{(\(.cfi_jt\) )?}}defined here
   ((void (*)(int))f)(42); // UB here
   // CHECK-DIAG: =3=
   // CHECK-NOT: =3=
diff --git a/compiler-rt/test/cfi/mfcall.cpp b/compiler-rt/test/cfi/mfcall.cpp
index d4666df8d5333..7ed31e9cc0851 100644
--- a/compiler-rt/test/cfi/mfcall.cpp
+++ b/compiler-rt/test/cfi/mfcall.cpp
@@ -63,12 +63,12 @@ int main(int argc, char **argv) {
   switch (argv[1][0]) {
     case 'a':
       // A: runtime error: control flow integrity check for type 'int (S::*)()' failed during non-virtual pointer to member function call
-      // A: note: S::f1() defined here
+      // A: note: S::f1() {{(\(.cfi_jt\) )?}}defined here
       (s.*bitcast<S_int>(&S::f1))();
       break;
     case 'b':
       // B: runtime error: control flow integrity check for type 'int (T::*)()' failed during non-virtual pointer to member function call
-      // B: note: S::f2() defined here
+      // B: note: S::f2() {{(\(.cfi_jt\) )?}}defined here
       (t.*bitcast<T_int>(&S::f2))();
       break;
     case 'c':
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 323df2a6a0abf..8bc63dcc260f7 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -35,6 +35,7 @@
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constant.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/Function.h"
@@ -1523,12 +1524,71 @@ Triple::ArchType LowerTypeTestsModule::selectJumpTableArmEncoding(
   return ArmCount > ThumbCount ? Triple::arm : Triple::thumb;
 }
 
+// Create location for each function entry which should look like this:
+// frame #0: c::c() (.cfi_jt) at sanitizer/ubsan_interface.h:0:0
+// frame #1: __ubsan_check_cfi_icall_jt at sanitizer/ubsan_interface.h:0
+// frame #2: .cfi.jumptable.81 at sanitizer/ubsan_interface.h:0:0
+static SmallVector<DILocation *>
+createJumpTableDebugInfo(Function *F, ArrayRef<GlobalTypeMember *> Functions) {
+  Module &M = *F->getParent();
+  DICompileUnit *CU = nullptr;
+  auto CUs = M.debug_compile_units();
+  if (!CUs.empty())
+    CU = *CUs.begin();
+
+  DIBuilder DIB(M, /*AllowUnresolved=*/true, CU);
+  DIFile *File = DIB.createFile("ubsan_interface.h", "sanitizer");
+  if (!CU) {
+    // Even with debug info enabled it can be missing if not info yet.
+    CU = DIB.createCompileUnit(
+        DISourceLanguageName(dwarf::DW_LANG_C), File, "llvm", true, "", 0, "",
+        DICompileUnit::DebugEmissionKind::LineTablesOnly);
+  }
+
+  DISubroutineType *DIFnTy = DIB.createSubroutineType(nullptr);
+
+  DISubprogram *JTSP = DIB.createFunction(CU, F->getName(), {}, File, 0, DIFnTy,
+                                          0, DINode::FlagArtificial,
+                                          DISubprogram::SPFlagDefinition);
+  F->setSubprogram(JTSP);
+
+  DILocation *JTLoc = DILocation::get(M.getContext(), 0, 0, JTSP);
+
+  DISubprogram *UbsanSP = DIB.createFunction(
+      CU, "__ubsan_check_cfi_icall_jt", {}, File, 0, DIFnTy, 0,
+      DINode::FlagArtificial, DISubprogram::SPFlagDefinition);
+
+  SmallVector<DILocation *> Locations;
+  Locations.reserve(Functions.size());
+
+  for (auto *Func : Functions) {
+    StringRef FuncName = Func->getGlobal()->getName();
+    FuncName.consume_back(".cfi");
+    DISubprogram *JumpSP = DIB.createFunction(
+        CU, (FuncName + ".cfi_jt").str(), {}, File, 0, DIFnTy, 0,
+        DINode::FlagArtificial, DISubprogram::SPFlagDefinition);
+
+    DILocation *EntryLoc = JTLoc;
+    EntryLoc = DILocation::get(M.getContext(), 0, 0, UbsanSP, EntryLoc);
+    EntryLoc = DILocation::get(M.getContext(), 0, 0, JumpSP, EntryLoc);
+    Locations.push_back(EntryLoc);
+  }
+
+  DIB.finalize();
+
+  return Locations;
+}
+
 void LowerTypeTestsModule::createJumpTable(
     Function *F, ArrayRef<GlobalTypeMember *> Functions,
     Triple::ArchType JumpTableArch) {
   BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", F);
   IRBuilder<> IRB(BB);
 
+  SmallVector<DILocation *> Locations;
+  if (M.getDwarfVersion() != 0)
+    Locations = createJumpTableDebugInfo(F, Functions);
+
   InlineAsm *JumpTableAsm = createJumpTableEntryAsm(JumpTableArch);
 
   // Check if all entries have the NoUnwind attribute.
@@ -1536,12 +1596,15 @@ void LowerTypeTestsModule::createJumpTable(
   // cfi.jumptable as NoUnwind, otherwise, direct calls
   // to the jump table will not handle exceptions properly
   bool areAllEntriesNounwind = true;
-  for (GlobalTypeMember *GTM : Functions) {
-    if (!llvm::cast<llvm::Function>(GTM->getGlobal())
-             ->hasFnAttribute(llvm::Attribute::NoUnwind)) {
+  assert(Locations.empty() || Functions.size() == Locations.size());
+  for (auto [GTM, Loc] : zip_longest(Functions, Locations)) {
+    if (Loc.has_value())
+      IRB.SetCurrentDebugLocation(*Loc);
+    if (!cast<Function>((*GTM)->getGlobal())
+             ->hasFnAttribute(Attribute::NoUnwind)) {
       areAllEntriesNounwind = false;
     }
-    IRB.CreateCall(JumpTableAsm, GTM->getGlobal());
+    IRB.CreateCall(JumpTableAsm, (*GTM)->getGlobal());
   }
   IRB.CreateUnreachable();
 
diff --git a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
index c10f16bf1f4d3..1c4df8b387e21 100644
--- a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
@@ -56,9 +56,9 @@ define i1 @foo(ptr %p) {
 ; AARCH64: Function Attrs: naked noinline
 ; AARCH64-LABEL: @.cfi.jumptable(
 ; AARCH64-NEXT:  entry:
-; AARCH64-NEXT:    call void asm sideeffect "bti c\0Ab $0\0A", "s"(ptr @f.cfi)
-; AARCH64-NEXT:    call void asm sideeffect "bti c\0Ab $0\0A", "s"(ptr @g.cfi)
-; AARCH64-NEXT:    unreachable
+; AARCH64-NEXT:    call void asm sideeffect "bti c\0Ab $0\0A", "s"(ptr @f.cfi), !dbg [[DBG8:![0-9]+]]
+; AARCH64-NEXT:    call void asm sideeffect "bti c\0Ab $0\0A", "s"(ptr @g.cfi), !dbg [[DBG13:![0-9]+]]
+; AARCH64-NEXT:    unreachable, !dbg [[DBG13]]
 ;
 ;.
 ; AARCH64: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
@@ -68,5 +68,16 @@ define i1 @foo(ptr %p) {
 ; AARCH64: [[META0:![0-9]+]] = !{i32 4, !"branch-target-enforcement", i32 1}
 ; AARCH64: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 5}
 ; AARCH64: [[META2:![0-9]+]] = !{i32 2, !"Debug Info Version", i32 3}
-; AARCH64: [[META3:![0-9]+]] = !{i32 0, !"typeid1"}
+; AARCH64: [[META3:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C, file: [[META4:![0-9]+]], producer: "llvm", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly)
+; AARCH64: [[META4]] = !DIFile(filename: "{{.*}}ubsan_interface.h", directory: {{.*}})
+; AARCH64: [[META5:![0-9]+]] = !{i32 0, !"typeid1"}
+; AARCH64: [[META6:![0-9]+]] = distinct !DISubprogram(name: ".cfi.jumptable", scope: null, file: [[META4]], type: [[META7:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; AARCH64: [[META7]] = !DISubroutineType(types: null)
+; AARCH64: [[DBG8]] = !DILocation(line: 0, scope: [[META9:![0-9]+]], inlinedAt: [[META10:![0-9]+]])
+; AARCH64: [[META9]] = distinct !DISubprogram(name: "f.cfi_jt", scope: null, file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; AARCH64: [[META10]] = !DILocation(line: 0, scope: [[META11:![0-9]+]], inlinedAt: [[META12:![0-9]+]])
+; AARCH64: [[META11]] = distinct !DISubprogram(name: "__ubsan_check_cfi_icall_jt", scope: null, file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; AARCH64: [[META12]] = !DILocation(line: 0, scope: [[META6]])
+; AARCH64: [[DBG13]] = !DILocation(line: 0, scope: [[META14:![0-9]+]], inlinedAt: [[META10]])
+; AARCH64: [[META14]] = distinct !DISubprogram(name: "g.cfi_jt", scope: null, file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
 ;.
diff --git a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
index d21ca284f911e..39b7f853fabc6 100644
--- a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
@@ -56,9 +56,9 @@ define i1 @foo(ptr %p) {
 ;
 ; X86_32-LABEL: @.cfi.jumptable(
 ; X86_32-NEXT:  entry:
-; X86_32-NEXT:    call void asm sideeffect "endbr32\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @f.cfi)
-; X86_32-NEXT:    call void asm sideeffect "endbr32\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @g.cfi)
-; X86_32-NEXT:    unreachable
+; X86_32-NEXT:    call void asm sideeffect "endbr32\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @f.cfi), !dbg [[DBG8:![0-9]+]]
+; X86_32-NEXT:    call void asm sideeffect "endbr32\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @g.cfi), !dbg [[DBG13:![0-9]+]]
+; X86_32-NEXT:    unreachable, !dbg [[DBG13]]
 ;
 ;
 ; X86_64-LABEL: @f.cfi(
@@ -79,9 +79,9 @@ define i1 @foo(ptr %p) {
 ;
 ; X86_64-LABEL: @.cfi.jumptable(
 ; X86_64-NEXT:  entry:
-; X86_64-NEXT:    call void asm sideeffect "endbr64\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @f.cfi)
-; X86_64-NEXT:    call void asm sideeffect "endbr64\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @g.cfi)
-; X86_64-NEXT:    unreachable
+; X86_64-NEXT:    call void asm sideeffect "endbr64\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @f.cfi), !dbg [[DBG8:![0-9]+]]
+; X86_64-NEXT:    call void asm sideeffect "endbr64\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @g.cfi), !dbg [[DBG13:![0-9]+]]
+; X86_64-NEXT:    unreachable, !dbg [[DBG13]]
 ;
 ;.
 ; X86_32: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
@@ -95,10 +95,32 @@ define i1 @foo(ptr %p) {
 ; X86_32: [[META0:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 1}
 ; X86_32: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 5}
 ; X86_32: [[META2:![0-9]+]] = !{i32 2, !"Debug Info Version", i32 3}
-; X86_32: [[META3:![0-9]+]] = !{i32 0, !"typeid1"}
+; X86_32: [[META3:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C, file: [[META4:![0-9]+]], producer: "llvm", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly)
+; X86_32: [[META4]] = !DIFile(filename: "{{.*}}ubsan_interface.h", directory: {{.*}})
+; X86_32: [[META5:![0-9]+]] = !{i32 0, !"typeid1"}
+; X86_32: [[META6:![0-9]+]] = distinct !DISubprogram(name: ".cfi.jumptable", scope: null, file: [[META4]], type: [[META7:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; X86_32: [[META7]] = !DISubroutineType(types: null)
+; X86_32: [[DBG8]] = !DILocation(line: 0, scope: [[META9:![0-9]+]], inlinedAt: [[META10:![0-9]+]])
+; X86_32: [[META9]] = distinct !DISubprogram(name: "f.cfi_jt", scope: null, file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; X86_32: [[META10]] = !DILocation(line: 0, scope: [[META11:![0-9]+]], inlinedAt: [[META12:![0-9]+]])
+; X86_32: [[META11]] = distinct !DISubprogram(name: "__ubsan_check_cfi_icall_jt", scope: null, file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; X86_32: [[META12]] = !DILocation(line: 0, scope: [[META6]])
+; X86_32: [[DBG13]] = !DILocation(line: 0, scope: [[META14:![0-9]+]], inlinedAt: [[META10]])
+; X86_32: [[META14]] = distinct !DISubprogram(name: "g.cfi_jt", scope: null, file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
 ;.
 ; X86_64: [[META0:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 1}
 ; X86_64: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 5}
 ; X86_64: [[META2:![0-9]+]] = !{i32 2, !"Debug Info Version", i32 3}
-; X86_64: [[META3:![0-9]+]] = !{i32 0, !"typeid1"}
+; X86_64: [[META3:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C, file: [[META4:![0-9]+]], producer: "llvm", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly)
+; X86_64: [[META4]] = !DIFile(filename: "{{.*}}ubsan_interface.h", directory: {{.*}})
+; X86_64: [[META5:![0-9]+]] = !{i32 0, !"typeid1"}
+; X86_64: [[META6:![0-9]+]] = distinct !DISubprogram(name: ".cfi.jumptable", scope: null, file: [[META4]], type: [[META7:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; X86_64: [[META7]] = !DISubroutineType(types: null)
+; X86_64: [[DBG8]] = !DILocation(line: 0, scope: [[META9:![0-9]+]], inlinedAt: [[META10:![0-9]+]])
+; X86_64: [[META9]] = distinct !DISubprogram(name: "f.cfi_jt", scope: null, file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; X86_64: [[META10]] = !DILocation(line: 0, scope: [[META11:![0-9]+]], inlinedAt: [[META12:![0-9]+]])
+; X86_64: [[META11]] = distinct !DISubprogram(name: "__ubsan_check_cfi_icall_jt", scope: null, file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; X86_64: [[META12]] = !DILocation(line: 0, scope: [[META6]])
+; X86_64: [[DBG13]] = !DILocation(line: 0, scope: [[META14:![0-9]+]], inlinedAt: [[META10]])
+; X86_64: [[META14]] = distinct !DISubprogram(name: "g.cfi_jt", scope: null, file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
 ;.

@vitalybuka
vitalybuka force-pushed the lower branch 4 times, most recently from 45d6a81 to 186f5f4 Compare April 28, 2026 23:55
Comment thread compiler-rt/lib/ubsan/ubsan_handlers.cpp Outdated
Comment thread compiler-rt/lib/ubsan/ubsan_handlers.cpp Outdated
Comment thread llvm/lib/Transforms/IPO/LowerTypeTests.cpp Outdated
Comment thread llvm/lib/Transforms/IPO/LowerTypeTests.cpp
Comment thread llvm/lib/Transforms/IPO/LowerTypeTests.cpp
vitalybuka added 4 commits May 1, 2026 17:08
When Control Flow Integrity (CFI) is enabled, jump tables are used to
redirect indirect calls. Previously, these jump table entries lacked
debug information, making it difficult for profilers and debuggers to
attribute execution time correctly.

Now stack trace, when stopped on jump table entry will looks like this:
```
#0: __ubsan_check_cfi_icall_jt at sanitizer/ubsan_interface.h:0
#1: c::c() (.cfi_jt) at sanitizer/ubsan_interface.h:0:0
llvm#2: .cfi.jumptable.81 at sanitizer/ubsan_interface.h:0:0
```
This it would be easier to make consistent runtime behavior.
Comment thread llvm/lib/Transforms/IPO/LowerTypeTests.cpp Outdated
cpullvm-upstream-sync Bot pushed a commit to navaneethshan/cpullvm-toolchain-1 that referenced this pull request May 2, 2026
@vitalybuka
vitalybuka requested a review from pcc May 2, 2026 01:34
@vitalybuka
vitalybuka merged commit 82bf5e8 into llvm:main May 2, 2026
10 checks passed
@llvm-ci

llvm-ci commented May 2, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder openmp-s390x-linux running on systemz-1 while building compiler-rt,llvm at step 6 "test-openmp".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/88/builds/22984

Here is the relevant piece of the build log for the reference
Step 6 (test-openmp) failure: test (failure)
******************** TEST 'libomp :: tasking/issue-94260-2.c' FAILED ********************
Exit Code: -11

Command Output (stdout):
--
# RUN: at line 1
/home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/./bin/clang -fopenmp   -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test -L /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -fno-omit-frame-pointer -mbackchain -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/ompt /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic && /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp
# executed command: /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/./bin/clang -fopenmp -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test -L /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -fno-omit-frame-pointer -mbackchain -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/ompt /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic
# executed command: /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp
# note: command had no output on stdout or stderr
# error: command failed with exit status: -11

--

********************


enferex pushed a commit to enferex/llvm-project that referenced this pull request May 5, 2026
enferex pushed a commit to enferex/llvm-project that referenced this pull request May 5, 2026
[LowerTypeTests] Add debug info to jump table entries (llvm#192736)
    
When Control Flow Integrity (CFI) is enabled, jump tables are used to
redirect indirect calls. Previously, these jump table entries lacked
debug information, making it difficult for profilers and debuggers to
attribute execution time correctly.

Now stack trace, when stopped on jump table entry will looks like this:
```
#0: c::c() (.cfi_jt) at sanitizer/ubsan_interface.h:0:0
llvm#1: __ubsan_check_cfi_icall_jt at sanitizer/ubsan_interface.h:0
```

Following up on previous attempts llvm#192736 and llvm#193670, this PR is
essentially llvm#192736 but with the `(.cfi_jt)` and
`__ubsan_check_cfi_icall_jt`
frames swapped. While the specific order of `__ubsan_check_cfi_icall_jt`
isn't strictly necessary, swapping them helps maintain existing
diagnostics
behavior.
Additionally, the diagnostics must remove `ubsan_interface.h` to allow
for a fallback to printing the module name.
See "Commits" tab for details.
moar55 pushed a commit to moar55/llvm-project that referenced this pull request May 12, 2026
moar55 pushed a commit to moar55/llvm-project that referenced this pull request May 12, 2026
[LowerTypeTests] Add debug info to jump table entries (llvm#192736)
    
When Control Flow Integrity (CFI) is enabled, jump tables are used to
redirect indirect calls. Previously, these jump table entries lacked
debug information, making it difficult for profilers and debuggers to
attribute execution time correctly.

Now stack trace, when stopped on jump table entry will looks like this:
```
#0: c::c() (.cfi_jt) at sanitizer/ubsan_interface.h:0:0
#1: __ubsan_check_cfi_icall_jt at sanitizer/ubsan_interface.h:0
```

Following up on previous attempts llvm#192736 and llvm#193670, this PR is
essentially llvm#192736 but with the `(.cfi_jt)` and
`__ubsan_check_cfi_icall_jt`
frames swapped. While the specific order of `__ubsan_check_cfi_icall_jt`
isn't strictly necessary, swapping them helps maintain existing
diagnostics
behavior.
Additionally, the diagnostics must remove `ubsan_interface.h` to allow
for a fallback to printing the module name.
See "Commits" tab for details.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants