[llvm] [CodeView] Match MSVC implicit this records for Visual Studio (PR #214289)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 7 21:26:19 PDT 2026
https://github.com/GkvJwa updated https://github.com/llvm/llvm-project/pull/214289
>From 8526211c46864bd7a5efd142bc7720519c21f7fa Mon Sep 17 00:00:00 2001
From: GkvJwa <gkvjwa at gmail.com>
Date: Thu, 6 Aug 2026 16:40:38 +0800
Subject: [PATCH 1/3] [CodeView] Match MSVC implicit this records for Visual
Studio
The Visual Studio debugger does not resolve LLVM's S_LOCAL and S_DEFRANGE
representation for an implicit C++ `this` pointer.
MSVC emits this location as S_REGREL32 and provides DbgStart/DbgEnd in the
containing S_GPROC32_ID record. Match this representation for simple stack
locations so the Visual Studio debugger can resolve `this` while stepping
through functions such as constructors and inspect member variables through
it.
The debug range also prevents the debugger from using the location after
the function epilogue restores the stack pointer.
---
llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 140 ++++++++++++++++-
llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h | 12 ++
.../DebugInfo/COFF/implicit-this-regrel.ll | 147 ++++++++++++++++++
3 files changed, 295 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/DebugInfo/COFF/implicit-this-regrel.ll
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index 84ea5e349f01d..c4f26f7d7f058 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -111,6 +111,12 @@ class CVMCAdapter : public CodeViewRecordStreamer {
};
} // namespace
+static bool isImplicitThisVariable(const DILocalVariable *Var,
+ const DISubprogram *SP = nullptr) {
+ return Var && (!SP || Var->getScope() == SP) && Var->isParameter() &&
+ Var->isObjectPointer() && Var->getName() == "this";
+}
+
static CPUType mapArchToCVCPUType(Triple::ArchType Type) {
switch (Type) {
case Triple::ArchType::x86:
@@ -1167,10 +1173,17 @@ void CodeViewDebug::emitDebugInfoForFunction(const Function *GV,
// code is located and what's its size:
OS.AddComment("Code size");
OS.emitAbsoluteSymbolDiff(FI.End, Fn, 4);
+ const bool HasDebugRange = FI.DebugStart && FI.DebugEnd;
OS.AddComment("Offset after prologue");
- OS.emitInt32(0);
+ if (HasDebugRange)
+ OS.emitAbsoluteSymbolDiff(FI.DebugStart, Fn, 4);
+ else
+ OS.emitInt32(0);
OS.AddComment("Offset before epilogue");
- OS.emitInt32(0);
+ if (HasDebugRange)
+ OS.emitAbsoluteSymbolDiff(FI.DebugEnd, Fn, 4);
+ else
+ OS.emitInt32(0);
OS.AddComment("Function type index");
OS.emitInt32(getFuncIdForSubprogram(GV->getSubprogram()).getIndex());
OS.AddComment("Function section relative address");
@@ -1568,6 +1581,15 @@ void CodeViewDebug::beginFunctionImpl(const MachineFunction *MF) {
OS.emitCVFuncIdDirective(CurFn->FuncId);
+ const DISubprogram *SP = GV.getSubprogram();
+ const bool HasImplicitThis =
+ any_of(
+ MF->getVariableDbgInfo(),
+ [&](const auto &VI) { return isImplicitThisVariable(VI.Var, SP); }) ||
+ any_of(DbgValues, [&](const auto &I) {
+ return isImplicitThisVariable(cast<DILocalVariable>(I.first.first), SP);
+ });
+
// Find the end of the function prolog. First known non-DBG_VALUE and
// non-frame setup location marks the beginning of the function body.
// FIXME: is there a simpler a way to do this? Can we just search
@@ -1578,6 +1600,10 @@ void CodeViewDebug::beginFunctionImpl(const MachineFunction *MF) {
for (const auto &MI : MBB) {
if (!MI.isMetaInstruction() && !MI.getFlag(MachineInstr::FrameSetup) &&
MI.getDebugLoc()) {
+ if (HasImplicitThis && !CurFn->PrologueEnd) {
+ CurFn->PrologueEnd = &MI;
+ requestLabelBeforeInsn(&MI);
+ }
PrologEndLoc = MI.getDebugLoc();
break;
} else if (!MI.isMetaInstruction()) {
@@ -1602,6 +1628,25 @@ void CodeViewDebug::beginFunctionImpl(const MachineFunction *MF) {
}
}
+ if (HasImplicitThis) {
+ for (const auto &MBB : *MF) {
+ const MachineInstr *EpilogueBegin = nullptr;
+ for (const auto &MI : MBB) {
+ if (!MI.isMetaInstruction() && MI.getFlag(MachineInstr::FrameDestroy)) {
+ EpilogueBegin = &MI;
+ requestLabelBeforeInsn(EpilogueBegin);
+ break;
+ }
+ }
+ if (!EpilogueBegin)
+ continue;
+
+ MachineBasicBlock::const_iterator Last = MBB.getLastNonDebugInstr();
+ if (Last != MBB.end())
+ requestLabelAfterInsn(&*Last);
+ }
+ }
+
// Mark branches that may potentially be using jump tables with labels.
bool isThumb = MMI->getModule()->getTargetTriple().getArch() ==
llvm::Triple::ArchType::thumb;
@@ -2845,8 +2890,32 @@ void CodeViewDebug::emitLocalVariableList(const FunctionInfo &FI,
}
}
+bool CodeViewDebug::LocalVariable::isSimpleImplicitThis() const {
+ if (!isImplicitThisVariable(DIVar) || DefRanges.empty())
+ return false;
+
+ const auto &DefRange = DefRanges.begin()->first;
+ return DefRange.InMemory && !DefRange.IsSubfield &&
+ DefRange.DerefOffset == LocalVarDef::NoDeref;
+}
+
void CodeViewDebug::emitLocalVariable(const FunctionInfo &FI,
const LocalVariable &Var) {
+ if (FI.DebugStart && FI.DebugEnd && Var.isSimpleImplicitThis()) {
+ const auto &DefRange = Var.DefRanges.begin()->first;
+ MCSymbol *LocalEnd = beginSymbolRecord(SymbolKind::S_REGREL32);
+ OS.AddComment("Offset");
+ OS.emitInt32(DefRange.DataOffset);
+ OS.AddComment("TypeIndex");
+ OS.emitInt32(getCompleteTypeIndex(Var.DIVar->getType()).getIndex());
+ OS.AddComment("Register");
+ OS.emitInt16(DefRange.CVRegister);
+ OS.AddComment("Name");
+ emitNullTerminatedSymbolName(OS, Var.DIVar->getName());
+ endSymbolRecord(LocalEnd);
+ return;
+ }
+
// LocalSym record, see SymbolRecord.h for more info.
MCSymbol *LocalEnd = beginSymbolRecord(SymbolKind::S_LOCAL);
@@ -2871,7 +2940,26 @@ void CodeViewDebug::emitLocalVariable(const FunctionInfo &FI,
SmallString<20> BytePrefix;
for (const auto &Pair : Var.DefRanges) {
LocalVarDef DefRange = Pair.first;
- const auto &Ranges = Pair.second;
+ using Range = std::pair<const MCSymbol *, const MCSymbol *>;
+ ArrayRef<Range> Ranges = Pair.second;
+ SmallVector<Range, 4> RangesWithoutEpilogues;
+
+ if (isImplicitThisVariable(Var.DIVar) && FI.EpilogueRanges.size() > 1 &&
+ DefRange.InMemory && !DefRange.IsSubfield &&
+ DefRange.DerefOffset == LocalVarDef::NoDeref && Ranges.size() == 1) {
+ const MCSymbol *Begin = Ranges.front().first;
+ const MCSymbol *End = Ranges.front().second;
+ for (const auto &Epilogue : FI.EpilogueRanges) {
+ if (Begin != Epilogue.first)
+ RangesWithoutEpilogues.emplace_back(Begin, Epilogue.first);
+ Begin = Epilogue.second;
+ }
+ if (Begin != End)
+ RangesWithoutEpilogues.emplace_back(Begin, End);
+ if (!RangesWithoutEpilogues.empty())
+ Ranges = RangesWithoutEpilogues;
+ }
+
BytePrefix.clear();
if (DefRange.InMemory) {
int Offset = DefRange.DataOffset;
@@ -2900,7 +2988,8 @@ void CodeViewDebug::emitLocalVariable(const FunctionInfo &FI,
DRHdr.BasePointerOffset = Offset;
DRHdr.OffsetInUdt = DefRange.DerefOffset;
OS.emitCVDefRangeDirective(Ranges, DRHdr);
- } else if (!DefRange.IsSubfield && EncFP != EncodedFramePtrReg::None &&
+ } else if (!isImplicitThisVariable(Var.DIVar) && !DefRange.IsSubfield &&
+ EncFP != EncodedFramePtrReg::None &&
(bool(Flags & LocalSymFlags::IsParameter)
? (EncFP == FI.EncodedParamFramePtrReg)
: (EncFP == FI.EncodedLocalFramePtrReg))) {
@@ -3085,6 +3174,49 @@ void CodeViewDebug::endFunctionImpl(const MachineFunction *MF) {
collectVariableInfo(GV.getSubprogram());
+ const LocalVariable *ImplicitThis = nullptr;
+ for (const auto &ScopeVars : ScopeVariables) {
+ for (const LocalVariable &Var : ScopeVars.second) {
+ if (isImplicitThisVariable(Var.DIVar, GV.getSubprogram())) {
+ ImplicitThis = &Var;
+ break;
+ }
+ }
+ if (ImplicitThis)
+ break;
+ }
+ if (ImplicitThis) {
+ for (const auto &MBB : *MF) {
+ const MachineInstr *EpilogueBegin = nullptr;
+ for (const auto &MI : MBB) {
+ if (!MI.isMetaInstruction() && MI.getFlag(MachineInstr::FrameDestroy)) {
+ EpilogueBegin = &MI;
+ break;
+ }
+ }
+ if (!EpilogueBegin)
+ continue;
+
+ MachineBasicBlock::const_iterator Last = MBB.getLastNonDebugInstr();
+ if (Last == MBB.end())
+ continue;
+
+ MCSymbol *EpilogueEnd = getLabelAfterInsn(&*Last);
+ assert(EpilogueEnd && "missing label after epilogue");
+ CurFn->EpilogueRanges.emplace_back(getLabelBeforeInsn(EpilogueBegin),
+ EpilogueEnd);
+ }
+
+ CurFn->DebugStart =
+ CurFn->PrologueEnd
+ ? getLabelBeforeInsn(CurFn->PrologueEnd)
+ : (ImplicitThis->DefRanges.empty()
+ ? nullptr
+ : ImplicitThis->DefRanges.begin()->second.front().first);
+ if (!CurFn->EpilogueRanges.empty())
+ CurFn->DebugEnd = CurFn->EpilogueRanges.back().first;
+ }
+
// Build the lexical block structure to emit for this routine.
if (LexicalScope *CFS = LScopes.getCurrentFunctionScope())
collectLexicalBlockInfo(*CFS,
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
index e28e0ab6da36b..851c04f413efc 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
@@ -50,6 +50,7 @@ class MCSectionCOFF;
class MCStreamer;
class MCSymbol;
class MachineFunction;
+class MachineInstr;
/// Collects and handles line tables information in a CodeView format.
class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
@@ -129,6 +130,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
SmallVector<std::pair<const MCSymbol *, const MCSymbol *>, 1>>
DefRanges;
std::optional<APSInt> ConstantValue;
+
+ bool isSimpleImplicitThis() const;
};
struct CVGlobalVariable {
@@ -200,6 +203,15 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
const MCSymbol *Begin = nullptr;
const MCSymbol *End = nullptr;
+
+ const MCSymbol *DebugStart = nullptr;
+ const MCSymbol *DebugEnd = nullptr;
+
+ SmallVector<std::pair<const MCSymbol *, const MCSymbol *>, 1>
+ EpilogueRanges;
+
+ const MachineInstr *PrologueEnd = nullptr;
+
unsigned FuncId = 0;
unsigned LastFileId = 0;
diff --git a/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll b/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll
new file mode 100644
index 0000000000000..009292a5a64a8
--- /dev/null
+++ b/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll
@@ -0,0 +1,147 @@
+; RUN: llc < %s -filetype=obj | llvm-readobj - --codeview | FileCheck %s --check-prefix=SIMPLE
+; RUN: llc < %s | llvm-mc -filetype=obj --triple=x86_64-windows | llvm-readobj - --codeview | FileCheck %s --check-prefix=SIMPLE
+; RUN: llc < %s -O0 -enable-tail-merge=0 -filetype=obj | llvm-readobj - --codeview | FileCheck %s --check-prefix=MULTI
+
+; Check that the compatibility S_REGREL32 record used for an implicit C++
+; `this` pointer is bounded by the containing procedure's debug range. In
+; particular, the range must end before the stack frame is restored.
+;
+; A stack-based implicit `this` cannot use S_REGREL32 when a function has
+; multiple epilogues. Each epilogue must instead be represented as a gap in
+; S_DEFRANGE_REGISTER_REL.
+
+; SIMPLE: GlobalProcIdSym {
+; SIMPLE: CodeSize: 0x{{[1-9A-Fa-f][0-9A-Fa-f]*}}
+; SIMPLE: DbgStart: 0x{{[1-9A-Fa-f][0-9A-Fa-f]*}}
+; SIMPLE: DbgEnd: 0x{{[1-9A-Fa-f][0-9A-Fa-f]*}}
+; SIMPLE: DisplayName: foo::foo
+; SIMPLE: }
+; SIMPLE: RegRelativeSym {
+; SIMPLE-NEXT: Kind: S_REGREL32 (0x1111)
+; SIMPLE: VarName: this
+; SIMPLE: }
+
+; MULTI: GlobalProcIdSym {
+; MULTI: CodeSize: 0x{{[1-9A-Fa-f][0-9A-Fa-f]*}}
+; MULTI: DbgStart: 0x{{[1-9A-Fa-f][0-9A-Fa-f]*}}
+; MULTI: DbgEnd: 0x{{[1-9A-Fa-f][0-9A-Fa-f]*}}
+; MULTI-LABEL: DisplayName: foo::f
+; MULTI: LinkageName: ?f at foo@@QEAAXH at Z
+; MULTI: RegRelativeSym {
+; MULTI-NEXT: Kind: S_REGREL32 (0x1111)
+; MULTI: VarName: this
+
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.11.25507"
+
+%class.foo = type { i32, i32 }
+
+$"\01??0foo@@QEAA at XZ" = comdat any
+$"?f at foo@@QEAAXH at Z" = comdat any
+
+; Function Attrs: noinline optnone uwtable
+define linkonce_odr void @"\01??0foo@@QEAA at XZ"(ptr %this) #0 comdat align 2 !dbg !10 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ call void @llvm.dbg.declare(metadata ptr %this.addr, metadata !11, metadata !DIExpression()), !dbg !12
+ %this1 = load ptr, ptr %this.addr, align 8
+ %a = getelementptr inbounds %class.foo, ptr %this1, i32 0, i32 0
+ store i32 1, ptr %a, align 4, !dbg !13
+ %b = getelementptr inbounds %class.foo, ptr %this1, i32 0, i32 1
+ store i32 2, ptr %b, align 4, !dbg !13
+ ret void, !dbg !13
+}
+
+declare void @exit_a(ptr)
+declare void @exit_b(ptr)
+declare void @exit_c(ptr)
+
+; Function Attrs: noinline optnone uwtable
+define linkonce_odr void @"?f at foo@@QEAAXH at Z"(ptr %this, i32 %value) #0 comdat align 2 !dbg !22 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ call void @llvm.dbg.declare(metadata ptr %this.addr, metadata !23,
+ metadata !DIExpression()), !dbg !24
+ %negative = icmp slt i32 %value, 0, !dbg !25
+ br i1 %negative, label %exit.a, label %check.zero, !dbg !25
+
+exit.a:
+ %this.a = load ptr, ptr %this.addr, align 8, !dbg !26
+ call void @exit_a(ptr %this.a), !dbg !26
+ ret void, !dbg !26
+
+check.zero:
+ %zero = icmp eq i32 %value, 0, !dbg !27
+ br i1 %zero, label %exit.b, label %exit.c, !dbg !27
+
+exit.b:
+ %this.b = load ptr, ptr %this.addr, align 8, !dbg !28
+ call void @exit_b(ptr %this.b), !dbg !28
+ ret void, !dbg !28
+
+exit.c:
+ %this.c = load ptr, ptr %this.addr, align 8, !dbg !29
+ call void @exit_c(ptr %this.c), !dbg !29
+ ret void, !dbg !29
+}
+
+declare void @llvm.dbg.declare(metadata, metadata, metadata)
+
+attributes #0 = { noinline optnone uwtable "frame-pointer"="none" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!14, !15, !16}
+!llvm.ident = !{!17}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1,
+ producer: "clang", isOptimized: false, runtimeVersion: 0,
+ emissionKind: FullDebug, enums: !2, retainedTypes: !2)
+!1 = !DIFile(filename: "test.cpp", directory: "C:\\src")
+!2 = !{}
+!3 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "foo", file: !1,
+ line: 1, size: 64, align: 64, elements: !4, identifier: ".?AVfoo@@")
+!4 = !{!5}
+!5 = !DISubprogram(name: "foo", linkageName: "\01??0foo@@QEAA at XZ", scope: !3,
+ file: !1, line: 3, type: !6, isLocal: false, isDefinition: false,
+ scopeLine: 3, flags: DIFlagPublic | DIFlagPrototyped)
+!6 = !DISubroutineType(types: !7)
+!7 = !{null, !8}
+!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64,
+ align: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
+!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64,
+ align: 64)
+!10 = distinct !DISubprogram(name: "foo", linkageName: "\01??0foo@@QEAA at XZ",
+ scope: !3, file: !1, line: 3, type: !6, isLocal: false,
+ scopeLine: 3, flags: DIFlagPublic | DIFlagPrototyped,
+ spFlags: DISPFlagDefinition, unit: !0,
+ declaration: !5, retainedNodes: !2)
+!11 = !DILocalVariable(name: "this", arg: 1, scope: !10, type: !9,
+ flags: DIFlagArtificial | DIFlagObjectPointer)
+!12 = !DILocation(line: 0, scope: !10)
+!13 = !DILocation(line: 3, column: 17, scope: !10)
+!14 = !{i32 2, !"CodeView", i32 1}
+!15 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !{i32 1, !"wchar_size", i32 2}
+!17 = !{!"clang"}
+!18 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!19 = !DISubroutineType(types: !20)
+!20 = !{null, !9, !18}
+!21 = !DISubprogram(name: "f", linkageName: "?f at foo@@QEAAXH at Z",
+ scope: !3, file: !1, line: 5, type: !19, isLocal: false,
+ isDefinition: false, scopeLine: 5,
+ flags: DIFlagPublic | DIFlagPrototyped)
+!22 = distinct !DISubprogram(name: "f", linkageName: "?f at foo@@QEAAXH at Z",
+ scope: !3, file: !1, line: 5, type: !19, isLocal: false,
+ scopeLine: 5, flags: DIFlagPublic | DIFlagPrototyped,
+ spFlags: DISPFlagDefinition, unit: !0, declaration: !21,
+ retainedNodes: !2)
+!23 = !DILocalVariable(name: "this", arg: 1, scope: !22, type: !9,
+ flags: DIFlagArtificial | DIFlagObjectPointer)
+!24 = !DILocation(line: 0, scope: !22)
+!25 = !DILocation(line: 6, column: 5, scope: !22)
+!26 = !DILocation(line: 7, column: 5, scope: !22)
+!27 = !DILocation(line: 8, column: 5, scope: !22)
+!28 = !DILocation(line: 9, column: 5, scope: !22)
+!29 = !DILocation(line: 10, column: 5, scope: !22)
>From 46abc4aa242cf28c413dace96f70716f459008cc Mon Sep 17 00:00:00 2001
From: GkvJwa <gkvjwa at gmail.com>
Date: Fri, 7 Aug 2026 12:56:07 +0800
Subject: [PATCH 2/3] Check multi count
---
llvm/test/DebugInfo/COFF/implicit-this-regrel.ll | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll b/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll
index 009292a5a64a8..3de279ddfcd25 100644
--- a/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll
+++ b/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll
@@ -1,14 +1,14 @@
; RUN: llc < %s -filetype=obj | llvm-readobj - --codeview | FileCheck %s --check-prefix=SIMPLE
; RUN: llc < %s | llvm-mc -filetype=obj --triple=x86_64-windows | llvm-readobj - --codeview | FileCheck %s --check-prefix=SIMPLE
+; RUN: llc < %s -O0 -enable-tail-merge=0 | FileCheck %s --check-prefix=MULTI-ASM
; RUN: llc < %s -O0 -enable-tail-merge=0 -filetype=obj | llvm-readobj - --codeview | FileCheck %s --check-prefix=MULTI
; Check that the compatibility S_REGREL32 record used for an implicit C++
-; `this` pointer is bounded by the containing procedure's debug range. In
-; particular, the range must end before the stack frame is restored.
-;
-; A stack-based implicit `this` cannot use S_REGREL32 when a function has
-; multiple epilogues. Each epilogue must instead be represented as a gap in
-; S_DEFRANGE_REGISTER_REL.
+; `this` pointer is bounded by the containing procedure's debug range,
+; including when the function has multiple epilogues.
+
+; MULTI-ASM-LABEL: "?f at foo@@QEAAXH at Z":
+; MULTI-ASM-COUNT-3: retq
; SIMPLE: GlobalProcIdSym {
; SIMPLE: CodeSize: 0x{{[1-9A-Fa-f][0-9A-Fa-f]*}}
@@ -27,7 +27,7 @@
; MULTI: DbgEnd: 0x{{[1-9A-Fa-f][0-9A-Fa-f]*}}
; MULTI-LABEL: DisplayName: foo::f
; MULTI: LinkageName: ?f at foo@@QEAAXH at Z
-; MULTI: RegRelativeSym {
+; MULTI-COUNT-1: RegRelativeSym {
; MULTI-NEXT: Kind: S_REGREL32 (0x1111)
; MULTI: VarName: this
>From a3161689bd500880c48c19106fada11c1346ff90 Mon Sep 17 00:00:00 2001
From: GkvJwa <gkvjwa at gmail.com>
Date: Sat, 8 Aug 2026 12:26:03 +0800
Subject: [PATCH 3/3] Handle no epilogue
---
llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 8 ++--
.../DebugInfo/COFF/implicit-this-regrel.ll | 37 ++++++++++++++++++-
2 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index c4f26f7d7f058..d0d4480e87ab3 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -3171,6 +3171,7 @@ void CodeViewDebug::endFunctionImpl(const MachineFunction *MF) {
const Function &GV = MF->getFunction();
assert(FnDebugInfo.count(&GV));
assert(CurFn == FnDebugInfo[&GV].get());
+ const MCSymbol *FunctionEnd = Asm->getFunctionEnd();
collectVariableInfo(GV.getSubprogram());
@@ -3213,8 +3214,9 @@ void CodeViewDebug::endFunctionImpl(const MachineFunction *MF) {
: (ImplicitThis->DefRanges.empty()
? nullptr
: ImplicitThis->DefRanges.begin()->second.front().first);
- if (!CurFn->EpilogueRanges.empty())
- CurFn->DebugEnd = CurFn->EpilogueRanges.back().first;
+ CurFn->DebugEnd = CurFn->EpilogueRanges.empty()
+ ? FunctionEnd
+ : CurFn->EpilogueRanges.back().first;
}
// Build the lexical block structure to emit for this routine.
@@ -3254,7 +3256,7 @@ void CodeViewDebug::endFunctionImpl(const MachineFunction *MF) {
CurFn->Annotations = MF->getCodeViewAnnotations();
- CurFn->End = Asm->getFunctionEnd();
+ CurFn->End = FunctionEnd;
CurFn = nullptr;
}
diff --git a/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll b/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll
index 3de279ddfcd25..635df867b37b7 100644
--- a/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll
+++ b/llvm/test/DebugInfo/COFF/implicit-this-regrel.ll
@@ -2,10 +2,11 @@
; RUN: llc < %s | llvm-mc -filetype=obj --triple=x86_64-windows | llvm-readobj - --codeview | FileCheck %s --check-prefix=SIMPLE
; RUN: llc < %s -O0 -enable-tail-merge=0 | FileCheck %s --check-prefix=MULTI-ASM
; RUN: llc < %s -O0 -enable-tail-merge=0 -filetype=obj | llvm-readobj - --codeview | FileCheck %s --check-prefix=MULTI
+; RUN: llc < %s -filetype=obj | llvm-readobj - --codeview | FileCheck %s --check-prefix=NOEPILOGUE
; Check that the compatibility S_REGREL32 record used for an implicit C++
; `this` pointer is bounded by the containing procedure's debug range,
-; including when the function has multiple epilogues.
+; including when the function has multiple epilogues or no epilogue.
; MULTI-ASM-LABEL: "?f at foo@@QEAAXH at Z":
; MULTI-ASM-COUNT-3: retq
@@ -31,6 +32,15 @@
; MULTI-NEXT: Kind: S_REGREL32 (0x1111)
; MULTI: VarName: this
+; NOEPILOGUE-LABEL: {{^ *ProcIdSym \{$}}
+; NOEPILOGUE: CodeSize: 0x[[NOEPILOGUE_SIZE:[1-9A-Fa-f][0-9A-Fa-f]*]]
+; NOEPILOGUE: DbgStart: 0x{{[1-9A-Fa-f][0-9A-Fa-f]*}}
+; NOEPILOGUE: DbgEnd: 0x[[NOEPILOGUE_SIZE]]
+; NOEPILOGUE: DisplayName: foo::g
+; NOEPILOGUE: RegRelativeSym {
+; NOEPILOGUE-NEXT: Kind: S_REGREL32 (0x1111)
+; NOEPILOGUE: VarName: this
+
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.11.25507"
@@ -56,6 +66,7 @@ entry:
declare void @exit_a(ptr)
declare void @exit_b(ptr)
declare void @exit_c(ptr)
+declare void @throw_now(ptr) #1
; Function Attrs: noinline optnone uwtable
define linkonce_odr void @"?f at foo@@QEAAXH at Z"(ptr %this, i32 %value) #0 comdat align 2 !dbg !22 {
@@ -87,9 +98,21 @@ exit.c:
ret void, !dbg !29
}
+; Function Attrs: noinline noreturn optnone uwtable
+define internal void @"?g at foo@@QEAAXXZ"(ptr %this) #0 align 2 !dbg !32 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ call void @llvm.dbg.declare(metadata ptr %this.addr, metadata !31,
+ metadata !DIExpression()), !dbg !33
+ call void @throw_now(ptr %this), !dbg !34
+ unreachable
+}
+
declare void @llvm.dbg.declare(metadata, metadata, metadata)
attributes #0 = { noinline optnone uwtable "frame-pointer"="none" }
+attributes #1 = { noreturn nounwind }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!14, !15, !16}
@@ -145,3 +168,15 @@ attributes #0 = { noinline optnone uwtable "frame-pointer"="none" }
!27 = !DILocation(line: 8, column: 5, scope: !22)
!28 = !DILocation(line: 9, column: 5, scope: !22)
!29 = !DILocation(line: 10, column: 5, scope: !22)
+!30 = !DISubprogram(name: "g", linkageName: "?g at foo@@QEAAXXZ", scope: !3,
+ file: !1, line: 13, type: !6, isLocal: false, isDefinition: false,
+ scopeLine: 13,
+ flags: DIFlagPublic | DIFlagPrototyped)
+!31 = !DILocalVariable(name: "this", arg: 1, scope: !32, type: !9,
+ flags: DIFlagArtificial | DIFlagObjectPointer)
+!32 = distinct !DISubprogram(name: "g", linkageName: "?g at foo@@QEAAXXZ",
+ scope: !3, file: !1, line: 13, type: !6, isLocal: false, scopeLine: 13,
+ flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDefinition,
+ unit: !0, declaration: !30, retainedNodes: !2)
+!33 = !DILocation(line: 0, scope: !32)
+!34 = !DILocation(line: 14, column: 5, scope: !32)
More information about the llvm-commits
mailing list