#include "llvm/CodeGen/AssignmentTrackingAnalysis.h"
+#include "LiveDebugValues/LiveDebugValues.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/IntervalMap.h"
static cl::opt<bool> PrintResults("print-debug-ata", cl::init(false),
cl::Hidden);
+/// Coalesce adjacent dbg locs describing memory locations that have contiguous
+/// fragments. This reduces the cost of LiveDebugValues which does SSA
+/// construction for each explicitly stated variable fragment.
+static cl::opt<cl::boolOrDefault>
+ CoalesceAdjacentFragmentsOpt("debug-ata-coalesce-frags", cl::Hidden);
+
// Implicit conversions are disabled for enum class types, so unfortunately we
// need to create a DenseMapInfo wrapper around the specified underlying type.
template <> struct llvm::DenseMapInfo<VariableID> {
return DebugAggregate(Var.getVariable(), Var.getInlinedAt());
}
+static bool shouldCoalesceFragments(Function &F) {
+ // Enabling fragment coalescing reduces compiler run time when instruction
+ // referencing is enabled. However, it may cause LiveDebugVariables to create
+ // incorrect locations. Since instruction-referencing mode effectively
+ // bypasses LiveDebugVariables we only enable coalescing if the cl::opt flag
+ // has not been explicitly set and instruction-referencing is turned on.
+ switch (CoalesceAdjacentFragmentsOpt) {
+ case cl::boolOrDefault::BOU_UNSET:
+ return debuginfoShouldUseDebugInstrRef(
+ Triple(F.getParent()->getTargetTriple()));
+ case cl::boolOrDefault::BOU_TRUE:
+ return true;
+ case cl::boolOrDefault::BOU_FALSE:
+ return false;
+ }
+ llvm_unreachable("Unknown boolOrDefault value");
+}
+
namespace {
/// In dwarf emission, the following sequence
/// 1. dbg.value ... Fragment(0, 64)
Function &Fn;
FunctionVarLocsBuilder *FnVarLocs;
const DenseSet<DebugAggregate> *VarsWithStackSlot;
+ bool CoalesceAdjacentFragments;
// 0 = no memory location.
using BaseAddress = unsigned;
<< " bits [" << StartBit << ", " << EndBit << ")\n");
}
+ /// Inserts a new dbg def if the interval found when looking up \p StartBit
+ /// in \p FragMap starts before \p StartBit or ends after \p EndBit (which
+ /// indicates - assuming StartBit->EndBit has just been inserted - that the
+ /// slice has been coalesced in the map).
+ void coalesceFragments(BasicBlock &BB, Instruction &Before, unsigned Var,
+ unsigned StartBit, unsigned EndBit, unsigned Base,
+ DebugLoc DL, const FragsInMemMap &FragMap) {
+ if (!CoalesceAdjacentFragments)
+ return;
+ // We've inserted the location into the map. The map will have coalesced
+ // adjacent intervals (variable fragments) that describe the same memory
+ // location. Use this knowledge to insert a debug location that describes
+ // that coalesced fragment. This may eclipse other locs we've just
+ // inserted. This is okay as redundant locs will be cleaned up later.
+ auto CoalescedFrag = FragMap.find(StartBit);
+ // Bail if no coalescing has taken place.
+ if (CoalescedFrag.start() == StartBit && CoalescedFrag.stop() == EndBit)
+ return;
+
+ LLVM_DEBUG(dbgs() << "- Insert loc for bits " << CoalescedFrag.start()
+ << " to " << CoalescedFrag.stop() << "\n");
+ insertMemLoc(BB, Before, Var, CoalescedFrag.start(), CoalescedFrag.stop(),
+ Base, DL);
+ }
+
void addDef(const VarLocInfo &VarLoc, Instruction &Before, BasicBlock &BB,
VarFragMap &LiveSet) {
DebugVariable DbgVar = FnVarLocs->getVariable(VarLoc.VariableID);
if (!FragMap.overlaps(StartBit, EndBit)) {
LLVM_DEBUG(dbgs() << "- No overlaps\n");
FragMap.insert(StartBit, EndBit, Base);
+ coalesceFragments(BB, Before, Var, StartBit, EndBit, Base, VarLoc.DL,
+ FragMap);
return;
}
// There is at least one overlap.
LLVM_DEBUG(dbgs() << "- Insert DEF into now-empty space\n");
FragMap.insert(StartBit, EndBit, Base);
}
+
+ coalesceFragments(BB, Before, Var, StartBit, EndBit, Base, VarLoc.DL,
+ FragMap);
}
bool skipVariable(const DILocalVariable *V) { return !V->getSizeInBits(); }
public:
MemLocFragmentFill(Function &Fn,
- const DenseSet<DebugAggregate> *VarsWithStackSlot)
- : Fn(Fn), VarsWithStackSlot(VarsWithStackSlot) {}
+ const DenseSet<DebugAggregate> *VarsWithStackSlot,
+ bool CoalesceAdjacentFragments)
+ : Fn(Fn), VarsWithStackSlot(VarsWithStackSlot),
+ CoalesceAdjacentFragments(CoalesceAdjacentFragments) {}
/// Add variable locations to \p FnVarLocs so that any bits of a variable
/// with a memory location have that location explicitly reinstated at each
for (auto &FragMemLoc : FragMemLocs) {
DIExpression *Expr = DIExpression::get(Ctx, std::nullopt);
- Expr = *DIExpression::createFragmentExpression(
- Expr, FragMemLoc.OffsetInBits, FragMemLoc.SizeInBits);
+ if (FragMemLoc.SizeInBits !=
+ *Aggregates[FragMemLoc.Var].first->getSizeInBits())
+ Expr = *DIExpression::createFragmentExpression(
+ Expr, FragMemLoc.OffsetInBits, FragMemLoc.SizeInBits);
Expr = DIExpression::prepend(Expr, DIExpression::DerefAfter,
FragMemLoc.OffsetInBits / 8);
DebugVariable Var(Aggregates[FragMemLoc.Var].first, Expr,
}
if (Changed) {
- MemLocFragmentFill Pass(Fn, &VarsWithStackSlot);
+ MemLocFragmentFill Pass(Fn, &VarsWithStackSlot,
+ shouldCoalesceFragments(Fn));
Pass.run(FnVarLocs);
// Remove redundant entries. As well as reducing memory consumption and
--- /dev/null
+; RUN: llc %s -o - -stop-after=finalize-isel \
+; RUN: | FileCheck %s --implicit-check-not=DBG_
+
+;; Test coalescing of contiguous fragments in adjacent location definitions.
+;; Further details and check directives inline.
+
+target triple = "x86_64-unknown-linux-gnu"
+
+@cond = dso_local global i8 0, align 1
+
+;; The final store and linked dbg.assign indicate the whole variable is located
+;; on the stack. Coalesce the two fragment defs that are generated (0-32,
+;; 32-64) at the final dbg.assign into one (0-64, which covers the whole
+;; variable meaning we don't need a fragment expression). And check the two
+;; DBG_VALUEs in if.then are not coalesced, since they specify different
+;; locations. This is the same as the first test in coalesce-simple.ll except
+;; the dbg intrinsics are split up over a simple diamond CFG to check the info
+;; is propagated betweeen blocks correctly.
+
+; CHECK-LABEL: bb.0.entry:
+; CHECK-NEXT: successors:
+; CHECK-NEXT: {{^ *$}}
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_deref)
+; CHECK-NEXT: TEST8mi
+; CHECK-NEXT: JCC_1 %bb.2
+; CHECK-NEXT: JMP_1 %bb.1
+
+; CHECK-LABEL: bb.1.if.then:
+; CHECK-NEXT: successors:
+; CHECK-NEXT: {{^ *$}}
+; CHECK-NEXT: MOV8mi $rip, 1, $noreg, @cond, $noreg, 0 :: (store (s8) into @cond)
+; CHECK-NEXT: DBG_VALUE 1, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 0, 32)
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_deref, DW_OP_LLVM_fragment, 32, 32)
+; CHECK-NEXT: JMP_1 %bb.3
+
+; CHECK-LABEL: bb.2.if.else:
+; CHECK-NEXT: successors:
+; CHECK-NEXT: {{^ *$}}
+; CHECK-NEXT: MOV8mi $rip, 1, $noreg, @cond, $noreg, 1 :: (store (s8) into @cond)
+
+; CHECK-LABEL: bb.3.if.end:
+; CHECK-NEXT: MOV32mi %stack.0.a, 1, $noreg, 0, $noreg, 5 :: (store (s32) into %ir.a, align 8)
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_deref)
+; CHECK-NEXT: RET 0
+
+define dso_local void @_Z3funv() local_unnamed_addr !dbg !16 {
+entry:
+ %a = alloca i64, !DIAssignID !37
+ call void @llvm.dbg.assign(metadata i64 poison, metadata !20, metadata !DIExpression(), metadata !37, metadata ptr %a, metadata !DIExpression()), !dbg !25
+ %0 = load i8, ptr @cond, align 1
+ %tobool = trunc i8 %0 to i1
+ br i1 %tobool, label %if.then, label %if.else
+
+if.then:
+ store i1 false, ptr @cond
+ call void @llvm.dbg.value(metadata i32 1, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25
+ br label %if.end
+
+if.else:
+ store i1 true, ptr @cond
+ br label %if.end
+
+if.end:
+ store i32 5, ptr %a, !DIAssignID !38
+ call void @llvm.dbg.assign(metadata i32 5, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !38, metadata ptr %a, metadata !DIExpression()), !dbg !25
+ ret void
+}
+
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!8, !9, !10, !11, !12, !13, !14}
+!llvm.ident = !{!15}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "G", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 17.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.cpp", directory: "/")
+!4 = !{!0, !5}
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+!6 = distinct !DIGlobalVariable(name: "F", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true)
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!8 = !{i32 7, !"Dwarf Version", i32 5}
+!9 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = !{i32 1, !"wchar_size", i32 4}
+!11 = !{i32 8, !"PIC Level", i32 2}
+!12 = !{i32 7, !"PIE Level", i32 2}
+!13 = !{i32 7, !"uwtable", i32 2}
+!14 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!15 = !{!"clang version 17.0.0"}
+!16 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !3, file: !3, line: 3, type: !17, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !19)
+!17 = !DISubroutineType(types: !18)
+!18 = !{null}
+!19 = !{!20}
+!20 = !DILocalVariable(name: "X", scope: !16, file: !3, line: 4, type: !21)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Pair", file: !3, line: 2, size: 64, flags: DIFlagTypePassByValue, elements: !22, identifier: "_ZTS4Pair")
+!22 = !{}
+!25 = !DILocation(line: 0, scope: !16)
+!26 = !DILocation(line: 7, column: 7, scope: !27)
+!27 = distinct !DILexicalBlock(scope: !16, file: !3, line: 7, column: 7)
+!28 = distinct !DIAssignID()
+!37 = distinct !DIAssignID()
+!38 = distinct !DIAssignID()
--- /dev/null
+;; Test coalescing of contiguous fragments in adjacent location definitions.
+;; This test contains the first function from coalesce-simple.ll. Just use it
+;; to check whether coalescing happens or not with different flag settings.
+;;
+;; +=================+==============================+======================+
+;; | Coalescing flag | Instruction-Referencing flag | Coalescing behaviour |
+;; +=================+==============================+======================+
+;; | default | enabled | enabled |
+;; | default | disabled | disabled |
+;; | enabled | * | enabled |
+;; | disabled | * | disabled |
+;; +-----------------+------------------------------+----------------------+
+
+;; Coalescing default + instructino-referencing enabled = enable.
+; RUN: llc %s -o - -stop-after=finalize-isel -experimental-debug-variable-locations=true \
+; RUN: | FileCheck %s --check-prefixes=CHECK,ENABLE
+
+;; Coalescing default + instructino-referencing disabled = disable.
+; RUN: llc %s -o - -stop-after=finalize-isel -experimental-debug-variable-locations=false \
+; RUN: | FileCheck %s --check-prefixes=CHECK,DISABLE
+
+;; Coalescing enabled + instructino-referencing disabled = enable.
+; RUN: llc %s -o - -stop-after=finalize-isel -experimental-debug-variable-locations=false \
+; RUN: -debug-ata-coalesce-frags=true \
+; RUN: | FileCheck %s --check-prefixes=CHECK,ENABLE
+
+;; Coalescing disabled + instructino-referencing enabled = disable.
+; RUN: llc %s -o - -stop-after=finalize-isel -experimental-debug-variable-locations=true \
+; RUN: -debug-ata-coalesce-frags=false \
+; RUN: | FileCheck %s --check-prefixes=CHECK,DISABLE
+
+; CHECK: MOV32mi %stack.0.a, 1, $noreg, 0, $noreg, 5
+; ENABLE-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_deref)
+; DISABLE-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32)
+
+target triple = "x86_64-unknown-linux-gnu"
+
+define dso_local void @_Z3funv() local_unnamed_addr !dbg !16 {
+entry:
+ %a = alloca i64, !DIAssignID !37
+ call void @llvm.dbg.assign(metadata i64 poison, metadata !20, metadata !DIExpression(), metadata !37, metadata ptr %a, metadata !DIExpression()), !dbg !25
+ call void @llvm.dbg.value(metadata i32 1, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25
+ store i32 5, ptr %a, !DIAssignID !38
+ call void @llvm.dbg.assign(metadata i32 5, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !38, metadata ptr %a, metadata !DIExpression()), !dbg !25
+ ret void
+}
+
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!8, !9, !10, !11, !12, !13, !14}
+!llvm.ident = !{!15}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "G", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 17.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.cpp", directory: "/")
+!4 = !{!0, !5}
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+!6 = distinct !DIGlobalVariable(name: "F", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true)
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!8 = !{i32 7, !"Dwarf Version", i32 5}
+!9 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = !{i32 1, !"wchar_size", i32 4}
+!11 = !{i32 8, !"PIC Level", i32 2}
+!12 = !{i32 7, !"PIE Level", i32 2}
+!13 = !{i32 7, !"uwtable", i32 2}
+!14 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!15 = !{!"clang version 17.0.0"}
+!16 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !3, file: !3, line: 3, type: !17, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !19)
+!17 = !DISubroutineType(types: !18)
+!18 = !{null}
+!19 = !{!20}
+!20 = !DILocalVariable(name: "X", scope: !16, file: !3, line: 4, type: !21)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Pair", file: !3, line: 2, size: 64, flags: DIFlagTypePassByValue, elements: !22, identifier: "_ZTS4Pair")
+!22 = !{}
+!25 = !DILocation(line: 0, scope: !16)
+!26 = !DILocation(line: 7, column: 7, scope: !27)
+!27 = distinct !DILexicalBlock(scope: !16, file: !3, line: 7, column: 7)
+!37 = distinct !DIAssignID()
+!38 = distinct !DIAssignID()
--- /dev/null
+; RUN: llc %s -o - -stop-after=finalize-isel \
+; RUN: | FileCheck %s --implicit-check-not=DBG_
+
+;; Test coalescing of contiguous fragments in adjacent location definitions.
+;; Further details and check directives inline.
+
+target triple = "x86_64-unknown-linux-gnu"
+
+;; The final store and linked dbg.assign indicate the whole variable is located
+;; on the stack. Coalesce the two fragment defs that are generated (0-32,
+;; 32-64) at the final dbg.assign into one (0-64, which covers the whole
+;; variable meaning we don't need a fragment expression). And check the
+;; first two DBG_VALUEs are not coalesced, since they specify different
+;; locations.
+; CHECK: _Z3fun
+; CHECK-LABEL: bb.0.entry:
+; CHECK-NEXT: DBG_VALUE 1, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 0, 32)
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_deref, DW_OP_LLVM_fragment, 32, 32)
+; CHECK-NEXT: MOV32mi %stack.0.a, 1, $noreg, 0, $noreg, 5
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_deref)
+; CHECK-NEXT: RET
+define dso_local void @_Z3funv() local_unnamed_addr !dbg !16 {
+entry:
+ %a = alloca i64, !DIAssignID !37
+ call void @llvm.dbg.assign(metadata i64 poison, metadata !20, metadata !DIExpression(), metadata !37, metadata ptr %a, metadata !DIExpression()), !dbg !25
+ call void @llvm.dbg.value(metadata i32 1, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25
+ store i32 5, ptr %a, !DIAssignID !38
+ call void @llvm.dbg.assign(metadata i32 5, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !38, metadata ptr %a, metadata !DIExpression()), !dbg !25
+ ret void
+}
+
+;; Similar to the test above except that the variable has been split over two
+;; allocas, so coalescing should not take place (different memory location for
+;; the fragments).
+; CHECK: Z3funv2
+; CHECK-LABEL: bb.0.entry:
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32)
+; CHECK-NEXT: DBG_VALUE %stack.1.b, $noreg, ![[#]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32)
+; CHECK-NEXT: DBG_VALUE 1, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 0, 32)
+; CHECK-NEXT: MOV32mi %stack.0.a, 1, $noreg, 0, $noreg, 5
+;; Both fragments 0-32 and 32-64 are in memory now, but located in different stack slots.
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32)
+; CHECK-NEXT: RET
+define dso_local void @_Z3funv2() local_unnamed_addr !dbg !39 {
+entry:
+ %a = alloca i32, !DIAssignID !42
+ call void @llvm.dbg.assign(metadata i64 poison, metadata !41, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !42, metadata ptr %a, metadata !DIExpression()), !dbg !44
+ %b = alloca i32, !DIAssignID !45
+ call void @llvm.dbg.assign(metadata i64 poison, metadata !41, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !45, metadata ptr %b, metadata !DIExpression()), !dbg !44
+ call void @llvm.dbg.value(metadata i32 1, metadata !41, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !44
+ store i32 5, ptr %a, !DIAssignID !43
+ call void @llvm.dbg.assign(metadata i32 5, metadata !41, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !43, metadata ptr %a, metadata !DIExpression()), !dbg !44
+ ret void
+}
+
+;; Similar to the first test above except the part that it's the slice 16-32
+;; that is "partially promoted". The dbg defs after the alloca cannot be
+;; coalesced (slices 0-16 and 32-64 are in memory but 16-32 isn't). The entire
+;; variable is on the stack after the store.
+; CHECK: _Z2funv3
+; CHECK-LABEL: bb.0.entry:
+; CHECK-NEXT: DBG_VALUE 2, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 16, 16)
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 16)
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_deref, DW_OP_LLVM_fragment, 32, 32)
+; CHECK-NEXT: MOV32mi %stack.0.a, 1, $noreg, 0, $noreg, 5
+; CHECK-NEXT: DBG_VALUE %stack.0.a, $noreg, ![[#]], !DIExpression(DW_OP_deref)
+; CHECK-NEXT: RET
+define dso_local void @_Z2funv3() local_unnamed_addr !dbg !46 {
+entry:
+ %a = alloca i64, !DIAssignID !49
+ call void @llvm.dbg.assign(metadata i64 poison, metadata !48, metadata !DIExpression(), metadata !49, metadata ptr %a, metadata !DIExpression()), !dbg !51
+ call void @llvm.dbg.value(metadata i32 2, metadata !48, metadata !DIExpression(DW_OP_LLVM_fragment, 16, 16)), !dbg !51
+ store i32 5, ptr %a, !DIAssignID !50
+ call void @llvm.dbg.assign(metadata i32 5, metadata !48, metadata !DIExpression(DW_OP_LLVM_fragment, 16, 16), metadata !50, metadata ptr %a, metadata !DIExpression(DW_OP_plus_uconst, 2)), !dbg !51
+ ret void
+}
+
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!8, !9, !10, !11, !12, !13, !14}
+!llvm.ident = !{!15}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "G", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 17.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.cpp", directory: "/")
+!4 = !{!0, !5}
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+!6 = distinct !DIGlobalVariable(name: "F", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true)
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!8 = !{i32 7, !"Dwarf Version", i32 5}
+!9 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = !{i32 1, !"wchar_size", i32 4}
+!11 = !{i32 8, !"PIC Level", i32 2}
+!12 = !{i32 7, !"PIE Level", i32 2}
+!13 = !{i32 7, !"uwtable", i32 2}
+!14 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!15 = !{!"clang version 17.0.0"}
+!16 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !3, file: !3, line: 3, type: !17, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !19)
+!17 = !DISubroutineType(types: !18)
+!18 = !{null}
+!19 = !{!20}
+!20 = !DILocalVariable(name: "X", scope: !16, file: !3, line: 4, type: !21)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Pair", file: !3, line: 2, size: 64, flags: DIFlagTypePassByValue, elements: !22, identifier: "_ZTS4Pair")
+!22 = !{}
+!25 = !DILocation(line: 0, scope: !16)
+!26 = !DILocation(line: 7, column: 7, scope: !27)
+!27 = distinct !DILexicalBlock(scope: !16, file: !3, line: 7, column: 7)
+!37 = distinct !DIAssignID()
+!38 = distinct !DIAssignID()
+!39 = distinct !DISubprogram(name: "fun2", linkageName: "_Z3funv2", scope: !3, file: !3, line: 3, type: !17, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !40)
+!40 = !{!41}
+!41 = !DILocalVariable(name: "X", scope: !39, file: !3, line: 10, type: !21)
+!42 = distinct !DIAssignID()
+!43 = distinct !DIAssignID()
+!44 = !DILocation(line: 0, scope: !39)
+!45 = distinct !DIAssignID()
+!46 = distinct !DISubprogram(name: "fun3", linkageName: "_Z3funv3", scope: !3, file: !3, line: 3, type: !17, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !47)
+!47 = !{!48}
+!48 = !DILocalVariable(name: "X", scope: !46, file: !3, line: 10, type: !21)
+!49 = distinct !DIAssignID()
+!50 = distinct !DIAssignID()
+!51 = !DILocation(line: 0, scope: !46)
; RUN: llc %s -stop-before finalize-isel -o - \
; RUN: -experimental-debug-variable-locations=false \
+; RUN: -debug-ata-coalesce-frags=true \
; RUN: | FileCheck %s --check-prefixes=CHECK,DBGVALUE --implicit-check-not=DBG_VALUE
; RUN: llc %s -stop-before finalize-isel -o - \
; RUN: -experimental-debug-variable-locations=true \
;; Then there is a store to the upper 64 bits.
; CHECK: MOV64mi32 %stack.0.X, 1, $noreg, 8, $noreg, 0, debug-location
-;; This DBG_VALUE is added by the mem-loc-frag-fill pass because bits [0, 64)
-;; are still live in memory.
-; CHECK-NEXT: DBG_VALUE %stack.0.X, $noreg, ![[VAR]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 64)
-; CHECK-NEXT: DBG_VALUE %stack.0.X, $noreg, ![[VAR]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_deref, DW_OP_LLVM_fragment, 64, 64), debug-location
+;; No change in variable location: the stack home is still valid.
;; The final assignment (X.B += 2) doesn't get stored back to the alloca. This
;; means that that the stack location isn't valid for the entire lifetime of X.
; RUN: llc %s -stop-before finalize-isel -o - \
; RUN: -experimental-debug-variable-locations=false \
+; RUN: -debug-ata-coalesce-frags=true \
; RUN: | FileCheck %s --implicit-check-not=DBG_
; RUN: llc %s -stop-before finalize-isel -o - \
; RUN: -experimental-debug-variable-locations=true \
;; Check that the mem-loc-frag-fill pseudo-pass works on a simple CFG. When
;; LLVM sees a dbg.value with an overlapping fragment it essentially considers
;; the previous location as valid for all bits in that fragment. The pass
-;; inserts dbg.value fragments to preserve memory locations for bits in memory
-;; when overlapping fragments are encountered.
+;; tracks which bits are in memory and inserts dbg.values preserve memory
+;; locations for bits in memory when overlapping fragments are encountered.
;; nums lives in mem, except prior to the second call to step() where there has
;; been some DSE. At this point, the memory loc for nums.c is invalid. But the
call void @llvm.dbg.assign(metadata i32 2, metadata !44, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata !56, metadata ptr %c, metadata !DIExpression()), !dbg !46
%call1 = tail call noundef zeroext i1 @_Z4stepv(), !dbg !57
store i32 1, ptr %c, align 4, !dbg !58, !DIAssignID !61
+;; Store to bits 64 to 96 - the whole variable is in memory again.
; CHECK: MOV32mi %stack.0.nums, 1, $noreg, 8, $noreg, 1
-; CHECK-NEXT: DBG_VALUE %stack.0.nums, $noreg, ![[nums]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_deref, DW_OP_LLVM_fragment, 64, 32)
+; CHECK-NEXT: DBG_VALUE %stack.0.nums, $noreg, ![[nums]], !DIExpression(DW_OP_deref)
call void @llvm.dbg.assign(metadata i32 1, metadata !44, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata !61, metadata ptr %c, metadata !DIExpression()), !dbg !46
call void @_Z4esc1P4Nums(ptr noundef nonnull %nums), !dbg !62
ret i32 0, !dbg !64
; RUN: llc %s -stop-before finalize-isel -o - \
; RUN: -experimental-debug-variable-locations=false \
+; RUN: -debug-ata-coalesce-frags=true \
; RUN: | FileCheck %s --implicit-check-not=DBG_
; RUN: llc %s -stop-before finalize-isel -o - \
; RUN: -experimental-debug-variable-locations=true \
store i32 2, ptr %c, align 8, !dbg !25, !DIAssignID !31
; CHECK: MOV32mi %stack.0.nums, 1, $noreg, 8, $noreg, 2
call void @llvm.dbg.assign(metadata i32 2, metadata !12, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata !31, metadata ptr %c, metadata !DIExpression()), !dbg !19
-; CHECK-NEXT: DBG_VALUE %stack.0.nums, $noreg, ![[nums]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_deref, DW_OP_LLVM_fragment, 64, 32)
+; CHECK-NEXT: DBG_VALUE %stack.0.nums, $noreg, ![[nums]], !DIExpression(DW_OP_deref)
tail call void @_Z4stepv(), !dbg !32
;; Next dbg.assign added by hand to test that the bits [64, 32) have been
;; correctly tracked as in memory - we know this has worked if
; CHECK-NEXT: DBG_VALUE %stack.4.e.addr, $noreg, ![[e]], !DIExpression(DW_OP_deref)
; CHECK-NEXT: MOV64mi32 %stack.0.a.addr, 1, $noreg, 0, $noreg, 1
; CHECK-NEXT: MOV64mi32 %stack.1.b.addr, 1, $noreg, 0, $noreg, 2
-; CHECK-NEXT: DBG_VALUE %stack.4.e.addr, $noreg, ![[e]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_deref, DW_OP_LLVM_fragment, 32, 32)
-; CHECK-NEXT: DBG_VALUE %stack.4.e.addr, $noreg, ![[e]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32)
; CHECK-NEXT: MOV32mi %stack.0.a.addr, 1, $noreg, 0, $noreg, 3
; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[a]], !DIExpression(DW_OP_LLVM_fragment, 0, 32)
; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_deref, DW_OP_LLVM_fragment, 32, 32)
; CHECK-LABEL: bb.2.do.body1:
; CHECK-NEXT: successors
; CHECK-NEXT: {{^ *$}}
-; CxHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression(DW_OP_LLVM_fragment, 0, 32)
; CHECK: JMP_1
; CHECK-NEXT: {{^ *$}}
; CHECK-NEXT: {{^ *$}}
; CHECK-NEXT: %5:gr32 = MOV32rm $rip, 1, $noreg, @g_a, $noreg
; CHECK-NEXT: MOV32mr %stack.1.b.addr, 1, $noreg, 0, $noreg, killed %5
-; CHECK-NEXT: DBG_VALUE %stack.1.b.addr, $noreg, ![[b]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32)
+; CHECK-NEXT: DBG_VALUE %stack.1.b.addr, $noreg, ![[b]], !DIExpression(DW_OP_deref)
; CHECK-NEXT: MOV32mi %stack.3.d.addr, 1, $noreg, 0, $noreg, 6
; CHECK-NEXT: DBG_VALUE 8, $noreg, ![[e]], !DIExpression(DW_OP_LLVM_fragment, 32, 32)
; CHECK-NEXT: MOV32mi %stack.4.e.addr, 1, $noreg, 0, $noreg, 8
; CHECK-NEXT: DBG_VALUE %stack.4.e.addr, $noreg, ![[e]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32)
; CHECK-NEXT: JMP_1 %bb.5
-; CHECxK-NEXT: {{^ *$}}
if.else: ; preds = %do.body1
store i32 6, ptr %d.addr, !DIAssignID !74 ; VAR:d
;; live in memory.
; CHECK-NEXT: DBG_VALUE %stack.0.c, $noreg, ![[var]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_deref, DW_OP_LLVM_fragment, 32, 32)
+;; After the call to @d there's a dbg.assign linked to the store to bits 0-32
+;; that comes before it. Meaning the stack location for bits 0-32 are valid
+;; from here (bits 32-64 for the variable are already located in memory).
; CHECK: CALL64pcrel32 @d
; CHECK-NEXT: ADJCALLSTACKUP64
-; CHECK-NEXT: DBG_VALUE %stack.0.c, $noreg, ![[var]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32), debug-location
+; CHECK-NEXT: DBG_VALUE %stack.0.c, $noreg, ![[var]], !DIExpression(DW_OP_deref), debug-location
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"