[compiler-rt] [llvm] [LowerTypeTests] Add debug info to jump table entries (PR #194493)
Vitaly Buka via llvm-commits
llvm-commits at lists.llvm.org
Fri May 1 16:04:31 PDT 2026
https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/194493
>From 29c098d4cbaab03e263677d1ccd77f3bb3e3acb3 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 1 May 2026 15:46:27 -0700
Subject: [PATCH 1/9] [NFC][LowerTypeTests] Pre-commit tests with debug info
kill switch
Pull Request: https://github.com/llvm/llvm-project/pull/195373
---
.../LowerTypeTests/aarch64-jumptable-dbg.ll | 39 +++++++++++++++++++
.../LowerTypeTests/x86-jumptable-dbg.ll | 38 ++++++++++++++++++
2 files changed, 77 insertions(+)
diff --git a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
index c10f16bf1f4d3..ce7d44085e488 100644
--- a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
@@ -1,5 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --include-generated-funcs
; RUN: opt -S -passes=lowertypetests -mtriple=aarch64-unknown-linux-gnu %s | FileCheck --check-prefixes=AARCH64 %s
+; RUN: opt -S -passes=lowertypetests -mtriple=aarch64-unknown-linux-gnu %s | FileCheck --check-prefixes=AARCH64-OFF %s
; Test for the jump table generation with branch protection on AArch64
@@ -37,6 +38,11 @@ define i1 @foo(ptr %p) {
; AARCH64: @f = alias [8 x i8], ptr @.cfi.jumptable
; AARCH64: @g = internal alias [8 x i8], getelementptr inbounds ([2 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 1)
;.
+; AARCH64-OFF: @[[GLOB0:[0-9]+]] = private unnamed_addr constant [2 x ptr] [ptr @f, ptr @g], align 16
+; AARCH64-OFF: @[[GLOB1:[0-9]+]] = private constant [0 x i8] zeroinitializer
+; AARCH64-OFF: @f = alias [8 x i8], ptr @.cfi.jumptable
+; AARCH64-OFF: @g = internal alias [8 x i8], getelementptr inbounds ([2 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 1)
+;.
; AARCH64-LABEL: @f.cfi(
; AARCH64-NEXT: ret void
;
@@ -60,13 +66,46 @@ define i1 @foo(ptr %p) {
; AARCH64-NEXT: call void asm sideeffect "bti c\0Ab $0\0A", "s"(ptr @g.cfi)
; AARCH64-NEXT: unreachable
;
+;
+; AARCH64-OFF-LABEL: @f.cfi(
+; AARCH64-OFF-NEXT: ret void
+;
+;
+; AARCH64-OFF-LABEL: @g.cfi(
+; AARCH64-OFF-NEXT: ret void
+;
+;
+; AARCH64-OFF-LABEL: @foo(
+; AARCH64-OFF-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P:%.*]] to i64
+; AARCH64-OFF-NEXT: [[TMP2:%.*]] = sub i64 ptrtoint (ptr getelementptr (i8, ptr @.cfi.jumptable, i64 8) to i64), [[TMP1]]
+; AARCH64-OFF-NEXT: [[TMP3:%.*]] = call i64 @llvm.fshr.i64(i64 [[TMP2]], i64 [[TMP2]], i64 3)
+; AARCH64-OFF-NEXT: [[TMP4:%.*]] = icmp ule i64 [[TMP3]], 1
+; AARCH64-OFF-NEXT: ret i1 [[TMP4]]
+;
+;
+; AARCH64-OFF: Function Attrs: naked noinline
+; AARCH64-OFF-LABEL: @.cfi.jumptable(
+; AARCH64-OFF-NEXT: entry:
+; AARCH64-OFF-NEXT: call void asm sideeffect "bti c\0Ab $0\0A", "s"(ptr @f.cfi)
+; AARCH64-OFF-NEXT: call void asm sideeffect "bti c\0Ab $0\0A", "s"(ptr @g.cfi)
+; AARCH64-OFF-NEXT: unreachable
+;
;.
; AARCH64: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
; AARCH64: attributes #[[ATTR1:[0-9]+]] = { naked noinline }
; AARCH64: attributes #[[ATTR2:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
;.
+; AARCH64-OFF: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+; AARCH64-OFF: attributes #[[ATTR1:[0-9]+]] = { naked noinline }
+; AARCH64-OFF: attributes #[[ATTR2:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
+;.
; 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-OFF: [[META0:![0-9]+]] = !{i32 4, !"branch-target-enforcement", i32 1}
+; AARCH64-OFF: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 5}
+; AARCH64-OFF: [[META2:![0-9]+]] = !{i32 2, !"Debug Info Version", i32 3}
+; AARCH64-OFF: [[META3:![0-9]+]] = !{i32 0, !"typeid1"}
+;.
diff --git a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
index d21ca284f911e..3d925fc3182d0 100644
--- a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
@@ -2,6 +2,7 @@
;; Test jump table generation with Indirect Branch Tracking on x86.
; RUN: opt -S -passes=lowertypetests -mtriple=i686 %s | FileCheck --check-prefixes=X86_32 %s
; RUN: opt -S -passes=lowertypetests -mtriple=x86_64 %s | FileCheck --check-prefixes=X86_64 %s
+; RUN: opt -S -passes=lowertypetests -mtriple=x86_64 %s | FileCheck --check-prefixes=X86_64-OFF %s
@0 = private unnamed_addr constant [2 x ptr] [ptr @f, ptr @g], align 16
@@ -38,6 +39,11 @@ define i1 @foo(ptr %p) {
; X86_64: @f = alias [16 x i8], ptr @.cfi.jumptable
; X86_64: @g = internal alias [16 x i8], getelementptr inbounds ([2 x [16 x i8]], ptr @.cfi.jumptable, i64 0, i64 1)
;.
+; X86_64-OFF: @[[GLOB0:[0-9]+]] = private unnamed_addr constant [2 x ptr] [ptr @f, ptr @g], align 16
+; X86_64-OFF: @[[GLOB1:[0-9]+]] = private constant [0 x i8] zeroinitializer
+; X86_64-OFF: @f = alias [16 x i8], ptr @.cfi.jumptable
+; X86_64-OFF: @g = internal alias [16 x i8], getelementptr inbounds ([2 x [16 x i8]], ptr @.cfi.jumptable, i64 0, i64 1)
+;.
; X86_32-LABEL: @f.cfi(
; X86_32-NEXT: ret void
;
@@ -83,6 +89,29 @@ define i1 @foo(ptr %p) {
; 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-OFF-LABEL: @f.cfi(
+; X86_64-OFF-NEXT: ret void
+;
+;
+; X86_64-OFF-LABEL: @g.cfi(
+; X86_64-OFF-NEXT: ret void
+;
+;
+; X86_64-OFF-LABEL: @foo(
+; X86_64-OFF-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P:%.*]] to i64
+; X86_64-OFF-NEXT: [[TMP2:%.*]] = sub i64 ptrtoint (ptr getelementptr (i8, ptr @.cfi.jumptable, i64 16) to i64), [[TMP1]]
+; X86_64-OFF-NEXT: [[TMP3:%.*]] = call i64 @llvm.fshr.i64(i64 [[TMP2]], i64 [[TMP2]], i64 4)
+; X86_64-OFF-NEXT: [[TMP4:%.*]] = icmp ule i64 [[TMP3]], 1
+; X86_64-OFF-NEXT: ret i1 [[TMP4]]
+;
+;
+; X86_64-OFF-LABEL: @.cfi.jumptable(
+; X86_64-OFF-NEXT: entry:
+; X86_64-OFF-NEXT: call void asm sideeffect "endbr64\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @f.cfi)
+; X86_64-OFF-NEXT: call void asm sideeffect "endbr64\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @g.cfi)
+; X86_64-OFF-NEXT: unreachable
+;
;.
; X86_32: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
; X86_32: attributes #[[ATTR1:[0-9]+]] = { naked nocf_check noinline }
@@ -92,6 +121,10 @@ define i1 @foo(ptr %p) {
; X86_64: attributes #[[ATTR1:[0-9]+]] = { naked nocf_check noinline }
; X86_64: attributes #[[ATTR2:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
;.
+; X86_64-OFF: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+; X86_64-OFF: attributes #[[ATTR1:[0-9]+]] = { naked nocf_check noinline }
+; X86_64-OFF: attributes #[[ATTR2:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
+;.
; 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}
@@ -102,3 +135,8 @@ define i1 @foo(ptr %p) {
; X86_64: [[META2:![0-9]+]] = !{i32 2, !"Debug Info Version", i32 3}
; X86_64: [[META3:![0-9]+]] = !{i32 0, !"typeid1"}
;.
+; X86_64-OFF: [[META0:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 1}
+; X86_64-OFF: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 5}
+; X86_64-OFF: [[META2:![0-9]+]] = !{i32 2, !"Debug Info Version", i32 3}
+; X86_64-OFF: [[META3:![0-9]+]] = !{i32 0, !"typeid1"}
+;.
>From c8f10026e4298787c8fd0872f234ce413a50deaf 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 2/9] [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 ce7d44085e488..6eb9a827b733d 100644
--- a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
@@ -62,9 +62,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-OFF-LABEL: @f.cfi(
@@ -102,7 +102,19 @@ 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]])
;.
; AARCH64-OFF: [[META0:![0-9]+]] = !{i32 4, !"branch-target-enforcement", i32 1}
; AARCH64-OFF: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 5}
diff --git a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
index 3d925fc3182d0..56879b404f751 100644
--- a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
@@ -62,9 +62,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(
@@ -85,9 +85,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_64-OFF-LABEL: @f.cfi(
@@ -128,12 +128,36 @@ 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]])
;.
; X86_64-OFF: [[META0:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 1}
; X86_64-OFF: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 5}
>From 9dc29b2c6e87b4f5dcca8f07d759b62029682e7e 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 3/9] [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 ad750ffacb50b595c07e0c0ceeef80211b28b5ff 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 4/9] [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 6eb9a827b733d..952de8202adb0 100644
--- a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
@@ -105,16 +105,15 @@ 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]])
;.
; AARCH64-OFF: [[META0:![0-9]+]] = !{i32 4, !"branch-target-enforcement", i32 1}
; AARCH64-OFF: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 5}
diff --git a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
index 56879b404f751..9f895cee186fe 100644
--- a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
@@ -131,16 +131,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}
@@ -148,16 +147,15 @@ 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]])
;.
; X86_64-OFF: [[META0:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 1}
; X86_64-OFF: [[META1:![0-9]+]] = !{i32 7, !"Dwarf Version", i32 5}
>From ffda64feab9f621311d0ed5f6d5121f354f3f580 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 5/9] [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 38bcba2a21cae636b34c257e0d610a90f828952c 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 6/9] [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 8c67883f87fbc81656b70beef6e19af4c92f406f 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 7/9] [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 0b118a9c0c694b1d4acd6c44510bdbc169dce283 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 8/9] [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);
}
>From c29edd453942fcfb62118875970deb74d7d6d5ee Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 1 May 2026 15:44:22 -0700
Subject: [PATCH 9/9] killswitch
---
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 7 ++++++-
.../Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll | 2 +-
llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll | 2 +-
3 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 7771ce4e488e9..0b5841d4b9201 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -124,6 +124,11 @@ static cl::opt<std::string> ClWriteSummary(
cl::desc("Write summary to given YAML file after running pass"),
cl::Hidden);
+// FIXME: Remove in clang 24.
+static cl::opt<bool> EnableJumpTableDebugInfo(
+ "lowertypetests-jump-table-debug-info", cl::init(true), cl::Hidden,
+ cl::desc("Enable debug info generation for jump tables"));
+
bool BitSetInfo::containsGlobalOffset(uint64_t Offset) const {
if (Offset < ByteOffset)
return false;
@@ -1589,7 +1594,7 @@ void LowerTypeTestsModule::createJumpTable(
IRBuilder<> IRB(BB);
SmallVector<DILocation *> Locations;
- if (M.getDwarfVersion() != 0)
+ if (M.getDwarfVersion() != 0 && EnableJumpTableDebugInfo)
Locations = createJumpTableDebugInfo(F, Functions);
InlineAsm *JumpTableAsm = createJumpTableEntryAsm(JumpTableArch);
diff --git a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
index 952de8202adb0..0af4ac64979b5 100644
--- a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll
@@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --include-generated-funcs
; RUN: opt -S -passes=lowertypetests -mtriple=aarch64-unknown-linux-gnu %s | FileCheck --check-prefixes=AARCH64 %s
-; RUN: opt -S -passes=lowertypetests -mtriple=aarch64-unknown-linux-gnu %s | FileCheck --check-prefixes=AARCH64-OFF %s
+; RUN: opt -S -passes=lowertypetests -lowertypetests-jump-table-debug-info=0 -mtriple=aarch64-unknown-linux-gnu %s | FileCheck --check-prefixes=AARCH64-OFF %s
; Test for the jump table generation with branch protection on AArch64
diff --git a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
index 9f895cee186fe..28119812f4090 100644
--- a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
+++ b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll
@@ -2,7 +2,7 @@
;; Test jump table generation with Indirect Branch Tracking on x86.
; RUN: opt -S -passes=lowertypetests -mtriple=i686 %s | FileCheck --check-prefixes=X86_32 %s
; RUN: opt -S -passes=lowertypetests -mtriple=x86_64 %s | FileCheck --check-prefixes=X86_64 %s
-; RUN: opt -S -passes=lowertypetests -mtriple=x86_64 %s | FileCheck --check-prefixes=X86_64-OFF %s
+; RUN: opt -S -passes=lowertypetests -lowertypetests-jump-table-debug-info=0 -mtriple=x86_64 %s | FileCheck --check-prefixes=X86_64-OFF %s
@0 = private unnamed_addr constant [2 x ptr] [ptr @f, ptr @g], align 16
More information about the llvm-commits
mailing list