[compiler-rt] [llvm] [LowerTypeTests] Add debug info to jump table entries (PR #194493)
Vitaly Buka via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 28 17:49:20 PDT 2026
https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/194493
>From ab781cb16986da32f2737d523c101e8410524dce Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Wed, 22 Apr 2026 20:43:52 -0700
Subject: [PATCH 1/7] [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: __ubsan_check_cfi_icall_jt at sanitizer/ubsan_interface.h:0
#1: c::c() (.cfi_jt) at sanitizer/ubsan_interface.h:0:0
#2: .cfi.jumptable.81 at sanitizer/ubsan_interface.h:0:0
```
---
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 71 +++++++++++++++++--
.../LowerTypeTests/aarch64-jumptable-dbg.ll | 20 ++++--
.../LowerTypeTests/x86-jumptable-dbg.ll | 40 ++++++++---
3 files changed, 115 insertions(+), 16 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 323df2a6a0abf..94131af57a6ff 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: __ubsan_check_cfi_icall_jt at sanitizer/ubsan_interface.h:0
+// frame #1: c::c() (.cfi_jt) at sanitizer/ubsan_interface.h:0: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(File, F->getName(), StringRef(), File,
+ 0, DIFnTy, 0, DINode::FlagArtificial,
+ DISubprogram::SPFlagDefinition);
+ F->setSubprogram(JTSP);
+
+ DILocation *JTLoc = DILocation::get(M.getContext(), 0, 0, JTSP);
+
+ DISubprogram *UbsanSP = DIB.createFunction(
+ File, "__ubsan_check_cfi_icall_jt", StringRef(), 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(
+ File, (FuncName + ".cfi_jt").str(), StringRef(), File, 0, DIFnTy, 0,
+ DINode::FlagArtificial, DISubprogram::SPFlagDefinition);
+
+ DILocation *EntryLoc = JTLoc;
+ EntryLoc = DILocation::get(M.getContext(), 0, 0, JumpSP, EntryLoc);
+ EntryLoc = DILocation::get(M.getContext(), 0, 0, UbsanSP, 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..836bff790967d 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,17 @@ 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: [[META4]], 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: "__ubsan_check_cfi_icall_jt", scope: [[META4]], 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: "f.cfi_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; AARCH64: [[META12]] = !DILocation(line: 0, scope: [[META6]])
+; AARCH64: [[DBG13]] = !DILocation(line: 0, scope: [[META9]], inlinedAt: [[META14:![0-9]+]])
+; AARCH64: [[META14]] = !DILocation(line: 0, scope: [[META15:![0-9]+]], inlinedAt: [[META12]])
+; AARCH64: [[META15]] = distinct !DISubprogram(name: "g.cfi_jt", scope: [[META4]], 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..b364207d0ddf5 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,34 @@ 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: [[META4]], 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: "__ubsan_check_cfi_icall_jt", scope: [[META4]], 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: "f.cfi_jt", scope: [[META4]], 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: [[META9]], inlinedAt: [[META14:![0-9]+]])
+; X86_32: [[META14]] = !DILocation(line: 0, scope: [[META15:![0-9]+]], inlinedAt: [[META12]])
+; X86_32: [[META15]] = distinct !DISubprogram(name: "g.cfi_jt", scope: [[META4]], 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: [[META4]], 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: "__ubsan_check_cfi_icall_jt", scope: [[META4]], 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: "f.cfi_jt", scope: [[META4]], 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: [[META9]], inlinedAt: [[META14:![0-9]+]])
+; X86_64: [[META14]] = !DILocation(line: 0, scope: [[META15:![0-9]+]], inlinedAt: [[META12]])
+; X86_64: [[META15]] = distinct !DISubprogram(name: "g.cfi_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
;.
>From 0baa02c3ee864048c15f64b1a9eba336901380b1 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Mon, 27 Apr 2026 17:10:42 -0700
Subject: [PATCH 2/7] [LowerTypeTests] Use DICompileUnit as scope for jump
table DISubprograms
Probably no difference from the File.
---
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 94131af57a6ff..37631534fe78f 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -1547,15 +1547,15 @@ createJumpTableDebugInfo(Function *F, ArrayRef<GlobalTypeMember *> Functions) {
DISubroutineType *DIFnTy = DIB.createSubroutineType(nullptr);
- DISubprogram *JTSP = DIB.createFunction(File, F->getName(), StringRef(), File,
- 0, DIFnTy, 0, DINode::FlagArtificial,
+ 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(
- File, "__ubsan_check_cfi_icall_jt", StringRef(), File, 0, DIFnTy, 0,
+ CU, "__ubsan_check_cfi_icall_jt", {}, File, 0, DIFnTy, 0,
DINode::FlagArtificial, DISubprogram::SPFlagDefinition);
SmallVector<DILocation *> Locations;
@@ -1565,7 +1565,7 @@ createJumpTableDebugInfo(Function *F, ArrayRef<GlobalTypeMember *> Functions) {
StringRef FuncName = Func->getGlobal()->getName();
FuncName.consume_back(".cfi");
DISubprogram *JumpSP = DIB.createFunction(
- File, (FuncName + ".cfi_jt").str(), StringRef(), File, 0, DIFnTy, 0,
+ CU, (FuncName + ".cfi_jt").str(), {}, File, 0, DIFnTy, 0,
DINode::FlagArtificial, DISubprogram::SPFlagDefinition);
DILocation *EntryLoc = JTLoc;
>From fe5d78b247e2e648cd35de1a3dec296fe56d6b93 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 28 Apr 2026 01:02:23 -0700
Subject: [PATCH 3/7] [LowerTypeTests] Swap order of jump table debug info
frames
This it would be easier to make consistent runtime behavior.
---
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 6 ++---
.../LowerTypeTests/aarch64-jumptable-dbg.ll | 11 +++++-----
.../LowerTypeTests/x86-jumptable-dbg.ll | 22 +++++++++----------
3 files changed, 18 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 37631534fe78f..8bc63dcc260f7 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -1525,8 +1525,8 @@ Triple::ArchType LowerTypeTestsModule::selectJumpTableArmEncoding(
}
// Create location for each function entry which should look like this:
-// frame #0: __ubsan_check_cfi_icall_jt at sanitizer/ubsan_interface.h:0
-// frame #1: c::c() (.cfi_jt) at sanitizer/ubsan_interface.h:0:0
+// 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) {
@@ -1569,8 +1569,8 @@ createJumpTableDebugInfo(Function *F, ArrayRef<GlobalTypeMember *> Functions) {
DINode::FlagArtificial, DISubprogram::SPFlagDefinition);
DILocation *EntryLoc = JTLoc;
- EntryLoc = DILocation::get(M.getContext(), 0, 0, JumpSP, EntryLoc);
EntryLoc = DILocation::get(M.getContext(), 0, 0, UbsanSP, EntryLoc);
+ EntryLoc = DILocation::get(M.getContext(), 0, 0, JumpSP, EntryLoc);
Locations.push_back(EntryLoc);
}
diff --git a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
index 836bff790967d..1c4df8b387e21 100644
--- a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
@@ -71,14 +71,13 @@ define i1 @foo(ptr %p) {
; 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: [[META4]], file: [[META4]], type: [[META7:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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: "__ubsan_check_cfi_icall_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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: "f.cfi_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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: [[META9]], inlinedAt: [[META14:![0-9]+]])
-; AARCH64: [[META14]] = !DILocation(line: 0, scope: [[META15:![0-9]+]], inlinedAt: [[META12]])
-; AARCH64: [[META15]] = distinct !DISubprogram(name: "g.cfi_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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 b364207d0ddf5..39b7f853fabc6 100644
--- a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
@@ -98,16 +98,15 @@ define i1 @foo(ptr %p) {
; 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: [[META4]], file: [[META4]], type: [[META7:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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: "__ubsan_check_cfi_icall_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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: "f.cfi_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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: [[META9]], inlinedAt: [[META14:![0-9]+]])
-; X86_32: [[META14]] = !DILocation(line: 0, scope: [[META15:![0-9]+]], inlinedAt: [[META12]])
-; X86_32: [[META15]] = distinct !DISubprogram(name: "g.cfi_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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}
@@ -115,14 +114,13 @@ define i1 @foo(ptr %p) {
; 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: [[META4]], file: [[META4]], type: [[META7:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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: "__ubsan_check_cfi_icall_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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: "f.cfi_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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: [[META9]], inlinedAt: [[META14:![0-9]+]])
-; X86_64: [[META14]] = !DILocation(line: 0, scope: [[META15:![0-9]+]], inlinedAt: [[META12]])
-; X86_64: [[META15]] = distinct !DISubprogram(name: "g.cfi_jt", scope: [[META4]], file: [[META4]], type: [[META7]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]])
+; 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]])
;.
>From f62f2a2e3bf93fd26b4a594a997640b351f4f09f Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 28 Apr 2026 01:04:19 -0700
Subject: [PATCH 4/7] [cfi] Update test expectations for diagnostic output
---
compiler-rt/test/cfi/cross-dso/icall/diag.cpp | 2 +-
compiler-rt/test/cfi/cross-dso/icall/icall-from-dso.cpp | 2 +-
compiler-rt/test/cfi/cross-dso/icall/icall.cpp | 2 +-
compiler-rt/test/cfi/mfcall.cpp | 4 ++--
4 files changed, 5 insertions(+), 5 deletions(-)
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':
>From 186f5f452b181663419a8e923407d901dff7fc3f Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 28 Apr 2026 01:04:39 -0700
Subject: [PATCH 5/7] [ubsan] Filter out artificial debug locations in CFI
---
compiler-rt/lib/ubsan/ubsan_handlers.cpp | 29 +++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
index 63319f46734a4..976a014e3df88 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -855,6 +855,31 @@ 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;
+ uptr FileLen = internal_strlen(File);
+ uptr SuffixLen = internal_strlen(kSuffix);
+ if (FileLen < SuffixLen)
+ return false;
+ return internal_strcmp(File + FileLen - SuffixLen, 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 *removeArtificialFiles(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 +900,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(
+ removeArtificialFiles(getSymbolizedLocation(Function)));
+
const char *FName = FLoc.get()->info.function;
if (!FName)
FName = "(unknown)";
>From fc63acc7b2f716d26bce468fc4534b8911f72493 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 28 Apr 2026 17:07:18 -0700
Subject: [PATCH 6/7] [ubsan] Improve comments for artificial debug locations
---
compiler-rt/lib/ubsan/ubsan_handlers.cpp | 11 ++++++-----
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 3 ++-
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
index 976a014e3df88..2614d49bf238b 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -855,8 +855,8 @@ void __ubsan::__ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data,
Die();
}
-// Returns true if this is a special location created by
-// `LowerTypeTestsModule::createJumpTable`.
+// Returns true if this is an artificial debug location created by the
+// LowerTypeTests pass (see createJumpTableDebugInfo in LLVM).
static bool isArtificialStack(const SymbolizedStack *S) {
static constexpr char kSuffix[] = "ubsan_interface.h";
if (!S || !S->info.function || !S->info.file)
@@ -869,9 +869,10 @@ static bool isArtificialStack(const SymbolizedStack *S) {
return internal_strcmp(File + FileLen - SuffixLen, 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.
+// Stripping the file name from artificial frames forces the UBSan Diag
+// to fall back to module names. This preserves the original behavior
+// of showing the module, while still allowing the symbolizer
+// to include the helpful (.cfi_jt) function suffix.
static SymbolizedStack *removeArtificialFiles(SymbolizedStack *FS) {
for (SymbolizedStack *S = FS; S; S = S->next) {
if (isArtificialStack(S))
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 8bc63dcc260f7..56b4fc357ead1 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -1539,7 +1539,8 @@ createJumpTableDebugInfo(Function *F, ArrayRef<GlobalTypeMember *> Functions) {
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.
+ // Synthetic module (like ld-temp.o), it frequently lacks a DICompileUnit
+ // even if the rest of the program has debug info.
CU = DIB.createCompileUnit(
DISourceLanguageName(dwarf::DW_LANG_C), File, "llvm", true, "", 0, "",
DICompileUnit::DebugEmissionKind::LineTablesOnly);
>From 3a59f6baa67a84303ca08cb501108f532da81694 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 28 Apr 2026 17:48:48 -0700
Subject: [PATCH 7/7] [LowerTypeTests] Reuse UbsanLoc in
createJumpTableDebugInfo
---
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 56b4fc357ead1..7771ce4e488e9 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -1559,6 +1559,8 @@ createJumpTableDebugInfo(Function *F, ArrayRef<GlobalTypeMember *> Functions) {
CU, "__ubsan_check_cfi_icall_jt", {}, File, 0, DIFnTy, 0,
DINode::FlagArtificial, DISubprogram::SPFlagDefinition);
+ DILocation *UbsanLoc = DILocation::get(M.getContext(), 0, 0, UbsanSP, JTLoc);
+
SmallVector<DILocation *> Locations;
Locations.reserve(Functions.size());
@@ -1569,9 +1571,9 @@ createJumpTableDebugInfo(Function *F, ArrayRef<GlobalTypeMember *> Functions) {
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);
+ DILocation *EntryLoc =
+ DILocation::get(M.getContext(), 0, 0, JumpSP, UbsanLoc);
+
Locations.push_back(EntryLoc);
}
More information about the llvm-commits
mailing list