From c3b43ecb6a970e3248beaa90cfcfe6673d7e2703 Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Wed, 22 Apr 2026 20:43:52 -0700 Subject: [PATCH 1/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 +createJumpTableDebugInfo(Function *F, ArrayRef 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 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 Functions, Triple::ArchType JumpTableArch) { BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", F); IRBuilder<> IRB(BB); + SmallVector 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(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((*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 c0f23e075132dad42224be0f78d4ccd85dc4e1ab Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Mon, 27 Apr 2026 17:10:42 -0700 Subject: [PATCH 2/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 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 Locations; @@ -1565,7 +1565,7 @@ createJumpTableDebugInfo(Function *F, ArrayRef 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 df98cdc9a1bdf424ac88ea93bb52faf30f5c8fd7 Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Tue, 28 Apr 2026 01:02:23 -0700 Subject: [PATCH 3/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 createJumpTableDebugInfo(Function *F, ArrayRef Functions) { @@ -1569,8 +1569,8 @@ createJumpTableDebugInfo(Function *F, ArrayRef 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 1812cb9ab18c953c26e77d9993572b2e4d21d269 Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Tue, 28 Apr 2026 01:04:19 -0700 Subject: [PATCH 4/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::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(&S::f2))(); break; case 'c': From f928970d7d90f667eb64f923181859f16075cd41 Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Tue, 28 Apr 2026 01:04:39 -0700 Subject: [PATCH 5/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 a7c9f984770edbb86b9c73c48da5cf940103415f Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Tue, 28 Apr 2026 17:07:18 -0700 Subject: [PATCH 6/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 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 e55f32c44c7c8b1b0eff59837ff5c109372223fb Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Tue, 28 Apr 2026 17:48:48 -0700 Subject: [PATCH 7/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 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 Locations; Locations.reserve(Functions.size()); @@ -1569,9 +1571,9 @@ createJumpTableDebugInfo(Function *F, ArrayRef 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 657452b1dde61c550b70237221715e732065f4ba Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Fri, 1 May 2026 15:44:22 -0700 Subject: [PATCH 8/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 ClWriteSummary( cl::desc("Write summary to given YAML file after running pass"), cl::Hidden); +// FIXME: Remove in clang 24. +static cl::opt 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 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 From f993d0f182ee19560da515540b2d003d6f9036da Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Fri, 1 May 2026 18:21:04 -0700 Subject: [PATCH 9/9] [LowerTypeTests] Use __ubsan_check_cfi_icall_jt as top level subprogram --- llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 12 ++------ .../LowerTypeTests/aarch64-jumptable-dbg.ll | 14 ++++------ .../LowerTypeTests/x86-jumptable-dbg.ll | 28 ++++++++----------- 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp index 0b5841d4b9201..a57e0c59726a3 100644 --- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp +++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp @@ -1532,7 +1532,6 @@ Triple::ArchType LowerTypeTestsModule::selectJumpTableArmEncoding( // 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 createJumpTableDebugInfo(Function *F, ArrayRef Functions) { Module &M = *F->getParent(); @@ -1553,18 +1552,13 @@ createJumpTableDebugInfo(Function *F, ArrayRef Functions) { 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); - DILocation *UbsanLoc = DILocation::get(M.getContext(), 0, 0, UbsanSP, JTLoc); + F->setSubprogram(UbsanSP); + + DILocation *UbsanLoc = DILocation::get(M.getContext(), 0, 0, UbsanSP); SmallVector Locations; Locations.reserve(Functions.size()); diff --git a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll index 0af4ac64979b5..87dc305f8c527 100644 --- a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll +++ b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable-dbg.ll @@ -63,8 +63,8 @@ define i1 @foo(ptr %p) { ; AARCH64-LABEL: @.cfi.jumptable( ; AARCH64-NEXT: entry: ; 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-NEXT: call void asm sideeffect "bti c\0Ab $0\0A", "s"(ptr @g.cfi), !dbg [[DBG11:![0-9]+]] +; AARCH64-NEXT: unreachable, !dbg [[DBG11]] ; ; ; AARCH64-OFF-LABEL: @f.cfi( @@ -105,15 +105,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: null, file: [[META4]], type: [[META7:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]]) +; AARCH64: [[META6:![0-9]+]] = distinct !DISubprogram(name: "__ubsan_check_cfi_icall_jt", 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]]) +; AARCH64: [[META10]] = !DILocation(line: 0, scope: [[META6]]) +; AARCH64: [[DBG11]] = !DILocation(line: 0, scope: [[META12:![0-9]+]], inlinedAt: [[META10]]) +; AARCH64: [[META12]] = 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 28119812f4090..eee523c960707 100644 --- a/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll +++ b/llvm/test/Transforms/LowerTypeTests/x86-jumptable-dbg.ll @@ -63,8 +63,8 @@ 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), !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_32-NEXT: call void asm sideeffect "endbr32\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @g.cfi), !dbg [[DBG11:![0-9]+]] +; X86_32-NEXT: unreachable, !dbg [[DBG11]] ; ; ; X86_64-LABEL: @f.cfi( @@ -86,8 +86,8 @@ 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), !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-NEXT: call void asm sideeffect "endbr64\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0A", "s"(ptr @g.cfi), !dbg [[DBG11:![0-9]+]] +; X86_64-NEXT: unreachable, !dbg [[DBG11]] ; ; ; X86_64-OFF-LABEL: @f.cfi( @@ -131,15 +131,13 @@ 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: null, file: [[META4]], type: [[META7:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]]) +; X86_32: [[META6:![0-9]+]] = distinct !DISubprogram(name: "__ubsan_check_cfi_icall_jt", 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_32: [[META10]] = !DILocation(line: 0, scope: [[META6]]) +; X86_32: [[DBG11]] = !DILocation(line: 0, scope: [[META12:![0-9]+]], inlinedAt: [[META10]]) +; X86_32: [[META12]] = 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} @@ -147,15 +145,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: null, file: [[META4]], type: [[META7:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META3]]) +; X86_64: [[META6:![0-9]+]] = distinct !DISubprogram(name: "__ubsan_check_cfi_icall_jt", 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]]) +; X86_64: [[META10]] = !DILocation(line: 0, scope: [[META6]]) +; X86_64: [[DBG11]] = !DILocation(line: 0, scope: [[META12:![0-9]+]], inlinedAt: [[META10]]) +; X86_64: [[META12]] = 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}