Index: lib/CodeGen/LiveDebugValues.cpp =================================================================== --- lib/CodeGen/LiveDebugValues.cpp +++ lib/CodeGen/LiveDebugValues.cpp @@ -310,7 +310,7 @@ TransferMap &Transfers, bool transferChanges); bool join(MachineBasicBlock &MBB, VarLocInMBB &OutLocs, VarLocInMBB &InLocs, - const VarLocMap &VarLocIDs, + VarLocMap &VarLocIDs, SmallPtrSet &Visited, SmallPtrSetImpl &ArtificialBlocks); @@ -757,13 +757,15 @@ /// source variable in all the predecessors of @MBB reside in the same location. bool LiveDebugValues::join( MachineBasicBlock &MBB, VarLocInMBB &OutLocs, VarLocInMBB &InLocs, - const VarLocMap &VarLocIDs, - SmallPtrSet &Visited, + VarLocMap &VarLocIDs, SmallPtrSet &Visited, SmallPtrSetImpl &ArtificialBlocks) { LLVM_DEBUG(dbgs() << "join MBB: " << MBB.getNumber() << "\n"); bool Changed = false; VarLocSet InLocsT; // Temporary incoming locations. + // Variables that have multiple locations in current BB. + SmallPtrSet ConflictVars; + DenseMap VariablesDL; // For all predecessors of this MBB, find the set of VarLocs that // can be joined. @@ -784,10 +786,27 @@ // Just copy over the Out locs to incoming locs for the first visited // predecessor, and for all other predecessors join the Out locs. - if (!NumVisited) + // Also keep track about variables that have multiple locations + // so that we can close theirs range since we can't decide which location + // we should take. + if (!NumVisited) { InLocsT = OL->second; - else + for (auto ID : InLocsT) + ConflictVars.insert(VarLocIDs[ID].Var.getVar()); + } else { InLocsT &= OL->second; + VarLocSet DanglingLocs = OL->second; + DanglingLocs.intersectWithComplement(InLocsT); + SmallPtrSet CurrentConf; + for (auto ID : DanglingLocs) { + const auto &VL = VarLocIDs[ID]; + if (ConflictVars.count(VL.Var.getVar())) { + CurrentConf.insert(VL.Var.getVar()); + VariablesDL[VL.Var.getVar()] = &VL.MI.getDebugLoc(); + } + } + std::swap(ConflictVars, CurrentConf); + } LLVM_DEBUG({ if (!InLocsT.empty()) { @@ -816,6 +835,38 @@ } InLocsT.intersectWithComplement(KillSet); + VarLocSet &ILS = InLocs[&MBB]; + + // Check if variable is InLocs. + auto isVarInLocs = [&](const DIVariable *DIVar) { + for (auto ID : ILS) + if (VarLocIDs[ID].Var.getVar() == DIVar) + return true; + return false; + }; + + // Close variables ranges that have different input locations. + if (NumVisited > 1) { + for (auto Var : ConflictVars) { + // If conflict variable is already in InLocs that means that we + // have already added it in previous call of join(). + if (isVarInLocs(Var)) + continue; + MachineInstr *MI = BuildMI( + *MBB.getParent(), *(VariablesDL[Var]), + TII->get(TargetOpcode::DBG_VALUE), false, 0, Var, + DIExpression::get(MBB.getParent()->getFunction().getContext(), {})); + LLVM_DEBUG(dbgs() << "Ending variable '" << Var->getName() + << "' range due to conflict locations\n" + << *MI << "\n"); + VarLoc VL(*MI, LS); + if (VL.dominates(MBB)) { + unsigned Loc = VarLocIDs.insert(VL); + InLocsT.set(Loc); + } + } + } + // As we are processing blocks in reverse post-order we // should have processed at least one predecessor, unless it // is the entry block which has no predecessor. @@ -824,8 +875,6 @@ if (InLocsT.empty()) return false; - VarLocSet &ILS = InLocs[&MBB]; - // Insert DBG_VALUE instructions, if not already inserted. VarLocSet Diff = InLocsT; Diff.intersectWithComplement(ILS); Index: test/DebugInfo/MIR/X86/dbg-live-debug-values-end-range.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/X86/dbg-live-debug-values-end-range.mir @@ -0,0 +1,116 @@ +# RUN: llc -run-pass=livedebugvalues %s -o -| FileCheck %s +# +# Verify that LiveDebugValues close variable's range when it has different +# input locations in one block. +# +# clang -O1 -g --target=x86_64-unknown-linux-gnu +# +# volatile int g; +# +#int baz(int p) { +# int local = 123; +# +# if (g > 1) { +# local = p; +# g += p; +# } +# +# return 3; +#} +# CHECK: DBG_VALUE 123, $noreg, ![[LOCALVAR:.*]] +# CHECK: DBG_VALUE $noreg, $noreg, ![[LOCALVAR]] +--- | + ; ModuleID = 'dbg-live-debug-values-end-range.c' + source_filename = "dbg-live-debug-values-end-range.c" + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + @g = common dso_local global i32 0, align 4, !dbg !0 + + ; Function Attrs: norecurse nounwind uwtable + define dso_local i32 @baz(i32 %p) local_unnamed_addr !dbg !12 { + entry: + call void @llvm.dbg.value(metadata i32 %p, metadata !16, metadata !DIExpression()), !dbg !18 + call void @llvm.dbg.value(metadata i32 123, metadata !17, metadata !DIExpression()), !dbg !18 + %0 = load volatile i32, i32* @g, align 4, !dbg !18, !tbaa !22 + %cmp = icmp sgt i32 %0, 1, !dbg !18 + br i1 %cmp, label %if.then, label %if.end, !dbg !18 + + if.then: ; preds = %entry + call void @llvm.dbg.value(metadata i32 %p, metadata !17, metadata !DIExpression()), !dbg !18 + %1 = load volatile i32, i32* @g, align 4, !dbg !18, !tbaa !22 + %add = add nsw i32 %1, %p, !dbg !18 + store volatile i32 %add, i32* @g, align 4, !dbg !18, !tbaa !22 + br label %if.end, !dbg !18 + + if.end: ; preds = %if.then, %entry + ret i32 3, !dbg !18 + } + + ; Function Attrs: nounwind readnone speculatable + declare void @llvm.dbg.value(metadata, metadata, metadata) + + ; Function Attrs: nounwind + declare void @llvm.stackprotector(i8*, i8**) + + !llvm.dbg.cu = !{!2} + !llvm.module.flags = !{!8, !9, !10} + !llvm.ident = !{!11} + + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 8, type: !6, isLocal: false, isDefinition: true) + !2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 9.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) + !3 = !DIFile(filename: "dbg-live-debug-values-end-range.c", directory: "/") + !4 = !{} + !5 = !{!0} + !6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) + !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !8 = !{i32 2, !"Dwarf Version", i32 4} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !11 = !{!"clang version 9.0.0 "} + !12 = distinct !DISubprogram(name: "baz", scope: !3, file: !3, line: 10, type: !13, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) + !13 = !DISubroutineType(types: !14) + !14 = !{!7, !7} + !15 = !{!16, !17} + !16 = !DILocalVariable(name: "p", arg: 1, scope: !12, file: !3, line: 10, type: !7) + !17 = !DILocalVariable(name: "local", scope: !12, file: !3, line: 11, type: !7) + !18 = !DILocation(line: 10, column: 13, scope: !12) + !22 = !{!23, !23, i64 0} + !23 = !{!"int", !24, i64 0} + !24 = !{!"omnipotent char", !25, i64 0} + !25 = !{!"Simple C/C++ TBAA"} + +... +--- +name: baz +alignment: 4 +tracksRegLiveness: true +liveins: + - { reg: '$edi' } +frameInfo: + maxCallFrameSize: 0 +machineFunctionInfo: {} +body: | + bb.0.entry: + successors: %bb.1, %bb.2 + liveins: $edi + + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !18 + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !18 + DBG_VALUE 123, $noreg, !17, !DIExpression(), debug-location !18 + renamable $eax = MOV32rm $rip, 1, $noreg, @g, $noreg, debug-location !18 :: (volatile dereferenceable load 4 from @g, !tbaa !22) + CMP32ri8 killed renamable $eax, 2, implicit-def $eflags, debug-location !18 + JCC_1 %bb.2, 12, implicit $eflags, debug-location !18 + + bb.1.if.then: + liveins: $edi + + DBG_VALUE $edi, $noreg, !17, !DIExpression(), debug-location !18 + ADD32mr $rip, 1, $noreg, @g, $noreg, killed renamable $edi, implicit-def dead $eflags, debug-location !18 :: (volatile store 4 into @g, !tbaa !22), (volatile dereferenceable load 4 from @g, !tbaa !22) + + bb.2.if.end: + $eax = MOV32ri 3, debug-location !18 + RETQ $eax, debug-location !18 + +... Index: test/DebugInfo/MIR/X86/dbg-stack-value-range.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/X86/dbg-stack-value-range.mir @@ -0,0 +1,153 @@ +# RUN: llc -start-before=livedebugvalues %s -filetype=obj -o -| llvm-dwarfdump -debug-info -| FileCheck %s +# +# LiveDebugValues should close variable's range when variable have multiple +# locations at block entry. Having BB1 that branches to BB2 and BB3, where +# BB2 falls through to BB3, variable can have different locations at BB1 and BB2. +# Since input locations at BB3 for same variable are different LiveDebugValues +# wont generate DBG_VALUE for BB3. If last variable location at BB2 is +# non-changing register, DbgEntityHistoryCalculator will extend range of +# DBG_VALUE from BB2 to whole BB3 and thus producing incorrect range for +# case when we took branch BB3 from BB1. +# +# CHECK: [0x000000000000001d, 0x0000000000000028): DW_OP_breg6 RBP-4) +# CHECK-NEXT: DW_AT_name {{.*}}"local1" +--- | + ; ModuleID = 'dbg-stack-value-range.ll' + source_filename = "dbg-stack-value-range.c" + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: nounwind uwtable + define dso_local i32 @foo(i32 %X) local_unnamed_addr !dbg !7 { + entry: + %local1 = alloca i32, align 4 + call void @llvm.dbg.value(metadata i32 %X, metadata !12, metadata !DIExpression()), !dbg !15 + %0 = bitcast i32* %local1 to i8*, !dbg !15 + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0), !dbg !15 + call void @llvm.dbg.value(metadata i32 5, metadata !14, metadata !DIExpression()), !dbg !15 + %call = tail call i32 (...) @check(), !dbg !15 + %tobool = icmp eq i32 %call, 0, !dbg !15 + br i1 %tobool, label %if.else, label %if.then, !dbg !15 + + if.then: ; preds = %entry + call void @llvm.dbg.value(metadata i32 4, metadata !13, metadata !DIExpression()), !dbg !15 + store i32 4, i32* %local1, align 4, !dbg !15, !tbaa !16 + call void @llvm.dbg.value(metadata i32* %local1, metadata !13, metadata !DIExpression(DW_OP_deref)), !dbg !15 + %call1 = call i32 @init(i32* nonnull %local1), !dbg !15 + call void @llvm.dbg.value(metadata i32 undef, metadata !14, metadata !DIExpression()), !dbg !15 + br label %if.end, !dbg !15 + + if.else: ; preds = %entry + call void @llvm.dbg.value(metadata i32 5, metadata !13, metadata !DIExpression()), !dbg !15 + store i32 5, i32* %local1, align 4, !dbg !15, !tbaa !16 + br label %if.end + + if.end: ; preds = %if.else, %if.then + %1 = bitcast i32* %local1 to i8*, !dbg !15 + call void @llvm.dbg.value(metadata i32 undef, metadata !14, metadata !DIExpression()), !dbg !15 + %call2 = call i32 (...) @init2(), !dbg !15 + call void @llvm.dbg.value(metadata i32 undef, metadata !14, metadata !DIExpression()), !dbg !15 + %2 = load i32, i32* %local1, align 4, !dbg !15, !tbaa !16 + call void @llvm.dbg.value(metadata i32 %2, metadata !13, metadata !DIExpression()), !dbg !15 + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %1), !dbg !15 + ret i32 %2, !dbg !15 + } + + ; Function Attrs: argmemonly nounwind + declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) + + declare dso_local i32 @check(...) local_unnamed_addr + + declare dso_local i32 @init(i32*) local_unnamed_addr + + declare dso_local i32 @init2(...) local_unnamed_addr + + ; Function Attrs: argmemonly nounwind + declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) + + ; Function Attrs: nounwind readnone speculatable + declare void @llvm.dbg.value(metadata, metadata, metadata) + + ; Function Attrs: nounwind + declare void @llvm.stackprotector(i8*, i8**) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) + !1 = !DIFile(filename: "dbg-stack-value-range.c", directory: "/") + !2 = !{} + !3 = !{i32 2, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 9.0.0 "} + !7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 11, type: !8, scopeLine: 11, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "X", arg: 1, scope: !7, file: !1, line: 11, type: !10) + !13 = !DILocalVariable(name: "local1", scope: !7, file: !1, line: 12, type: !10) + !14 = !DILocalVariable(name: "local2", scope: !7, file: !1, line: 12, type: !10) + !15 = !DILocation(line: 11, column: 13, scope: !7) + !16 = !{!17, !17, i64 0} + !17 = !{!"int", !18, i64 0} + !18 = !{!"omnipotent char", !19, i64 0} + !19 = !{!"Simple C/C++ TBAA"} + +... +--- +name: foo +liveins: [] +fixedStack: + - { id: 0, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: 0, + callee-saved-register: '$r14d', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } +stack: + - { id: 0, name: local1, type: default, offset: -20, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +constants: [] +machineFunctionInfo: {} +body: | + bb.0.entry: + successors: %bb.2(0x30000000), %bb.1(0x50000000) + + DBG_VALUE $edi, $noreg, !12, !DIExpression(), debug-location !15 + frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + CFI_INSTRUCTION offset $rbp, -16 + $rbp = frame-setup MOV64rr $rsp + CFI_INSTRUCTION def_cfa_register $rbp + $rsp = frame-setup SUB64ri8 $rsp, 16, implicit-def dead $eflags + DBG_VALUE 5, $noreg, !14, !DIExpression(), debug-location !15 + $r14d = MOV32ri 4, implicit-def $r14 + DBG_VALUE $r14d, $noreg, !13, !DIExpression(), debug-location !15 + dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al, debug-location !15 + CALL64pcrel32 @check, csr_64, implicit $rsp, implicit $ssp, implicit $al, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !15 + TEST32rr killed renamable $eax, renamable $eax, implicit-def $eflags, debug-location !15 + JCC_1 %bb.2, 4, implicit killed $eflags, debug-location !15 + + bb.1.if.then: + successors: %bb.2(0x80000000) + + MOV32mr $rbp, 1, $noreg, -4, $noreg, $r14d, debug-location !15 :: (store 4 into %ir.local1, !tbaa !16) + DBG_VALUE $rbp, $noreg, !13, !DIExpression(DW_OP_constu, 4, DW_OP_minus, DW_OP_deref), debug-location !15 + renamable $rdi = LEA64r $rbp, 1, $noreg, -4, $noreg + CALL64pcrel32 @init, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def dead $eax, debug-location !15 + DBG_VALUE $noreg, $noreg, !14, !DIExpression(), debug-location !15 + JMP_1 %bb.2 + + bb.2.if.end: + DBG_VALUE $noreg, $noreg, !14, !DIExpression(), debug-location !15 + dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al, debug-location !15 + CALL64pcrel32 @init2, csr_64, implicit $rsp, implicit $ssp, implicit $al, implicit-def $rsp, implicit-def $ssp, implicit-def dead $eax, debug-location !15 + DBG_VALUE $noreg, $noreg, !14, !DIExpression(), debug-location !15 + renamable $eax = MOV32rm $rbp, 1, $noreg, -4, $noreg, debug-location !15 :: (dereferenceable load 4 from %ir.local1, !tbaa !16) + $rsp = frame-destroy ADD64ri8 $rsp, 16, implicit-def dead $eflags, debug-location !15 + $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !15 + CFI_INSTRUCTION def_cfa $rsp, 8, debug-location !15 + RETQ $eax, debug-location !15 + +... Index: test/DebugInfo/X86/live-debug-values-constprop.mir =================================================================== --- test/DebugInfo/X86/live-debug-values-constprop.mir +++ test/DebugInfo/X86/live-debug-values-constprop.mir @@ -177,7 +177,7 @@ ; CHECK-NEXT: DBG_VALUE $eax ; CHECK-NEXT: JMP_1 %bb.3 ; CHECK-LABEL: bb.3.bb3 - ; CHECK-NOT: DBG_VALUE + ; CHECK: DBG_VALUE $noreg ; Test for there being a location-list gap between bb1 and bb2, the variable ; should not have a location over the ADD32ri. The range of the last entry ; should not cover the last block. @@ -241,7 +241,7 @@ ; CHECK-NEXT: DBG_VALUE 0 ; CHECK-NEXT: JMP_1 %bb.3 ; CHECK-LABEL: bb.3.bb3 - ; CHECK-NOT: DBG_VALUE + ; CHECK: DBG_VALUE $noreg ; Test for there being a location-list gap between bb1 and bb2, the variable ; should not have a location over the ADD32ri. The range of the last item ; should not cover the last block.