Index: llvm/test/DebugInfo/assignment-tracking/X86/DSE.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/DSE.ll @@ -0,0 +1,72 @@ +; RUN: llc %s -stop-before=finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +; Check basic lowering behaviour of dbg.assign intrinsics. The first +; assignment to `local`, which has been DSE'd, should be represented with a +; constant value DBG_VALUE. The second assignment should have a DBG_VALUE +; describing the stack home of the variable. + +; $ cat test.c +; void esc(int*); +; void fun() { +; int local = 5; +; // ^ killed by v +; local = 6; +; esc(&local); +; } +; $ clang -O2 -g -emit -llvm -S test.c -o - + +; CHECK: ![[LOCAL:[0-9]+]] = !DILocalVariable(name: "local", +; CHECK: DBG_VALUE 5, $noreg, ![[LOCAL]], !DIExpression(), debug-location ![[DBG:[0-9]+]] +; CHECK-NEXT: MOV32mi [[DEST:.*]], 1, $noreg, 0, $noreg, 6 +; CHECK-NEXT: DBG_VALUE [[DEST]], $noreg, ![[LOCAL]], !DIExpression(DW_OP_deref), debug-location ![[DBG]] + +target triple = "x86_64-unknown-linux-gnu" + +define dso_local void @fun() local_unnamed_addr !dbg !7 { +entry: + %local = alloca i32, align 4 + call void @llvm.dbg.assign(metadata i32 5, metadata !11, metadata !DIExpression(), metadata !30, metadata ptr %local, metadata !DIExpression()), !dbg !16 + store i32 6, ptr %local, align 4, !dbg !23, !DIAssignID !31 + call void @llvm.dbg.assign(metadata i32 6, metadata !11, metadata !DIExpression(), metadata !31, metadata ptr %local, metadata !DIExpression()), !dbg !16 + call void @esc(ptr nonnull %local), !dbg !24 + ret void, !dbg !25 +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +declare !dbg !26 dso_local void @esc(ptr) local_unnamed_addr + +!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 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "local", scope: !7, file: !1, line: 3, type: !12) +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !12) +!15 = !DILocation(line: 3, column: 3, scope: !7) +!16 = !DILocation(line: 0, scope: !7) +!17 = !DILocation(line: 4, column: 3, scope: !7) +!18 = !DILocation(line: 4, column: 16, scope: !7) +!23 = !DILocation(line: 5, column: 9, scope: !7) +!24 = !DILocation(line: 6, column: 3, scope: !7) +!25 = !DILocation(line: 7, column: 1, scope: !7) +!26 = !DISubprogram(name: "esc", scope: !1, file: !1, line: 1, type: !27, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!27 = !DISubroutineType(types: !28) +!28 = !{null, !29} +!29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!30 = distinct !DIAssignID() +!31 = distinct !DIAssignID() + Index: llvm/test/DebugInfo/assignment-tracking/X86/dbg-phi-produces-undef.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/dbg-phi-produces-undef.ll @@ -0,0 +1,106 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; Hand written test because the scenario is unlikely. Check that the "value" +;; of a debug def PHIs is "undef" (because we don't actually track PHIs). +;; +;; entry: +;; memdef 5, !1 +;; dbgdef !1 +;; br if.then, exit +;; +;; if.then: +;; memdef 0, !1 +;; dbgdef !1 +;; br exit +;; +;; exit: +;; ; <-- Dbg=!1, Stack=!1, Loc=Mem +;; memddef 1, !2 ; <-- Dbg=!1, Stack=!2, Loc=Val(undef) @HERE +;; call +;; dbgdef !2 +;; +;; Check that the dbg.value inserted at @HERE is undef because there's no +;; appropriate alternative value to choose. + +; CHECK: bb.0.entry: +; CHECK: DBG_VALUE %stack.0.c, $noreg, ![[var:[0-9]+]], !DIExpression(DW_OP_deref), debug-location +; CHECK-NEXT: MOV8mi %stack.0.c, 1, $noreg, 0, $noreg, 5, debug-location +; CHECL-NEXT: DBG_VALUE %stack.0.c, $noreg, ![[var]], !DIExpression(DW_OP_deref), debug-location + +; CHECK: bb.1.if.then: +; CHECK: MOV8mi %stack.0.c, 1, $noreg, 0, $noreg, 0 +; CHECK: DBG_VALUE %stack.0.c, $noreg, ![[var]], !DIExpression(DW_OP_deref), debug-location + +; CHECK: bb.2.exit: +; CHECK-NEXT: MOV8mi %stack.0.c, 1, $noreg, 0, $noreg, 1 +;; @HERE +; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[var]], !DIExpression() +; CHECK: CALL64pcrel32 @d +; CHECK-NEXT: ADJCALLSTACKUP64 +; 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" + +; Function Attrs: nounwind uwtable +define dso_local void @b(i1 %cond) local_unnamed_addr #0 !dbg !7 { +entry: + %c = alloca i8, align 1, !DIAssignID !13 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !13, metadata ptr %c, metadata !DIExpression()), !dbg !14 + store i8 5, ptr %c, align 1, !dbg !16, !DIAssignID !31 + call void @llvm.dbg.assign(metadata i8 5, metadata !11, metadata !DIExpression(), metadata !31, metadata ptr %c, metadata !DIExpression()), !dbg !14 + br i1 %cond, label %if.then, label %exit + +if.then: + tail call void (...) @d() #4, !dbg !21 + store i8 0, ptr %c, align 1, !dbg !16, !DIAssignID !31 + call void @llvm.dbg.assign(metadata i8 0, metadata !11, metadata !DIExpression(), metadata !31, metadata ptr %c, metadata !DIExpression()), !dbg !14 + br label %exit + +exit: + store i8 1, ptr %c, align 1, !dbg !16, !DIAssignID !20 + tail call void (...) @d() #4, !dbg !21 + call void @llvm.dbg.assign(metadata i8 1, metadata !11, metadata !DIExpression(), metadata !20, metadata ptr %c, metadata !DIExpression()), !dbg !14 + call void @a(ptr nonnull %c) #4, !dbg !22 + ret void, !dbg !23 +} + + +declare !dbg !24 dso_local void @d(...) local_unnamed_addr #2 +declare !dbg !27 dso_local void @a(ptr) local_unnamed_addr #2 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +!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 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "reduce.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "b", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 4, type: !12) +!12 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!13 = distinct !DIAssignID() +!14 = !DILocation(line: 0, scope: !7) +!15 = !DILocation(line: 4, column: 3, scope: !7) +!16 = !DILocation(line: 4, column: 8, scope: !7) +!20 = distinct !DIAssignID() +!21 = !DILocation(line: 5, column: 3, scope: !7) +!22 = !DILocation(line: 6, column: 3, scope: !7) +!23 = !DILocation(line: 7, column: 1, scope: !7) +!24 = !DISubprogram(name: "d", scope: !1, file: !1, line: 2, type: !25, spFlags: DISPFlagOptimized, retainedNodes: !2) +!25 = !DISubroutineType(types: !26) +!26 = !{null, null} +!27 = !DISubprogram(name: "a", scope: !1, file: !1, line: 1, type: !28, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!28 = !DISubroutineType(types: !29) +!29 = !{null, !30} +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!31 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/diamond-1.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/diamond-1.ll @@ -0,0 +1,123 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; cat test.cpp +;; void d(); +;; void e(); +;; void es(int*); +;; int f(int a) { +;; if (a) { +;; e(); +;; a = 100; +;; } else { +;; d(); +;; a = 500; +;; } +;; es(&a); +;; return a; +;; } +;; $ clang++ test.cpp -S -emit-llvm -Xclang -fexperimental-assignment-tracking + +;; Check that the memory location is selected after the store in if.end: +;; entry: +;; a = param-value +;; if.then: +;; a = 100 +;; if.else: +;; a = 500 +;; if.end: +;; store (phi if.then: 100, if.else: 500) +;; a = in memory + +; CHECK-DAG: ![[VAR:[0-9]+]] = !DILocalVariable(name: "a", + +; CHECK: bb.0.entry: +; CHECK: DBG_VALUE $edi, $noreg, ![[VAR]], !DIExpression() + +; CHECK: bb.1.if.then: +; CHECK: DBG_VALUE 100, $noreg, ![[VAR]], !DIExpression() + +; CHECK: bb.2.if.else: +; CHECK: DBG_VALUE 500, $noreg, ![[VAR]], !DIExpression() + +; CHECK: bb.3.if.end: +; CHECK-NEXT: %0:gr32 = PHI %2, %bb.1, %3, %bb.2 +; CHECK-NEXT: MOV32mr %stack.0.a.addr, 1, $noreg, 0, $noreg, %0 +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[VAR]], !DIExpression(DW_OP_deref) + +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: mustprogress uwtable +define dso_local noundef i32 @_Z1fi(i32 noundef %a) local_unnamed_addr #0 !dbg !7 { +entry: + %a.addr = alloca i32, align 4, !DIAssignID !13 + call void @llvm.dbg.assign(metadata i1 undef, metadata !12, metadata !DIExpression(), metadata !13, metadata ptr %a.addr, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.assign(metadata i32 %a, metadata !12, metadata !DIExpression(), metadata !15, metadata ptr %a.addr, metadata !DIExpression()), !dbg !14 + %tobool.not = icmp eq i32 %a, 0, !dbg !16 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !18 + +if.then: ; preds = %entry + tail call void @_Z1ev(), !dbg !19 + call void @llvm.dbg.assign(metadata i32 100, metadata !12, metadata !DIExpression(), metadata !21, metadata ptr %a.addr, metadata !DIExpression()), !dbg !14 + br label %if.end, !dbg !22 + +if.else: ; preds = %entry + tail call void @_Z1dv(), !dbg !23 + call void @llvm.dbg.assign(metadata i32 500, metadata !12, metadata !DIExpression(), metadata !21, metadata ptr %a.addr, metadata !DIExpression()), !dbg !14 + br label %if.end + +if.end: ; preds = %if.else, %if.then + %storemerge = phi i32 [ 500, %if.else ], [ 100, %if.then ], !dbg !25 + store i32 %storemerge, ptr %a.addr, align 4, !dbg !25, !DIAssignID !21 + call void @_Z2esPi(ptr noundef nonnull %a.addr), !dbg !30 + %0 = load i32, ptr %a.addr, align 4, !dbg !31 + ret i32 %0, !dbg !32 +} + +declare !dbg !33 dso_local void @_Z1ev() local_unnamed_addr #1 +declare !dbg !37 dso_local void @_Z1dv() local_unnamed_addr #1 +declare !dbg !38 dso_local void @_Z2esPi(ptr noundef) local_unnamed_addr #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{!"clang version 14.0.0"} +!7 = distinct !DISubprogram(name: "f", linkageName: "_Z1fi", scope: !1, file: !1, line: 4, type: !8, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, 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} +!12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 4, type: !10) +!13 = distinct !DIAssignID() +!14 = !DILocation(line: 0, scope: !7) +!15 = distinct !DIAssignID() +!16 = !DILocation(line: 5, column: 7, scope: !17) +!17 = distinct !DILexicalBlock(scope: !7, file: !1, line: 5, column: 7) +!18 = !DILocation(line: 5, column: 7, scope: !7) +!19 = !DILocation(line: 6, column: 5, scope: !20) +!20 = distinct !DILexicalBlock(scope: !17, file: !1, line: 5, column: 10) +!21 = distinct !DIAssignID() +!22 = !DILocation(line: 8, column: 3, scope: !20) +!23 = !DILocation(line: 9, column: 5, scope: !24) +!24 = distinct !DILexicalBlock(scope: !17, file: !1, line: 8, column: 10) +!25 = !DILocation(line: 0, scope: !17) +!30 = !DILocation(line: 12, column: 3, scope: !7) +!31 = !DILocation(line: 13, column: 10, scope: !7) +!32 = !DILocation(line: 13, column: 3, scope: !7) +!33 = !DISubprogram(name: "e", linkageName: "_Z1ev", scope: !1, file: !1, line: 2, type: !34, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !36) +!34 = !DISubroutineType(types: !35) +!35 = !{null} +!36 = !{} +!37 = !DISubprogram(name: "d", linkageName: "_Z1dv", scope: !1, file: !1, line: 1, type: !34, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !36) +!38 = !DISubprogram(name: "es", linkageName: "_Z2esPi", scope: !1, file: !1, line: 3, type: !39, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !36) +!39 = !DISubroutineType(types: !40) +!40 = !{null, !41} +!41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) Index: llvm/test/DebugInfo/assignment-tracking/X86/diamond-2.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/diamond-2.ll @@ -0,0 +1,111 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; Same as diamond-1.ll except that the DIAssignID attached to the store has +;; been deleted. In this case, we expect the same output as for diamond-1.ll +;; because we choose to interpret stores to stack slots that don't link to +;; debug intrinsics for a variable in the stack slot as a though they're linked +;; to one placed immediately after. +;; +;; entry: +;; a = param-value +;; if.then: +;; a = 100 +;; if.else: +;; a = 500 +;; if.end: +;; store (phi if.then: 100, if.else: 500) +;; a = in memory + +; CHECK-DAG: ![[VAR:[0-9]+]] = !DILocalVariable(name: "a", + +; CHECK: bb.0.entry: +; CHECK: DBG_VALUE $edi, $noreg, ![[VAR]], !DIExpression() + +; CHECK: bb.1.if.then: +; CHECK: DBG_VALUE 100, $noreg, ![[VAR]], !DIExpression() + +; CHECK: bb.2.if.else: +; CHECK: DBG_VALUE 500, $noreg, ![[VAR]], !DIExpression() + +; CHECK: bb.3.if.end: +; CHECK-NEXT: %0:gr32 = PHI %2, %bb.1, %3, %bb.2 +; CHECK-NEXT: MOV32mr %stack.0.a.addr, 1, $noreg, 0, $noreg, %0 +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[VAR]], !DIExpression(DW_OP_deref) + +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: mustprogress uwtable +define dso_local noundef i32 @_Z1fi(i32 noundef %a) local_unnamed_addr #0 !dbg !7 { +entry: + %a.addr = alloca i32, align 4, !DIAssignID !13 + call void @llvm.dbg.assign(metadata i1 undef, metadata !12, metadata !DIExpression(), metadata !13, metadata ptr %a.addr, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.assign(metadata i32 %a, metadata !12, metadata !DIExpression(), metadata !15, metadata ptr %a.addr, metadata !DIExpression()), !dbg !14 + %tobool.not = icmp eq i32 %a, 0, !dbg !16 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !18 + +if.then: ; preds = %entry + tail call void @_Z1ev(), !dbg !19 + call void @llvm.dbg.assign(metadata i32 100, metadata !12, metadata !DIExpression(), metadata !21, metadata ptr %a.addr, metadata !DIExpression()), !dbg !14 + br label %if.end, !dbg !22 + +if.else: ; preds = %entry + tail call void @_Z1dv(), !dbg !23 + call void @llvm.dbg.assign(metadata i32 500, metadata !12, metadata !DIExpression(), metadata !21, metadata ptr %a.addr, metadata !DIExpression()), !dbg !14 + br label %if.end + +if.end: ; preds = %if.else, %if.then + %storemerge = phi i32 [ 500, %if.else ], [ 100, %if.then ], !dbg !25 + store i32 %storemerge, ptr %a.addr, align 4, !dbg !25 + call void @_Z2esPi(ptr noundef nonnull %a.addr), !dbg !30 + %0 = load i32, ptr %a.addr, align 4, !dbg !31 + ret i32 %0, !dbg !32 +} + +declare !dbg !33 dso_local void @_Z1ev() local_unnamed_addr #1 +declare !dbg !37 dso_local void @_Z1dv() local_unnamed_addr #1 +declare !dbg !38 dso_local void @_Z2esPi(ptr noundef) local_unnamed_addr #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{!"clang version 14.0.0"} +!7 = distinct !DISubprogram(name: "f", linkageName: "_Z1fi", scope: !1, file: !1, line: 4, type: !8, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, 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} +!12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 4, type: !10) +!13 = distinct !DIAssignID() +!14 = !DILocation(line: 0, scope: !7) +!15 = distinct !DIAssignID() +!16 = !DILocation(line: 5, column: 7, scope: !17) +!17 = distinct !DILexicalBlock(scope: !7, file: !1, line: 5, column: 7) +!18 = !DILocation(line: 5, column: 7, scope: !7) +!19 = !DILocation(line: 6, column: 5, scope: !20) +!20 = distinct !DILexicalBlock(scope: !17, file: !1, line: 5, column: 10) +!21 = distinct !DIAssignID() +!22 = !DILocation(line: 8, column: 3, scope: !20) +!23 = !DILocation(line: 9, column: 5, scope: !24) +!24 = distinct !DILexicalBlock(scope: !17, file: !1, line: 8, column: 10) +!25 = !DILocation(line: 0, scope: !17) +!30 = !DILocation(line: 12, column: 3, scope: !7) +!31 = !DILocation(line: 13, column: 10, scope: !7) +!32 = !DILocation(line: 13, column: 3, scope: !7) +!33 = !DISubprogram(name: "e", linkageName: "_Z1ev", scope: !1, file: !1, line: 2, type: !34, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !36) +!34 = !DISubroutineType(types: !35) +!35 = !{null} +!36 = !{} +!37 = !DISubprogram(name: "d", linkageName: "_Z1dv", scope: !1, file: !1, line: 1, type: !34, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !36) +!38 = !DISubprogram(name: "es", linkageName: "_Z2esPi", scope: !1, file: !1, line: 3, type: !39, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !36) +!39 = !DISubroutineType(types: !40) +!40 = !{null, !41} +!41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) Index: llvm/test/DebugInfo/assignment-tracking/X86/diamond-3.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/diamond-3.ll @@ -0,0 +1,117 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG_ + +;; Hand written to test scenario we can definitely run into in the wild. This +;; file name includes "diamond" because the idea is that we lose (while +;; optimizing) one of the diamond branches which was empty except for a debug +;; intrinsic. In this case, the debug intrinsic linked to the common-and-sunk +;; store now in if.end. So we've got this: +;; +;; entry: ; -> br if.then, if.end +;; mem(a) = !19 +;; dbg(a) = !21 ; dbg and mem disagree, don't use mem loc. +;; if.then: ; -> br if.end +;; dbg(a) = !20 +;; if.end: +;; mem(a) = !20 ; two preds disagree that !20 is the last assignment, don't +;; ; use mem loc. +;; ; This feels highly unfortunate, and highlights the need to reinstate the +;; ; memory location at call sites leaking the address (in an ideal world, +;; ; the memory location would always be in use at that point and so this +;; ; wouldn't be necessary). +;; esc(a) ; force the memory location + +;; In real world examples this is caused by InstCombine sinking common code +;; followed by SimplifyCFG deleting empty-except-for-dbg blocks. + +; CHECK-DAG: ![[A:[0-9]+]] = !DILocalVariable(name: "a", +; CHECK-LABEL: bb.0.entry: +; CHECK: DBG_VALUE $edi, $noreg, ![[A]], !DIExpression() +; CHECK-LABEL: bb.1.if.then: +; CHECK: DBG_VALUE 0, $noreg, ![[A]], !DIExpression() + +;; === TODO / WISHLIST === +; LEBAL-KCEHC: bb.2.if.end: +; KCEHC: CALL64pcrel32 target-flags(x86-plt) @es +; KCEHC: DBG_VALUE %stack.0.a.addr, $noreg, ![[A]], !DIExpression(DW_OP_deref) + +target triple = "x86_64-unknown-linux-gnu" + +@g = dso_local local_unnamed_addr global ptr null, align 8, !dbg !0 + +define dso_local noundef i32 @_Z1fiii(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 !dbg !12 { +entry: + %a.addr = alloca i32, align 4, !DIAssignID !19 + call void @llvm.dbg.assign(metadata i1 undef, metadata !16, metadata !DIExpression(), metadata !19, metadata ptr %a.addr, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.assign(metadata i32 %a, metadata !16, metadata !DIExpression(), metadata !21, metadata ptr %a.addr, metadata !DIExpression()), !dbg !20 + %tobool.not = icmp eq i32 %c, 0 + br i1 %tobool.not, label %if.then, label %if.end + +if.then: + call void @e() + call void @llvm.dbg.assign(metadata i32 0, metadata !16, metadata !DIExpression(), metadata !22, metadata ptr %a.addr, metadata !DIExpression()), !dbg !20 + br label %if.end + +if.end: ; preds = %do.body + store i32 0, ptr %a.addr, align 4, !DIAssignID !22 + call void @es(ptr %a.addr) + ret i32 0 +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 +declare void @e() +declare void @es(ptr) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 4, type: !5, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{!0} +!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64) +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 7, !"Dwarf Version", i32 5} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 7, !"uwtable", i32 1} +!11 = !{!"clang version 14.0.0"} +!12 = distinct !DISubprogram(name: "f", linkageName: "_Z1fiii", scope: !3, file: !3, line: 5, type: !13, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!6, !6, !6, !6} +!15 = !{!16, !17, !18} +!16 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !3, line: 5, type: !6) +!17 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !3, line: 5, type: !6) +!18 = !DILocalVariable(name: "c", arg: 3, scope: !12, file: !3, line: 5, type: !6) +!19 = distinct !DIAssignID() +!20 = !DILocation(line: 0, scope: !12) +!21 = distinct !DIAssignID() +!22 = distinct !DIAssignID() +!23 = distinct !DIAssignID() +!28 = distinct !DIAssignID() +!29 = !DILocation(line: 6, column: 3, scope: !12) +!30 = !DILocation(line: 8, column: 7, scope: !31) +!31 = distinct !DILexicalBlock(scope: !12, file: !3, line: 6, column: 6) +!32 = distinct !DIAssignID() +!33 = !DILocation(line: 10, column: 5, scope: !31) +!34 = !DILocation(line: 11, column: 12, scope: !12) +!35 = !DILocation(line: 11, column: 3, scope: !31) +!36 = distinct !{!36, !29, !37, !38} +!37 = !DILocation(line: 11, column: 15, scope: !12) +!38 = !{!"llvm.loop.mustprogress"} +!39 = !DILocation(line: 12, column: 3, scope: !12) +!40 = !DILocation(line: 13, column: 10, scope: !12) +!41 = !DILocation(line: 13, column: 12, scope: !12) +!42 = !DILocation(line: 13, column: 3, scope: !12) +!43 = !DISubprogram(name: "e", linkageName: "_Z1ev", scope: !3, file: !3, line: 2, type: !44, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !46) +!44 = !DISubroutineType(types: !45) +!45 = !{null} +!46 = !{} +!47 = !DISubprogram(name: "d", linkageName: "_Z1dv", scope: !3, file: !3, line: 1, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !46) +!48 = !DISubroutineType(types: !49) +!49 = !{!6} +!50 = !DISubprogram(name: "es", linkageName: "_Z2esPi", scope: !3, file: !3, line: 3, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !46) +!51 = !DISubroutineType(types: !52) +!52 = !{null, !5} Index: llvm/test/DebugInfo/assignment-tracking/X86/lit.local.cfg =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'X86' in config.root.targets: + config.unsupported = True Index: llvm/test/DebugInfo/assignment-tracking/X86/loop-hoist.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/loop-hoist.ll @@ -0,0 +1,118 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG_ + +;; $ cat test.cpp +;; int d(); +;; void e(); +;; void es(int*); +;; int *g; +;; int f(int a, int b, int c) { +;; do { +;; /* stuff */ +;; c *= c; +;; a = b; +;; e(); +;; } while (d()); +;; es(&a); +;; return a + c; +;; } + +;; The variable of interest is `a`, which has a store that is hoisted out of the +;; loop into the entry BB. Check the memory location is not used after the +;; hoisted store until the assignment position within the loop. + +; CHECK-DAG: ![[A:[0-9]+]] = !DILocalVariable(name: "a", + +; CHECK: bb.0.entry: +; CHECK: DBG_VALUE $edi, $noreg, ![[A]], !DIExpression() + +; CHECK: bb.1.do.body: +; CHECK: DBG_VALUE %stack.0.a.addr, $noreg, ![[A]], !DIExpression(DW_OP_deref) + +target triple = "x86_64-unknown-linux-gnu" + +@g = dso_local local_unnamed_addr global ptr null, align 8, !dbg !0 + +define dso_local noundef i32 @_Z1fiii(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 !dbg !12 { +entry: + %a.addr = alloca i32, align 4, !DIAssignID !19 + call void @llvm.dbg.assign(metadata i1 undef, metadata !16, metadata !DIExpression(), metadata !19, metadata ptr %a.addr, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.assign(metadata i32 %a, metadata !16, metadata !DIExpression(), metadata !21, metadata ptr %a.addr, metadata !DIExpression()), !dbg !20 + store i32 %b, ptr %a.addr, align 4, !DIAssignID !28 + br label %do.body, !dbg !29 + +do.body: ; preds = %do.body, %entry + %c.addr.0 = phi i32 [ %c, %entry ], [ %mul, %do.body ] + %mul = mul nsw i32 %c.addr.0, %c.addr.0, !dbg !30 + call void @llvm.dbg.assign(metadata i32 %b, metadata !16, metadata !DIExpression(), metadata !28, metadata ptr %a.addr, metadata !DIExpression()), !dbg !20 + tail call void @_Z1ev(), !dbg !33 + %call = tail call noundef i32 @_Z1dv(), !dbg !34 + %tobool.not = icmp eq i32 %call, 0, !dbg !34 + br i1 %tobool.not, label %do.end, label %do.body, !dbg !35, !llvm.loop !36 + +do.end: ; preds = %do.body + call void @_Z2esPi(ptr noundef nonnull %a.addr), !dbg !39 + %0 = load i32, ptr %a.addr, align 4, !dbg !40 + %add = add nsw i32 %0, %mul, !dbg !41 + ret i32 %add, !dbg !42 +} + +declare !dbg !43 dso_local void @_Z1ev() local_unnamed_addr #1 +declare !dbg !47 dso_local noundef i32 @_Z1dv() local_unnamed_addr #1 +declare !dbg !50 dso_local void @_Z2esPi(ptr noundef) local_unnamed_addr #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 +declare void @llvm.dbg.value(metadata, metadata, metadata) #3 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 4, type: !5, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{!0} +!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64) +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 7, !"Dwarf Version", i32 5} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 7, !"uwtable", i32 1} +!11 = !{!"clang version 14.0.0"} +!12 = distinct !DISubprogram(name: "f", linkageName: "_Z1fiii", scope: !3, file: !3, line: 5, type: !13, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!6, !6, !6, !6} +!15 = !{!16, !17, !18} +!16 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !3, line: 5, type: !6) +!17 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !3, line: 5, type: !6) +!18 = !DILocalVariable(name: "c", arg: 3, scope: !12, file: !3, line: 5, type: !6) +!19 = distinct !DIAssignID() +!20 = !DILocation(line: 0, scope: !12) +!21 = distinct !DIAssignID() +!22 = distinct !DIAssignID() +!23 = distinct !DIAssignID() +!28 = distinct !DIAssignID() +!29 = !DILocation(line: 6, column: 3, scope: !12) +!30 = !DILocation(line: 8, column: 7, scope: !31) +!31 = distinct !DILexicalBlock(scope: !12, file: !3, line: 6, column: 6) +!32 = distinct !DIAssignID() +!33 = !DILocation(line: 10, column: 5, scope: !31) +!34 = !DILocation(line: 11, column: 12, scope: !12) +!35 = !DILocation(line: 11, column: 3, scope: !31) +!36 = distinct !{!36, !29, !37, !38} +!37 = !DILocation(line: 11, column: 15, scope: !12) +!38 = !{!"llvm.loop.mustprogress"} +!39 = !DILocation(line: 12, column: 3, scope: !12) +!40 = !DILocation(line: 13, column: 10, scope: !12) +!41 = !DILocation(line: 13, column: 12, scope: !12) +!42 = !DILocation(line: 13, column: 3, scope: !12) +!43 = !DISubprogram(name: "e", linkageName: "_Z1ev", scope: !3, file: !3, line: 2, type: !44, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !46) +!44 = !DISubroutineType(types: !45) +!45 = !{null} +!46 = !{} +!47 = !DISubprogram(name: "d", linkageName: "_Z1dv", scope: !3, file: !3, line: 1, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !46) +!48 = !DISubroutineType(types: !49) +!49 = !{!6} +!50 = !DISubprogram(name: "es", linkageName: "_Z2esPi", scope: !3, file: !3, line: 3, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !46) +!51 = !DISubroutineType(types: !52) +!52 = !{null, !5} Index: llvm/test/DebugInfo/assignment-tracking/X86/loop-sink.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/loop-sink.ll @@ -0,0 +1,114 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG + +;; Tiny loop with a store sunk out of it: +;; void e(); +;; void es(int*); +;; int *g; +;; int getInt(); +;; int f(int a, int b) { +;; int z = getInt(); +;; while (g) { +;; e(); +;; a = z; +;; } +;; es(&a); +;; return a; +;; } +;; +;; Store to `a` has been sunk out the loop - there's a dbg.assign left in the +;; loop that is linked the store that is now outside it. Check that the memory +;; location is not used inside the loop and is reinstated after the sunk store. + +; CHECK-DAG: ![[A:[0-9]+]] = !DILocalVariable(name: "a", + +; CHECK-LABEL: bb.0.entry: +; CHECK: DBG_VALUE $edi, $noreg, ![[A]], !DIExpression() +; CHECK: CALL64pcrel32 @getInt{{.*}}debug-instr-number 1 + +; CHECK-LABEL: bb.2.while.body: +; CHECK: DBG_INSTR_REF 1, 6, ![[A]], !DIExpression() + +; CHECK-LABEL: bb.3.while.end: +; CHECK: MOV32mr %stack.0.a.addr, 1, $noreg, 0, $noreg, %1 +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[A]], !DIExpression(DW_OP_deref) + +target triple = "x86_64-unknown-linux-gnu" + +@g = dso_local local_unnamed_addr global ptr null, align 8, !dbg !0 + +; Function Attrs: mustprogress uwtable +define dso_local noundef i32 @_Z1fii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 !dbg !12 { +entry: + %a.addr = alloca i32, align 4, !DIAssignID !18 + call void @llvm.dbg.assign(metadata i1 undef, metadata !16, metadata !DIExpression(), metadata !18, metadata ptr %a.addr, metadata !DIExpression()), !dbg !19 + call void @llvm.dbg.assign(metadata i32 %a, metadata !16, metadata !DIExpression(), metadata !20, metadata ptr %a.addr, metadata !DIExpression()), !dbg !19 + %z = call i32 @getInt() + %0 = load ptr, ptr @g, align 8, !dbg !22 + %tobool.not1 = icmp eq ptr %0, null, !dbg !22 + br i1 %tobool.not1, label %while.end, label %while.body, !dbg !27 + +while.body: ; preds = %entry, %while.body + tail call void @_Z1ev(), !dbg !28 + call void @llvm.dbg.assign(metadata i32 %z, metadata !16, metadata !DIExpression(), metadata !20, metadata ptr %a.addr, metadata !DIExpression()), !dbg !19 + %1 = load ptr, ptr @g, align 8, !dbg !22 + %tobool.not = icmp eq ptr %1, null, !dbg !22 + br i1 %tobool.not, label %while.end, label %while.body, !dbg !27, !llvm.loop !30 + +while.end: ; preds = %while.body, %entry + %storemerge.lcssa = phi i32 [ %a, %entry ], [ %b, %while.body ] + store i32 %storemerge.lcssa, ptr %a.addr, align 4, !DIAssignID !20 + call void @_Z2esPi(ptr noundef nonnull %a.addr), !dbg !35 + %2 = load i32, ptr %a.addr, align 4, !dbg !36 + %r = add i32 %2, %z + ret i32 %r, !dbg !37 +} + +declare !dbg !38 dso_local void @_Z1ev() local_unnamed_addr #1 +declare !dbg !42 dso_local void @_Z2esPi(ptr noundef) local_unnamed_addr #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 +declare dso_local i32 @getInt() + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 4, type: !5, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{!0} +!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64) +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 7, !"Dwarf Version", i32 5} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 7, !"uwtable", i32 1} +!11 = !{!"clang version 14.0.0"} +!12 = distinct !DISubprogram(name: "f", linkageName: "_Z1fii", scope: !3, file: !3, line: 5, type: !13, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!6, !6, !6} +!15 = !{!16, !17} +!16 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !3, line: 5, type: !6) +!17 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !3, line: 5, type: !6) +!18 = distinct !DIAssignID() +!19 = !DILocation(line: 0, scope: !12) +!20 = distinct !DIAssignID() +!21 = distinct !DIAssignID() +!22 = !DILocation(line: 6, column: 10, scope: !12) +!27 = !DILocation(line: 6, column: 3, scope: !12) +!28 = !DILocation(line: 7, column: 5, scope: !29) +!29 = distinct !DILexicalBlock(scope: !12, file: !3, line: 6, column: 13) +!30 = distinct !{!30, !27, !31, !32} +!31 = !DILocation(line: 9, column: 3, scope: !12) +!32 = !{!"llvm.loop.mustprogress"} +!35 = !DILocation(line: 10, column: 3, scope: !12) +!36 = !DILocation(line: 11, column: 10, scope: !12) +!37 = !DILocation(line: 11, column: 3, scope: !12) +!38 = !DISubprogram(name: "e", linkageName: "_Z1ev", scope: !3, file: !3, line: 2, type: !39, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !41) +!39 = !DISubroutineType(types: !40) +!40 = !{null} +!41 = !{} +!42 = !DISubprogram(name: "es", linkageName: "_Z2esPi", scope: !3, file: !3, line: 3, type: !43, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !41) +!43 = !DISubroutineType(types: !44) +!44 = !{null, !5} Index: llvm/test/DebugInfo/assignment-tracking/X86/loop-unroll.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/loop-unroll.ll @@ -0,0 +1,88 @@ +; RUN: llc %s -stop-after=finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s +;; +;; Backend counterpart to ../Generic/dbg-assign-loop-unroll. This IR was +;; generated by running `opt -loop-unroll -S` on the IR in that test. +;; +;; Check that the backend can handle a mesh of dbg.assign and linked +;; instructions. +;; +;; Generated from the following source: +;; void esc(int*); +;; void d(int p) { +;; for (int i = 0; i < 2; ++i) { +;; p = i; +;; esc(&p); +;; } +;; } + +; CHECK: ![[p:[0-9]+]] = !DILocalVariable(name: "p", + +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: p.addr, type: default, offset: 0, size: 4, alignment: 4, +; CHECK-NEXT: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK-NEXT: debug-info-variable: '![[p]]', debug-info-expression: '!DIExpression()', +; CHECK-NEXT: debug-info-location: '!{{.+}}' } + +target triple = "x86_64-unknown-linux-gnu" + +define dso_local void @_Z1di(i32 %p) local_unnamed_addr !dbg !7 { +entry: + %p.addr = alloca i32, align 4 + store i32 %p, ptr %p.addr, align 4, !DIAssignID !19 + call void @llvm.dbg.assign(metadata i32 %p, metadata !12, metadata !DIExpression(), metadata !19, metadata ptr %p.addr, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.assign(metadata i32 0, metadata !13, metadata !DIExpression(), metadata !21, metadata ptr undef, metadata !DIExpression()), !dbg !22 + br label %for.body, !dbg !23 + +for.body: ; preds = %entry + store i32 0, ptr %p.addr, align 4, !dbg !24, !DIAssignID !27 + call void @llvm.dbg.assign(metadata i32 0, metadata !12, metadata !DIExpression(), metadata !27, metadata ptr %p.addr, metadata !DIExpression()), !dbg !24 + call void @_Z3escPi(ptr nonnull %p.addr), !dbg !28 + call void @llvm.dbg.assign(metadata i32 1, metadata !13, metadata !DIExpression(), metadata !29, metadata ptr undef, metadata !DIExpression()), !dbg !30 + store i32 1, ptr %p.addr, align 4, !dbg !24, !DIAssignID !27 + call void @llvm.dbg.assign(metadata i32 1, metadata !12, metadata !DIExpression(), metadata !27, metadata ptr %p.addr, metadata !DIExpression()), !dbg !24 + call void @_Z3escPi(ptr nonnull %p.addr), !dbg !28 + call void @llvm.dbg.assign(metadata i32 2, metadata !13, metadata !DIExpression(), metadata !29, metadata ptr undef, metadata !DIExpression()), !dbg !30 + ret void, !dbg !31 +} + +declare !dbg !32 dso_local void @_Z3escPi(ptr) local_unnamed_addr +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "d", linkageName: "_Z1di", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{null, !10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12, !13} +!12 = !DILocalVariable(name: "p", arg: 1, scope: !7, file: !1, line: 2, type: !10) +!13 = !DILocalVariable(name: "i", scope: !14, file: !1, line: 3, type: !10) +!14 = distinct !DILexicalBlock(scope: !7, file: !1, line: 3, column: 3) +!19 = distinct !DIAssignID() +!20 = !DILocation(line: 0, scope: !7) +!21 = distinct !DIAssignID() +!22 = !DILocation(line: 3, column: 12, scope: !14) +!23 = !DILocation(line: 3, column: 3, scope: !14) +!24 = !DILocation(line: 4, column: 7, scope: !25) +!25 = distinct !DILexicalBlock(scope: !26, file: !1, line: 3, column: 31) +!26 = distinct !DILexicalBlock(scope: !14, file: !1, line: 3, column: 3) +!27 = distinct !DIAssignID() +!28 = !DILocation(line: 5, column: 5, scope: !25) +!29 = distinct !DIAssignID() +!30 = !DILocation(line: 3, column: 26, scope: !26) +!31 = !DILocation(line: 7, column: 1, scope: !7) +!32 = !DISubprogram(name: "esc", linkageName: "_Z3escPi", scope: !1, file: !1, line: 1, type: !33, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!33 = !DISubroutineType(types: !34) +!34 = !{null, !35} +!35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) Index: llvm/test/DebugInfo/assignment-tracking/X86/lower-offset-expression.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/lower-offset-expression.ll @@ -0,0 +1,68 @@ +; RUN: llc %s -stop-after=finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; Handwritten test. + +;; Here we have dbg.assign intrinsics with fragments (in the value-expression) +;; and address-expressions that involve arithmetic. The generated DBG_VALUE +;; intructions needs a DIExpression that: +;; a) Uses the fragment from the value-expression, +;; b) Uses the offset expression of the address-expression, +;; c) Has a DW_OP_deref appended. + +; CHECK: DBG_VALUE %stack.0.a, $noreg, {{.+}}, !DIExpression(DW_OP_plus_uconst, 8, DW_OP_deref, DW_OP_LLVM_fragment, 64, 32), debug-location + +define dso_local void @fun() !dbg !7 { +entry: + %a = alloca <4 x i32>, !DIAssignID !24 + call void @llvm.dbg.assign(metadata i32 undef, metadata !16, metadata !DIExpression(), metadata !24, metadata ptr %a, metadata !DIExpression()), !dbg !34 + ;; unlink and undef a dbg.assign to avoid using sidetable for var loc. + call void @llvm.dbg.assign(metadata i32 undef, metadata !16, metadata !DIExpression(), metadata !26, metadata ptr undef, metadata !DIExpression()), !dbg !34 + %idx2 = getelementptr inbounds i32, i32* %a, i32 2 + store i32 100, i32* %idx2, !DIAssignID !25 + call void @llvm.dbg.assign(metadata i32 100, metadata !16, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata !25, metadata i32* %a, metadata !DIExpression(DW_OP_plus_uconst, 8)), !dbg !34 + ret void +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "fun", linkageName: "fun", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{null, !10, !10, !10, !10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!16} +!16 = !DILocalVariable(name: "quad", scope: !7, file: !1, line: 4, type: !17) +!17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !10, size: 128, elements: !18) +!18 = !{!19} +!19 = !DISubrange(count: 4) +!23 = !DILocation(line: 0, scope: !7) +!24 = distinct !DIAssignID() +!25 = distinct !DIAssignID() +!26 = distinct !DIAssignID() +!34 = !DILocation(line: 0, column: 0, scope: !7) +!44 = !{!45, !45, i64 0} +!45 = !{!"float", !46, i64 0} +!46 = !{!"omnipotent char", !47, i64 0} +!47 = !{!"Simple C++ TBAA"} +!48 = !DILocation(line: 11, column: 3, scope: !7) +!49 = !DILocation(line: 12, column: 1, scope: !7) +!50 = !DISubprogram(name: "get", linkageName: "_Z3getv", scope: !1, file: !1, line: 1, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!51 = !DISubroutineType(types: !52) +!52 = !{!10} +!53 = !DISubprogram(name: "ext", linkageName: "_Z3extPf", scope: !1, file: !1, line: 2, type: !54, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!54 = !DISubroutineType(types: !55) +!55 = !{!10, !56} +!56 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) + Index: llvm/test/DebugInfo/assignment-tracking/X86/lower-to-value.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/lower-to-value.ll @@ -0,0 +1,114 @@ +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=false \ +; RUN: | FileCheck %s --check-prefixes=CHECK,DBGVALUE --implicit-check-not=DBG_VALUE +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=true \ +; RUN: | FileCheck %s --check-prefixes=CHECK,INSTRREF --implicit-check-not=DBG_VALUE \ +; RUN: --implicit-check-not=DBG_INSTR_REF + +;; Check that dbg.assigns for an aggregate variable which lives on the stack +;; for some of its lifetime are lowered into an appropriate set of DBG_VALUEs. +;; +;; $ cat test.cpp +;; void esc(long* p); +;; struct Ex { +;; long A; +;; long B; +;; }; +;; long fun() { +;; Ex X; +;; X.B = 0; +;; esc(&X.B); +;; X.B += 2; +;; return X.B; +;; } +;; $ clang++ test.cpp -O2 -g -emit-llvm -S -c -Xclang -fexperimental-assignment-tracking + +; CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "X", +;; Check we have no debug info for local in the side table. +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: X, type: default, offset: 0, size: 16, alignment: 8, +; CHECK-NEXT: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK-NEXT: debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + +;; Initially the whole variable is on the stack. +; CHECK: bb.0.entry: +; CHECK-NEXT: DBG_VALUE %stack.0.X, $noreg, ![[VAR]], !DIExpression(DW_OP_deref), debug-location + +;; 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 + +;; 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. +; DBGVALUE: %2:gr64 = nsw ADD64ri8 %1, 2, implicit-def dead $eflags, debug-location +; DBGVALUE-NEXT: DBG_VALUE %2, $noreg, ![[VAR]], !DIExpression(DW_OP_LLVM_fragment, 64, 64), debug-location +; INSTRREF: %2:gr64 = nsw ADD64ri8 %1, 2, implicit-def dead $eflags, debug-instr-number 1 +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0, ![[VAR]], !DIExpression(DW_OP_LLVM_fragment, 64, 64), debug-location + +source_filename = "test.cpp" +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" + +%struct.Ex = type { i64, i64 } + +define dso_local i64 @_Z3funv() local_unnamed_addr !dbg !7 { +entry: + %X = alloca %struct.Ex, align 8, !DIAssignID !17 + call void @llvm.dbg.assign(metadata i1 undef, metadata !12, metadata !DIExpression(), metadata !17, metadata ptr %X, metadata !DIExpression()), !dbg !18 + %B = getelementptr inbounds %struct.Ex, ptr %X, i64 0, i32 1, !dbg !20 + store i64 0, ptr %B, align 8, !dbg !21, !DIAssignID !27 + call void @llvm.dbg.assign(metadata i64 0, metadata !12, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !27, metadata ptr %B, metadata !DIExpression()), !dbg !21 + call void @_Z3escPl(ptr nonnull %B), !dbg !28 + %0 = load i64, ptr %B, align 8, !dbg !29 + %add = add nsw i64 %0, 2, !dbg !29 + call void @llvm.dbg.assign(metadata i64 %add, metadata !12, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !30, metadata ptr %B, metadata !DIExpression()), !dbg !29 + ret i64 %add, !dbg !32 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) +declare !dbg !33 dso_local void @_Z3escPl(ptr) local_unnamed_addr +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture) +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !1, file: !1, line: 6, type: !8, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DILocalVariable(name: "X", scope: !7, file: !1, line: 7, type: !13) +!13 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Ex", file: !1, line: 2, size: 128, flags: DIFlagTypePassByValue, elements: !14, identifier: "_ZTS2Ex") +!14 = !{!15, !16} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !13, file: !1, line: 3, baseType: !10, size: 64) +!16 = !DIDerivedType(tag: DW_TAG_member, name: "B", scope: !13, file: !1, line: 4, baseType: !10, size: 64, offset: 64) +!17 = distinct !DIAssignID() +!18 = !DILocation(line: 0, scope: !7) +!19 = !DILocation(line: 7, column: 3, scope: !7) +!20 = !DILocation(line: 8, column: 5, scope: !7) +!21 = !DILocation(line: 8, column: 7, scope: !7) +!27 = distinct !DIAssignID() +!28 = !DILocation(line: 9, column: 3, scope: !7) +!29 = !DILocation(line: 10, column: 7, scope: !7) +!30 = distinct !DIAssignID() +!31 = !DILocation(line: 12, column: 1, scope: !7) +!32 = !DILocation(line: 11, column: 3, scope: !7) +!33 = !DISubprogram(name: "esc", linkageName: "_Z3escPl", scope: !1, file: !1, line: 1, type: !34, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!34 = !DISubroutineType(types: !35) +!35 = !{null, !36} +!36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) Index: llvm/test/DebugInfo/assignment-tracking/X86/mem-loc-frag-fill-cfg.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/mem-loc-frag-fill-cfg.ll @@ -0,0 +1,147 @@ +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=false \ +; RUN: | FileCheck %s --implicit-check-not=DBG_ +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=true \ +; RUN: | FileCheck %s --implicit-check-not=DBG_ + +;; 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. + +;; 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 +;; rest of num's bits, [0, 64), are in memory, so check there's a dbg.value for +;; them. + +;; $ cat test.cpp +;; struct Nums { int a, b, c; }; +;; +;; void esc1(struct Nums*); +;; void esc2(struct Nums*); +;; bool step(); +;; +;; int main() { +;; struct Nums nums = { 1, 2, 1 }; +;; if (step()) +;; esc1(&nums); +;; else +;; esc2(&nums); +;; +;; nums.c = 2; //< Include some DSE to force a non-mem location. +;; step(); +;; +;; nums.c = nums.a; +;; +;; esc1(&nums); +;; return 0; +;; } +;; +;; $ clang++ test.cpp -O2 -g -Xclang -fexperimental-assignment-tracking -emit-llvm -S -o - + +;; Most check lines are inline in main. +; CHECK: ![[nums:[0-9]+]] = !DILocalVariable(name: "nums", + +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" + +%struct.Nums = type { i32, i32, i32 } + +@__const.main.nums = private unnamed_addr constant %struct.Nums { i32 1, i32 2, i32 1 }, align 4 + +declare void @_Z4esc1P4Nums(ptr nocapture noundef readonly %p) +declare void @_Z4esc2P4Nums(ptr nocapture noundef readonly %p) + +; Function Attrs: mustprogress norecurse uwtable +define dso_local noundef i32 @main() local_unnamed_addr #3 !dbg !40 { +; CHECK: name: main +entry: + %nums = alloca %struct.Nums, align 4, !DIAssignID !45 + call void @llvm.dbg.assign(metadata i1 undef, metadata !44, metadata !DIExpression(), metadata !45, metadata ptr %nums, metadata !DIExpression()), !dbg !46 +; CHECK: DBG_VALUE %stack.0.nums, $noreg, ![[nums]], !DIExpression(DW_OP_deref) + call void @llvm.memcpy.p0i8.p0i8.i64(ptr noundef nonnull align 4 dereferenceable(12) %nums, ptr noundef nonnull align 4 dereferenceable(12) %nums, i64 12, i1 false), !dbg !48, !DIAssignID !49 + call void @llvm.dbg.assign(metadata i1 undef, metadata !44, metadata !DIExpression(), metadata !49, metadata ptr %nums, metadata !DIExpression()), !dbg !46 + %call = tail call noundef zeroext i1 @_Z4stepv(), !dbg !50 + br i1 %call, label %if.then, label %if.else, !dbg !52 + +if.then: ; preds = %entry + call void @_Z4esc1P4Nums(ptr noundef nonnull %nums), !dbg !53 + br label %if.end, !dbg !53 + +if.else: ; preds = %entry + call void @_Z4esc2P4Nums(ptr noundef nonnull %nums), !dbg !54 + br label %if.end + +if.end: ; preds = %if.else, %if.then +; CHECK: bb.3.if.end: +; CHECK-NEXT: DBG_VALUE 2, $noreg, ![[nums]], !DIExpression(DW_OP_LLVM_fragment, 64, 32), debug-location +; CHECK-NEXT: DBG_VALUE %stack.0.nums, $noreg, ![[nums]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 64) + %c = getelementptr inbounds %struct.Nums, ptr %nums, i64 0, i32 2, !dbg !55 + 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 +; 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) + 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 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) #4 +declare !dbg !65 dso_local noundef zeroext i1 @_Z4stepv() local_unnamed_addr #5 +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture) #4 +declare void @llvm.memcpy.p0i8.p0i8.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!11, !12, !13, !14} +!llvm.ident = !{!15} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "glob", scope: !2, file: !3, line: 2, type: !5, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{!0} +!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Nums", file: !3, line: 1, size: 96, flags: DIFlagTypePassByValue, elements: !6, identifier: "_ZTS4Nums") +!6 = !{!7, !9, !10} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !5, file: !3, line: 1, baseType: !8, size: 32) +!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !5, file: !3, line: 1, baseType: !8, size: 32, offset: 32) +!10 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !5, file: !3, line: 1, baseType: !8, size: 32, offset: 64) +!11 = !{i32 7, !"Dwarf Version", i32 5} +!12 = !{i32 2, !"Debug Info Version", i32 3} +!13 = !{i32 1, !"wchar_size", i32 4} +!14 = !{i32 7, !"uwtable", i32 1} +!15 = !{!"clang version 14.0.0"} +!40 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 7, type: !41, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !43) +!41 = !DISubroutineType(types: !42) +!42 = !{!8} +!43 = !{!44} +!44 = !DILocalVariable(name: "nums", scope: !40, file: !3, line: 8, type: !5) +!45 = distinct !DIAssignID() +!46 = !DILocation(line: 0, scope: !40) +!47 = !DILocation(line: 8, column: 3, scope: !40) +!48 = !DILocation(line: 8, column: 15, scope: !40) +!49 = distinct !DIAssignID() +!50 = !DILocation(line: 9, column: 7, scope: !51) +!51 = distinct !DILexicalBlock(scope: !40, file: !3, line: 9, column: 7) +!52 = !DILocation(line: 9, column: 7, scope: !40) +!53 = !DILocation(line: 10, column: 5, scope: !51) +!54 = !DILocation(line: 12, column: 5, scope: !51) +!55 = !DILocation(line: 14, column: 8, scope: !40) +!56 = distinct !DIAssignID() +!57 = !DILocation(line: 15, column: 3, scope: !40) +!58 = !DILocation(line: 17, column: 10, scope: !40) +!61 = distinct !DIAssignID() +!62 = !DILocation(line: 19, column: 3, scope: !40) +!63 = !DILocation(line: 21, column: 1, scope: !40) +!64 = !DILocation(line: 20, column: 3, scope: !40) +!65 = !DISubprogram(name: "step", linkageName: "_Z4stepv", scope: !3, file: !3, line: 5, type: !66, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !69) +!66 = !DISubroutineType(types: !67) +!67 = !{!68} +!68 = !DIBasicType(name: "bool", size: 8, encoding: DW_ATE_boolean) +!69 = !{} Index: llvm/test/DebugInfo/assignment-tracking/X86/mem-loc-frag-fill.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/mem-loc-frag-fill.ll @@ -0,0 +1,109 @@ +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=false \ +; RUN: | FileCheck %s --implicit-check-not=DBG_ +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=true \ +; RUN: | FileCheck %s --implicit-check-not=DBG_ + +;; Check that the mem-loc-frag-fill analysis works on a simple case; ensure +;; that location definitions are added to preserve memory locations of +;; fragments of variables at subsequent location definitions for other +;; fragments of the variable are not currently in memory. + +;; Test generated from: +;; $ cat test.cpp +;; struct Nums { int a, b, c; }; +;; void esc(struct Nums*); +;; void step(); +;; int main() { +;; struct Nums nums = { 1, 2, 1 }; //< Store to .c is elided. +;; step(); +;; nums.c = 2; //< Killing store. +;; step(); +;; esc(&nums); +;; return 0; +;; } +;; $ clang++ test.cpp -O2 -g -Xclang -fexperimental-assignmment-tracking -emit-llvm -S -o - + +;; Most check lines are inline in main. +; CHECK: ![[nums:[0-9]+]] = !DILocalVariable(name: "nums", + +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" + +%struct.Nums = type { i32, i32, i32 } + +; Function Attrs: mustprogress norecurse uwtable +define dso_local noundef i32 @main() local_unnamed_addr #0 !dbg !7 { +entry: + %nums = alloca %struct.Nums, align 8, !DIAssignID !18 +; CHECK: DBG_VALUE %stack.0.nums, $noreg, ![[nums]], !DIExpression(DW_OP_deref) + call void @llvm.dbg.assign(metadata i1 undef, metadata !12, metadata !DIExpression(), metadata !18, metadata ptr %nums, metadata !DIExpression()), !dbg !19 + store i64 8589934593, ptr %nums, align 8, !dbg !21, !DIAssignID !22 +; CHECK: MOV64mr %stack.0.nums, 1, $noreg, 0, $noreg, killed %0 + call void @llvm.dbg.assign(metadata i1 undef, metadata !12, metadata !DIExpression(), metadata !22, metadata ptr %nums, metadata !DIExpression()), !dbg !19 + call void @llvm.dbg.assign(metadata i1 undef, metadata !12, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata !22, metadata ptr undef, metadata !DIExpression()), !dbg !19 +; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[nums]], !DIExpression(DW_OP_LLVM_fragment, 64, 32) +; CHECK-NEXT: DBG_VALUE %stack.0.nums, $noreg, ![[nums]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 64) + tail call void @_Z4stepv(), !dbg !23 + %c = getelementptr inbounds %struct.Nums, ptr %nums, i64 0, i32 2, !dbg !24 + 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) + tail call void @_Z4stepv(), !dbg !32 + call void @_Z3escP4Nums(ptr noundef nonnull %nums), !dbg !33 + ret i32 0, !dbg !35 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) #1 +declare !dbg !36 dso_local void @_Z4stepv() local_unnamed_addr #2 +declare !dbg !40 dso_local void @_Z3escP4Nums(ptr noundef) local_unnamed_addr #2 +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture) #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{!"clang version 14.0.0"} +!7 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 4, type: !8, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DILocalVariable(name: "nums", scope: !7, file: !1, line: 5, type: !13) +!13 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Nums", file: !1, line: 1, size: 96, flags: DIFlagTypePassByValue, elements: !14, identifier: "_ZTS4Nums") +!14 = !{!15, !16, !17} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !13, file: !1, line: 1, baseType: !10, size: 32) +!16 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !13, file: !1, line: 1, baseType: !10, size: 32, offset: 32) +!17 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !13, file: !1, line: 1, baseType: !10, size: 32, offset: 64) +!18 = distinct !DIAssignID() +!19 = !DILocation(line: 0, scope: !7) +!20 = !DILocation(line: 5, column: 3, scope: !7) +!21 = !DILocation(line: 5, column: 15, scope: !7) +!22 = distinct !DIAssignID() +!23 = !DILocation(line: 6, column: 3, scope: !7) +!24 = !DILocation(line: 7, column: 8, scope: !7) +!25 = !DILocation(line: 7, column: 10, scope: !7) +!31 = distinct !DIAssignID() +!32 = !DILocation(line: 8, column: 3, scope: !7) +!33 = !DILocation(line: 9, column: 3, scope: !7) +!34 = !DILocation(line: 11, column: 1, scope: !7) +!35 = !DILocation(line: 10, column: 3, scope: !7) +!36 = !DISubprogram(name: "step", linkageName: "_Z4stepv", scope: !1, file: !1, line: 3, type: !37, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !39) +!37 = !DISubroutineType(types: !38) +!38 = !{null} +!39 = !{} +!40 = !DISubprogram(name: "esc", linkageName: "_Z3escP4Nums", scope: !1, file: !1, line: 2, type: !41, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !39) +!41 = !DISubroutineType(types: !42) +!42 = !{null, !43} +!43 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) Index: llvm/test/DebugInfo/assignment-tracking/X86/nested-loop-frags.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/nested-loop-frags.ll @@ -0,0 +1,331 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG + +;; Test a variety of block inputs and lattice configurations for the assignment +;; tracking analysis (debug-ata). This is similar to nested-loop.ll and +;; nested-loop-sroa.ll except that the allocas are 64 bits and the stores are a +;; mix of 32 and 64 bits. Unlike nested-loop-sroa.ll this one is not a clone +;; of nested-loop.ll. + +;; The CFG looks like this: +;; entry +;; | +;; v +;; do.body <-----+ +;; | | +;; V | +;; do.body1 <--+ | +;; / \ | | +;; / \ | | +;; / \ | | +;; v v | | +;; if.then if.else | | +;; \ / | | +;; \ / | | +;; \ / | | +;; do.cond ----+ | +;; | | +;; v | +;; do.cond4 -----+ +;; | +;; v +;; do.end6 + +;; This version doesn't contain tables of assignments as it is difficult to neatly represent +;; the additional dimension of fragments (stores to part of the variable/alloca). + +;; Variable 'a' (!21) +;; Check that both the full assignment (!70) assignment to the lower bits (and !63) are +;; propagated to do.end6. +;; +;; Variable 'b' (!22) +;; Check mem=dbg assignment to the lower 32 bits in if.then causes a mem=phi (tested by +;; looking for value-based DBG_VALUE in do.end6). Meanwhile, check the assignment (!71) +;; is propagated to do.end6 for the upper bits (checked by looking for a memory location). +;; +;; Variable 'c' (!67) +;; Check initial dbg and mem assignment values are propagated through all blocks, with +;; dbg defs with the inital assignment ID put in do.cond and do.end6 (variable is always +;; in memory). The initial mem and dbg defs are to the whole variable, and the subsequent +;; dbg defs come in pairs, split for the high and low bits of the variable. +;; +;; Variable 'd' (!72) +;; Same as above, except the dbg def in do.cond has been split into two assignments +;; that both use the same ID as the inital one - one fragment is assigned in each of +;; the if-branches. +;; +;; Variable 'e' (!75) +;; The join in do.body covers assignments to different fragments. Out of entry +;; we have [0-31: mem=!77 dbg=!78 loc=val] [32-63: mem=!77 dbg=!76 loc=val]. +;; There's a tagged store to the lower bits in if.then and an untagged store to +;; the upper bits in if.else. The important check here is that in do.body the +;; memory location isn't used at the dbg def !77 as the live-in loc=val and the +;; incoming mem assignments are not all !77. + +; CHECK-DAG: ![[a:[0-9]+]] = !DILocalVariable(name: "a", +; CHECK-DAG: ![[b:[0-9]+]] = !DILocalVariable(name: "b", +; CHECK-DAG: ![[c:[0-9]+]] = !DILocalVariable(name: "c", +; CHECK-DAG: ![[d:[0-9]+]] = !DILocalVariable(name: "d", +; CHECK-DAG: ![[e:[0-9]+]] = !DILocalVariable(name: "e", + +;; Variables 'c' (!67) and 'd' (!72) are always stack-homed. +; CHECK: - { id: 2, name: c.addr, type: default, offset: 0, size: 8, alignment: 8, +; CHECK-NEXT: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK-NEXT: debug-info-variable: '![[c]]', debug-info-expression: '!DIExpression()', +; CHECK: - { id: 3, name: d.addr, type: default, offset: 0, size: 8, alignment: 8, +; CHECK-NEXT: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK-NEXT: debug-info-variable: '![[d]]', debug-info-expression: '!DIExpression()', + +source_filename = "test.cpp" +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" + +@g_a = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 +@g_b = dso_local local_unnamed_addr global i32 0, align 4, !dbg !5 +@g_c = dso_local local_unnamed_addr global i32 0, align 4, !dbg !8 + +define dso_local noundef i32 @_Z3funii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 !dbg !17 { +entry: + %a.addr = alloca i64, align 4, !DIAssignID !58 ; VAR:a + call void @llvm.dbg.assign(metadata i1 undef, metadata !21, metadata !DIExpression(), metadata !58, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27 ; VAR:a + %b.addr = alloca i64, align 4, !DIAssignID !64 ; VAR:b + call void @llvm.dbg.assign(metadata i1 undef, metadata !22, metadata !DIExpression(), metadata !64, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + %c.addr = alloca i64, align 4, !DIAssignID !68 ; VAR:c + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(), metadata !68, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + %d.addr = alloca i64, align 4, !DIAssignID !73 ; VAR:d + call void @llvm.dbg.assign(metadata i1 undef, metadata !72, metadata !DIExpression(), metadata !73, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + %e.addr = alloca i64, align 4, !DIAssignID !76 ; VAR:e + call void @llvm.dbg.assign(metadata i1 undef, metadata !75, metadata !DIExpression(), metadata !76, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + ;%f.addr = alloca i64, align 4, !DIAssignID !80 ; VAR:f + ;call void @llvm.dbg.assign(metadata i1 undef, metadata !79, metadata !DIExpression(), metadata !80, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + store i64 1, ptr %a.addr, !DIAssignID !70 ; VAR:a + call void @llvm.dbg.assign(metadata i64 1, metadata !21, metadata !DIExpression(), metadata !70, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27 ; VAR:a + store i64 2, ptr %b.addr, !DIAssignID !71 ; VAR:b + call void @llvm.dbg.assign(metadata i32 2, metadata !22, metadata !DIExpression(), metadata !71, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + store i32 9, ptr %e.addr, !DIAssignID !78 ; VAR:e + call void @llvm.dbg.assign(metadata i32 9, metadata !75, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !78, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + store i32 3, ptr %a.addr, !DIAssignID !63 ; VAR:a + store i32 4, ptr %b.addr, !DIAssignID !65 ; VAR:b + store i64 5, ptr %c.addr, !DIAssignID !69 ; VAR:c + call void @llvm.dbg.assign(metadata i64 5, metadata !67, metadata !DIExpression(), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + store i64 6, ptr %d.addr, !DIAssignID !74 ; VAR:d + call void @llvm.dbg.assign(metadata i64 6, metadata !72, metadata !DIExpression(), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + store i64 16, ptr %e.addr, !DIAssignID !77 ; VAR:e + ;store i32 11, ptr %f.addr, !DIAssignID !81 ; VAR:f + ;call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + br label %do.body, !dbg !24 +; CHECK-LABEL: bb.0.entry: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: DBG_VALUE %stack.1.b.addr, $noreg, ![[b]], !DIExpression(DW_OP_deref) +; 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: DBG_VALUE $noreg, $noreg, ![[a]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32) +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: MOV64mi32 %stack.1.b.addr, 1, $noreg, 0, $noreg, 2 +; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[b]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; 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: 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-NEXT: MOV32mi %stack.1.b.addr, 1, $noreg, 0, $noreg, 4 +; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[b]], !DIExpression(DW_OP_LLVM_fragment, 0, 32) +; CHECK-NEXT: DBG_VALUE %stack.1.b.addr, $noreg, ![[b]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: MOV64mi32 %stack.2.c.addr, 1, $noreg, 0, $noreg, 5 +; CHECK-NEXT: MOV64mi32 %stack.3.d.addr, 1, $noreg, 0, $noreg, 6 +; CHECK-NEXT: MOV64mi32 %stack.4.e.addr, 1, $noreg, 0, $noreg, 16 +; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[e]], !DIExpression() +; CHECK-NEXT: {{^ *$}} + +do.body: ; preds = %do.cond4, %entry + call void @llvm.dbg.assign(metadata i64 16, metadata !75, metadata !DIExpression(), metadata !77, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + ;call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + %.pre10 = load i32, ptr @g_a, align 4, !dbg !27 + br label %do.body1, !dbg !34 +; CHECK-LABEL: bb.1.do.body: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: DBG_VALUE 16, $noreg, ![[e]], !DIExpression() +; CHECK-NEXT: %0:gr32 = MOV32rm $rip, 1, $noreg, @g_a, $noreg +; CHECK-NEXT: {{^ *$}} + +do.body1: ; preds = %do.cond, %do.body + %0 = phi i32 [ %.pre10, %do.body ], [ %1, %do.cond ], !dbg !27 + ;call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + %tobool.not = icmp eq i32 %0, 0, !dbg !27 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !35 +; 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: {{^ *$}} + +if.then: ; preds = %do.body1 + %.pre = load i32, ptr @g_a, align 4, !dbg !27 + store i32 %.pre, ptr %b.addr, !DIAssignID !66 ; VAR:b + call void @llvm.dbg.assign(metadata i32 %.pre, metadata !22, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !66, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + store i32 6, ptr %d.addr, !DIAssignID !74 ; VAR:d + call void @llvm.dbg.assign(metadata i32 6, metadata !72, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + call void @llvm.dbg.assign(metadata i32 8, metadata !75, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !82, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + store i32 8, ptr %e.addr, !DIAssignID !82 ; VAR:e + br label %do.cond, !dbg !39 +; CHECK-LABEL: bb.3.if.then: +; CHECK-NEXT: successors +; 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: 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 + call void @llvm.dbg.assign(metadata i32 6, metadata !72, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + %e.high32 = getelementptr i32, ptr %e.addr, i32 1 + store i32 15, ptr %e.high32 ; VAR:e + ;store i32 10, ptr %f.addr ; VAR:f + br label %do.cond +; CHECK-LABEL: bb.4.if.else: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: MOV32mi %stack.3.d.addr, 1, $noreg, 0, $noreg, 6 +; CHECK-NEXT: MOV32mi %stack.4.e.addr, 1, $noreg, 4, $noreg, 15 +; CHECK-NEXT: DBG_VALUE %stack.4.e.addr, $noreg, !34, !DIExpression(DW_OP_plus_uconst, 4, DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK: {{^ *$}} + +do.cond: ; preds = %if.then, %if.else + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + ;call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + %1 = load i32, ptr @g_b, align 4, !dbg !43 + %tobool3.not = icmp eq i32 %1, 0, !dbg !43 + br i1 %tobool3.not, label %do.cond4, label %do.body1, !dbg !44, !llvm.loop !45 +; CHECK-LABEL: bb.5.do.cond: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; xCHECK-NEXT: XXX ?DBG_VALUE %stack.2.c.addr, $noreg, ![[c]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; xCHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK-NOT: DBG_VALUE +; CHECK: {{^ *$}} + +do.cond4: ; preds = %do.cond + %2 = load i32, ptr @g_c, align 4, !dbg !48 + %tobool5.not = icmp eq i32 %2, 0, !dbg !48 + br i1 %tobool5.not, label %do.end6, label %do.body, !dbg !49, !llvm.loop !50 +; CHECK-LABEL: bb.6.do.cond4: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NOT: DBG +; CHECK: {{^ *$}} + +do.end6: ; preds = %do.cond4 + call void @llvm.dbg.assign(metadata i32 3, metadata !21, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !63, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27; VAR:a + call void @llvm.dbg.assign(metadata i32 0, metadata !21, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !70, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27; VAR:a + ;call void @llvm.dbg.assign(metadata i32 3, metadata !21, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !81, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27; VAR:a + call void @llvm.dbg.assign(metadata i32 4, metadata !22, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !65, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + call void @llvm.dbg.assign(metadata i32 0, metadata !22, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !71, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + call void @llvm.dbg.assign(metadata i32 6, metadata !72, metadata !DIExpression(), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + ;call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + ret i32 0, !dbg !53 +; CHECK-LABEL: bb.7.do.end6: +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32) +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE 4, $noreg, ![[b]], !DIExpression(DW_OP_LLVM_fragment, 0, 32) +; CHECK-NEXT: DBG_VALUE %stack.1.b.addr, $noreg, ![[b]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; xCHECK-NEXT: XXX ? DBG_VALUE %stack.3.d.addr, $noreg, ![[d]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; xCHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +} + +declare !dbg !54 void @_Z4calli(i32 noundef) local_unnamed_addr #1 +declare void @llvm.dbg.declare(metadata, metadata, metadata) +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 = !{!10, !11, !12, !13, !14, !15} +!llvm.ident = !{!16} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g_a", 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 16.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{!0, !5, !8} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "g_b", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true) +!7 = !DIBasicType(name: "long long", size: 64, encoding: DW_ATE_signed) +!8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression()) +!9 = distinct !DIGlobalVariable(name: "g_c", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true) +!10 = !{i32 7, !"Dwarf Version", i32 5} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{i32 8, !"PIC Level", i32 2} +!14 = !{i32 7, !"PIE Level", i32 2} +!15 = !{i32 7, !"uwtable", i32 2} +!16 = !{!"clang version 16.0.0"} +!17 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funii", scope: !3, file: !3, line: 3, type: !18, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !20) +!18 = !DISubroutineType(types: !19) +!19 = !{!7, !7, !7} +!20 = !{!21, !22} +!21 = !DILocalVariable(name: "a", arg: 1, scope: !17, file: !3, line: 3, type: !7) +!22 = !DILocalVariable(name: "b", arg: 2, scope: !17, file: !3, line: 3, type: !7) +!23 = !DILocation(line: 0, scope: !17) +!24 = !DILocation(line: 4, column: 3, scope: !17) +!25 = !DILocation(line: 5, column: 5, scope: !26) +!26 = distinct !DILexicalBlock(scope: !17, file: !3, line: 4, column: 6) +!27 = !DILocation(line: 7, column: 11, scope: !28) +!28 = distinct !DILexicalBlock(scope: !29, file: !3, line: 7, column: 11) +!29 = distinct !DILexicalBlock(scope: !26, file: !3, line: 6, column: 8) +!34 = !DILocation(line: 6, column: 5, scope: !26) +!35 = !DILocation(line: 7, column: 11, scope: !29) +!36 = !DILocation(line: 8, column: 11, scope: !37) +!37 = distinct !DILexicalBlock(scope: !28, file: !3, line: 7, column: 16) +!38 = !DILocation(line: 9, column: 9, scope: !37) +!39 = !DILocation(line: 10, column: 7, scope: !37) +!40 = !DILocation(line: 11, column: 11, scope: !41) +!41 = distinct !DILexicalBlock(scope: !28, file: !3, line: 10, column: 14) +!42 = !DILocation(line: 0, scope: !28) +!43 = !DILocation(line: 13, column: 14, scope: !26) +!44 = !DILocation(line: 13, column: 5, scope: !29) +!45 = distinct !{!45, !34, !46, !47} +!46 = !DILocation(line: 13, column: 17, scope: !26) +!47 = !{!"llvm.loop.mustprogress"} +!48 = !DILocation(line: 14, column: 12, scope: !17) +!49 = !DILocation(line: 14, column: 3, scope: !26) +!50 = distinct !{!50, !24, !51, !47} +!51 = !DILocation(line: 14, column: 15, scope: !17) +!52 = !DILocation(line: 15, column: 12, scope: !17) +!53 = !DILocation(line: 15, column: 3, scope: !17) +!54 = !DISubprogram(name: "call", linkageName: "_Z4calli", scope: !3, file: !3, line: 2, type: !55, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !57) +!55 = !DISubroutineType(types: !56) +!56 = !{null, !7} +!57 = !{} +!58 = distinct !DIAssignID() +!63 = distinct !DIAssignID() +!64 = distinct !DIAssignID() +!65 = distinct !DIAssignID() +!66 = distinct !DIAssignID() +!67 = !DILocalVariable(name: "c", scope: !17, file: !3, line: 3, type: !7) +!68 = distinct !DIAssignID() +!69 = distinct !DIAssignID() +!70 = distinct !DIAssignID() +!71 = distinct !DIAssignID() +!72 = !DILocalVariable(name: "d", scope: !17, file: !3, line: 3, type: !7) +!73 = distinct !DIAssignID() +!74 = distinct !DIAssignID() +!75 = !DILocalVariable(name: "e", scope: !17, file: !3, line: 3, type: !7) +!76 = distinct !DIAssignID() +!77 = distinct !DIAssignID() +!78 = distinct !DIAssignID() +!82 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/nested-loop-sroa.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/nested-loop-sroa.ll @@ -0,0 +1,403 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG + +;; Test a variety of block inputs and lattice configurations for the assignment +;; tracking analysis (debug-ata). This is the same as nested-loop.ll except each +;; alloca holds a fragment of a variable instead of a whole variable. +;; The CFG looks like this: +;; entry +;; | +;; v +;; do.body <-----+ +;; | | +;; V | +;; do.body1 <--+ | +;; / \ | | +;; / \ | | +;; / \ | | +;; v v | | +;; if.then if.else | | +;; \ / | | +;; \ / | | +;; \ / | | +;; do.cond ----+ | +;; | | +;; v | +;; do.cond4 -----+ +;; | +;; v +;; do.end6 + +;; Key +;; ╔═════════════════════╦═══════════════════════════════════════════════════════════════════╗ +;; ║ thing ║ meaning ║ +;; ╠═════════════════════╬═══════════════════════════════════════════════════════════════════╣ +;; ║ mem= ║ assignment of !n or phi to memory ║ +;; ║ dbg= ║ assignment of !n or phi to source variable ║ +;; ║ phi ║ phi of assignments (operands not traked)* ║ +;; ║ loc= ║ location to use is value (implicit location), stack home, or none ║ +;; ╚═════════════════════╩═══════════════════════════════════════════════════════════════════╝ +;; (*) A phi in the def column represents an assignment made by an untagged store. +;; +;; Variable 'a' (!21) +;; Check initial dbg and mem assignment values are propagated through all blocks. +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!63 dbg=!70 ║ mem=!63 dbg=!70 loc=val ║ +;; ║ do.end6 ║ mem=!63 dbg=!70 loc=val ║ mem=!63 ║ mem=!63 dbg=!63 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'b' (!22) +;; Check mem=dbg assignment on branch in nested loop causes a mem=phi (tested by looking +;; for value-based DBG_VALUE in do.end6). +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!65 dbg=!71 ║ mem=!65 dbg=!71 loc=val ║ +;; ║ if.then ║ mem=phi dbg=phi loc=none ║ mem=!66 dbg=!66 ║ mem=!66 dbg=!66 loc=mem ║ +;; ║ do.end6 ║ mem=phi dbg=phi loc=none ║ mem=!65 ║ mem=phi dbg=!65 loc=val ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'c' (!67) +;; Check initial dbg and mem assignment values are propagated through all blocks, with +;; dbg defs with the inital assignment ID put in do.cond and do.end6 (variable is always +;; in memory). +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!69 dbg=!69 ║ mem=!69 dbg=!69 loc=mem ║ +;; ║ do.cond ║ mem=!69 dbg=!69 loc=mem ║ dbg=!69 ║ mem=!69 dbg=!69 loc=mem ║ +;; ║ do.end6 ║ mem=!69 dbg=!69 loc=mem ║ dbg=!69 ║ mem=!69 dbg=!69 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'd' (!72) +;; Same as above, except the dbg def in do.cond has been swapped for a dbg=mem def (with +;; the initial assignment ID) and has been moved to if.else. +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!74 dbg=!74 ║ mem=!74 dbg=!74 loc=mem ║ +;; ║ if.else ║ mem=!74 dbg=!74 loc=mem ║ mem=!74 dbg=!74 ║ mem=!74 dbg=!74 loc=mem ║ +;; ║ do.end6 ║ mem=!74 dbg=!74 loc=mem ║ dbg=!74 ║ mem=!74 dbg=!74 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'e' (!75) +;; mem defs in entry, if.then and if.else with same ID (!77). Check these join correct +;; (tested using the dbg defs of the same ID - the memory location is valid at each of +;; these with that ID). +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!77 dbg=!78 ║ mem=!77 dbg=!78 loc=val ║ +;; ║ do.body ║ mem=!77 dbg=phi loc=none ║ dbg=!77 ║ mem=!77 dbg=!77 loc=mem ║ +;; ║ do.body1 ║ mem=!77 dbg=!77 loc=mem ║ dbg=!77 ║ mem=!77 dbg=!77 loc=mem ║ +;; ║ if.then ║ mem=!77 dbg=!77 loc=mem ║ mem=!77 ║ mem=!77 dbg=!77 loc=mem ║ +;; ║ if.else ║ mem=!77 dbg=!77 loc=mem ║ mem=!77 ║ mem=!77 dbg=!77 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'f' (!79) +;; mem def in entry and an untagged store in if.else (results in mem=phi, dbg=phi defs). +;; Use dbg defs in do.body, do.body1, do.cond and do.end6 to check the phi-ness +;; has been propagated (the memory loc at each is not a valid location). Check the memory +;; loc is used in if.else after the untagged store. +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!81 dbg=!81 ║ mem=!81 dbg=!81 loc=mem ║ +;; ║ do.body ║ mem=phi dbg=phi loc=none ║ dbg=!81 ║ mem=phi dbg=!81 loc=val ║ +;; ║ do.body1 ║ mem=phi dbg=phi loc=none ║ dbg=!81 ║ mem=phi dbg=!81 loc=val ║ +;; ║ if.else ║ mem=phi dbg=phi loc=none ║ mem=phi dbg=phi ║ mem=phi dbg=phi loc=mem ║ +;; ║ do.cond ║ mem=phi dbg=phi loc=none ║ dbg=!81 ║ mem=phi dbg=!81 loc=val ║ +;; ║ do.end6 ║ mem=phi dbg=!81 loc=val ║ dbg=!81 ║ mem=!69 dbg=!81 loc=val ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'g' (!82) +;; Check that joining loc=none with anything else results in loc=none. The out-loc of +;; entry is set up to be loc=none by following an untagged store with a tagged store, +;; with the linked dbg.assign in another block. The dbg.assign is in do.body - it follows +;; another store linked to it. Importantly, there are other instructions wedged between +;; them, which is how we test that the in-loc is loc=none. The result of encountering +;; a tagged store while the loc=none is to emit nothing. Thus, we check that no location +;; def is emitted in do.body until the dbg.assign is encountered (after the load that was +;; wedged between the store and intrinsic). +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=phi dbg=phi ║ mem=phi dbg=phi loc=none ║ +;; ║ do.body ║ mem=phi dbg=phi loc=none ║ mem=!84 dbg=!84 ║ mem=!84 dbg=!84 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ + +; CHECK-DAG: ![[a:[0-9]+]] = !DILocalVariable(name: "a", +; CHECK-DAG: ![[b:[0-9]+]] = !DILocalVariable(name: "b", +; CHECK-DAG: ![[c:[0-9]+]] = !DILocalVariable(name: "c", +; CHECK-DAG: ![[d:[0-9]+]] = !DILocalVariable(name: "d", +; CHECK-DAG: ![[e:[0-9]+]] = !DILocalVariable(name: "e", +; CHECK-DAG: ![[f:[0-9]+]] = !DILocalVariable(name: "f", +; CHECK-DAG: ![[g:[0-9]+]] = !DILocalVariable(name: "g", + +;; Variables 'c' (!67) and 'd' (!72) are always stack-homed, but the analysis +;; currently doesn't try to work this out for sroa-d vars. Instead, we get a +;; set of DBG_VALUEs. +;; TODO / Wishlist: +; KCEHC: - { id: 2, name: c.addr, type: default, offset: 0, size: 4, alignment: 4, +; TXEN-KCEHC: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; TXEN-KCEHC: debug-info-variable: '![[c]]', debug-info-expression: '!DIExpression()', +; KCEHC: - { id: 3, name: d.addr, type: default, offset: 0, size: 4, alignment: 4, +; TXEN-KCEHC: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; TXEN-KCEHC: debug-info-variable: '![[d]]', debug-info-expression: '!DIExpression()', + +source_filename = "test.cpp" +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" + +@g_a = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 +@g_b = dso_local local_unnamed_addr global i32 0, align 4, !dbg !5 +@g_c = dso_local local_unnamed_addr global i32 0, align 4, !dbg !8 + +define dso_local noundef i32 @_Z3funii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 !dbg !17 { +entry: + %a.addr = alloca i32, align 4, !DIAssignID !58 ; VAR:a + call void @llvm.dbg.assign(metadata i1 undef, metadata !21, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !58, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27 ; VAR:a + %b.addr = alloca i32, align 4, !DIAssignID !64 ; VAR:b + call void @llvm.dbg.assign(metadata i1 undef, metadata !22, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !64, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + %c.addr = alloca i32, align 4, !DIAssignID !68 ; VAR:c + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !68, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + %d.addr = alloca i32, align 4, !DIAssignID !73 ; VAR:d + call void @llvm.dbg.assign(metadata i1 undef, metadata !72, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !73, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + %e.addr = alloca i32, align 4, !DIAssignID !76 ; VAR:e + call void @llvm.dbg.assign(metadata i1 undef, metadata !75, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !76, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + %f.addr = alloca i32, align 4, !DIAssignID !80 ; VAR:f + call void @llvm.dbg.assign(metadata i1 undef, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !80, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + %g.addr = alloca i32, align 4, !DIAssignID !83 ; VAR:g + call void @llvm.dbg.assign(metadata i1 undef, metadata !82, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !83, metadata ptr %g.addr, metadata !DIExpression()), !dbg !27 ; VAR:g + store i32 1, ptr %a.addr, !DIAssignID !70 ; VAR:a + call void @llvm.dbg.assign(metadata i32 1, metadata !21, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !70, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27 ; VAR:a + store i32 2, ptr %b.addr, !DIAssignID !71 ; VAR:b + call void @llvm.dbg.assign(metadata i32 2, metadata !22, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !71, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + store i32 12, ptr %g.addr ; VAR:g + store i32 9, ptr %e.addr, !DIAssignID !78 ; VAR:e + call void @llvm.dbg.assign(metadata i32 9, metadata !75, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !78, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + store i32 3, ptr %a.addr, !DIAssignID !63 ; VAR:a + store i32 4, ptr %b.addr, !DIAssignID !65 ; VAR:b + store i32 5, ptr %c.addr, !DIAssignID !69 ; VAR:c + call void @llvm.dbg.assign(metadata i32 5, metadata !67, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + store i32 6, ptr %d.addr, !DIAssignID !74 ; VAR:d + call void @llvm.dbg.assign(metadata i32 6, metadata !72, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + store i32 8, ptr %e.addr, !DIAssignID !77 ; VAR:e + store i32 13, ptr %g.addr, !DIAssignID !84 ; VAR:g + store i32 11, ptr %f.addr, !DIAssignID !81 ; VAR:f + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + br label %do.body, !dbg !24 +; CHECK-LABEL: bb.0.entry: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE %stack.1.b.addr, $noreg, ![[b]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE %stack.2.c.addr, $noreg, ![[c]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE %stack.3.d.addr, $noreg, ![[d]], !DIExpression(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, 32, 32) +; CHECK-NEXT: DBG_VALUE %stack.5.f.addr, $noreg, ![[f]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE %stack.6.g.addr, $noreg, ![[g]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: MOV32mi %stack.0.a.addr, 1, $noreg, 0, $noreg, 3 +; CHECK-NEXT: DBG_VALUE 1, $noreg, ![[a]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: MOV32mi %stack.1.b.addr, 1, $noreg, 0, $noreg, 4 +; CHECK-NEXT: DBG_VALUE 2, $noreg, ![[b]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: MOV32mi %stack.2.c.addr, 1, $noreg, 0, $noreg, 5 +; CHECK-NEXT: MOV32mi %stack.3.d.addr, 1, $noreg, 0, $noreg, 6 +; CHECK-NEXT: MOV32mi %stack.4.e.addr, 1, $noreg, 0, $noreg, 8 +; CHECK-NEXT: DBG_VALUE 9, $noreg, ![[e]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: MOV32mi %stack.6.g.addr, 1, $noreg, 0, $noreg, 13 +; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[g]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: MOV32mi %stack.5.f.addr, 1, $noreg, 0, $noreg, 11 +; CHECK-NEXT: {{^ *$}} + +do.body: ; preds = %do.cond4, %entry + call void @llvm.dbg.assign(metadata i32 8, metadata !75, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !77, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + store i32 13, ptr %g.addr, !DIAssignID !84 ; VAR:g + %.pre10 = load i32, ptr @g_a, align 4, !dbg !27 + call void @llvm.dbg.assign(metadata i32 11, metadata !82, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !84, metadata ptr %g.addr, metadata !DIExpression()), !dbg !27 ; VAR:g + br label %do.body1, !dbg !34 +; CHECK-LABEL: bb.1.do.body: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: DBG_VALUE %stack.4.e.addr, $noreg, ![[e]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: MOV32mi %stack.6.g.addr, 1, $noreg, 0, $noreg, 13 +; CHECK-NEXT: %0:gr32 = MOV32rm $rip, 1, $noreg, @g_a, $noreg +; CHECK-NEXT: DBG_VALUE %stack.6.g.addr, $noreg, ![[g]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: {{^ *$}} + +do.body1: ; preds = %do.cond, %do.body + %0 = phi i32 [ %.pre10, %do.body ], [ %1, %do.cond ], !dbg !27 + call void @llvm.dbg.assign(metadata i32 8, metadata !75, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !77, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + %tobool.not = icmp eq i32 %0, 0, !dbg !27 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !35 +; CHECK-LABEL: bb.2.do.body1: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK: DBG_VALUE %stack.4.e.addr, $noreg, ![[e]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK: JMP_1 +; CHECK-NEXT: {{^ *$}} + +if.then: ; preds = %do.body1 + %.pre = load i32, ptr @g_a, align 4, !dbg !27 + store i32 %.pre, ptr %b.addr, !DIAssignID !66 ; VAR:b + call void @llvm.dbg.assign(metadata i32 %.pre, metadata !22, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !66, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + store i32 8, ptr %e.addr, !DIAssignID !77 ; VAR:e + br label %do.cond, !dbg !39 +; CHECK-LABEL: bb.3.if.then: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: %5:gr32 = MOV32rm +; 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 +; 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 +; CHECK-NEXT: {{^ *$}} + +if.else: ; preds = %do.body1 + store i32 6, ptr %d.addr, !DIAssignID !74 ; VAR:d + call void @llvm.dbg.assign(metadata i32 6, metadata !72, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + store i32 8, ptr %e.addr, !DIAssignID !77 ; VAR:e + store i32 10, ptr %f.addr ; VAR:f + br label %do.cond +; CHECK-LABEL: bb.4.if.else: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: MOV32mi %stack.3.d.addr, 1, $noreg, 0, $noreg, 6 +; CHECK-NEXT: DBG_VALUE %stack.3.d.addr, $noreg, ![[d]], !DIExpression(DW_OP_deref, 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: MOV32mi %stack.5.f.addr, 1, $noreg, 0, $noreg, 10 +; CHECK-NEXT: DBG_VALUE %stack.5.f.addr, $noreg, !36, !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: {{^ *$}} + +do.cond: ; preds = %if.then, %if.else + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + %1 = load i32, ptr @g_b, align 4, !dbg !43 + %tobool3.not = icmp eq i32 %1, 0, !dbg !43 + br i1 %tobool3.not, label %do.cond4, label %do.body1, !dbg !44, !llvm.loop !45 +; CHECK-LABEL: bb.5.do.cond: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: DBG_VALUE %stack.2.c.addr, $noreg, ![[c]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK: {{^ *$}} + +do.cond4: ; preds = %do.cond + %2 = load i32, ptr @g_c, align 4, !dbg !48 + %tobool5.not = icmp eq i32 %2, 0, !dbg !48 + br i1 %tobool5.not, label %do.end6, label %do.body, !dbg !49, !llvm.loop !50 +; CHECK-LABEL: bb.6.do.cond4: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NOT: DBG +; CHECK: {{^ *$}} + +do.end6: ; preds = %do.cond4 + call void @llvm.dbg.assign(metadata i32 3, metadata !21, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !63, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27; VAR:a + call void @llvm.dbg.assign(metadata i32 4, metadata !22, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !65, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + call void @llvm.dbg.assign(metadata i32 6, metadata !72, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + ret i32 0, !dbg !53 +; CHECK-LABEL: bb.7.do.end6: +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE 4, $noreg, ![[b]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE %stack.2.c.addr, $noreg, ![[c]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE %stack.3.d.addr, $noreg, ![[d]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +} + +declare !dbg !54 void @_Z4calli(i32 noundef) local_unnamed_addr #1 +declare void @llvm.dbg.declare(metadata, metadata, metadata) +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 = !{!10, !11, !12, !13, !14, !15} +!llvm.ident = !{!16} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g_a", 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 16.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{!0, !5, !8} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "g_b", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true) +!7 = !DIBasicType(name: "long long", size: 64, encoding: DW_ATE_signed) +!8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression()) +!9 = distinct !DIGlobalVariable(name: "g_c", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true) +!10 = !{i32 7, !"Dwarf Version", i32 5} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{i32 8, !"PIC Level", i32 2} +!14 = !{i32 7, !"PIE Level", i32 2} +!15 = !{i32 7, !"uwtable", i32 2} +!16 = !{!"clang version 16.0.0"} +!17 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funii", scope: !3, file: !3, line: 3, type: !18, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !20) +!18 = !DISubroutineType(types: !19) +!19 = !{!7, !7, !7} +!20 = !{!21, !22} +!21 = !DILocalVariable(name: "a", arg: 1, scope: !17, file: !3, line: 3, type: !7) +!22 = !DILocalVariable(name: "b", arg: 2, scope: !17, file: !3, line: 3, type: !7) +!23 = !DILocation(line: 0, scope: !17) +!24 = !DILocation(line: 4, column: 3, scope: !17) +!25 = !DILocation(line: 5, column: 5, scope: !26) +!26 = distinct !DILexicalBlock(scope: !17, file: !3, line: 4, column: 6) +!27 = !DILocation(line: 7, column: 11, scope: !28) +!28 = distinct !DILexicalBlock(scope: !29, file: !3, line: 7, column: 11) +!29 = distinct !DILexicalBlock(scope: !26, file: !3, line: 6, column: 8) +!34 = !DILocation(line: 6, column: 5, scope: !26) +!35 = !DILocation(line: 7, column: 11, scope: !29) +!36 = !DILocation(line: 8, column: 11, scope: !37) +!37 = distinct !DILexicalBlock(scope: !28, file: !3, line: 7, column: 16) +!38 = !DILocation(line: 9, column: 9, scope: !37) +!39 = !DILocation(line: 10, column: 7, scope: !37) +!40 = !DILocation(line: 11, column: 11, scope: !41) +!41 = distinct !DILexicalBlock(scope: !28, file: !3, line: 10, column: 14) +!42 = !DILocation(line: 0, scope: !28) +!43 = !DILocation(line: 13, column: 14, scope: !26) +!44 = !DILocation(line: 13, column: 5, scope: !29) +!45 = distinct !{!45, !34, !46, !47} +!46 = !DILocation(line: 13, column: 17, scope: !26) +!47 = !{!"llvm.loop.mustprogress"} +!48 = !DILocation(line: 14, column: 12, scope: !17) +!49 = !DILocation(line: 14, column: 3, scope: !26) +!50 = distinct !{!50, !24, !51, !47} +!51 = !DILocation(line: 14, column: 15, scope: !17) +!52 = !DILocation(line: 15, column: 12, scope: !17) +!53 = !DILocation(line: 15, column: 3, scope: !17) +!54 = !DISubprogram(name: "call", linkageName: "_Z4calli", scope: !3, file: !3, line: 2, type: !55, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !57) +!55 = !DISubroutineType(types: !56) +!56 = !{null, !7} +!57 = !{} +!58 = distinct !DIAssignID() +!63 = distinct !DIAssignID() +!64 = distinct !DIAssignID() +!65 = distinct !DIAssignID() +!66 = distinct !DIAssignID() +!67 = !DILocalVariable(name: "c", scope: !17, file: !3, line: 3, type: !7) +!68 = distinct !DIAssignID() +!69 = distinct !DIAssignID() +!70 = distinct !DIAssignID() +!71 = distinct !DIAssignID() +!72 = !DILocalVariable(name: "d", scope: !17, file: !3, line: 3, type: !7) +!73 = distinct !DIAssignID() +!74 = distinct !DIAssignID() +!75 = !DILocalVariable(name: "e", scope: !17, file: !3, line: 3, type: !7) +!76 = distinct !DIAssignID() +!77 = distinct !DIAssignID() +!78 = distinct !DIAssignID() +!79 = !DILocalVariable(name: "f", scope: !17, file: !3, line: 3, type: !7) +!80 = distinct !DIAssignID() +!81 = distinct !DIAssignID() +!82 = !DILocalVariable(name: "g", scope: !17, file: !3, line: 3, type: !7) +!83 = distinct !DIAssignID() +!84 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/nested-loop.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/nested-loop.ll @@ -0,0 +1,398 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG + +;; Test a variety of block inputs and lattice configurations for the assignment +;; tracking analysis (debug-ata). + +;; The CFG looks like this: +;; entry +;; | +;; v +;; do.body <-----+ +;; | | +;; V | +;; do.body1 <--+ | +;; / \ | | +;; / \ | | +;; / \ | | +;; v v | | +;; if.then if.else | | +;; \ / | | +;; \ / | | +;; \ / | | +;; do.cond ----+ | +;; | | +;; v | +;; do.cond4 -----+ +;; | +;; v +;; do.end6 + +;; Key +;; ╔═════════════════════╦═══════════════════════════════════════════════════════════════════╗ +;; ║ thing ║ meaning ║ +;; ╠═════════════════════╬═══════════════════════════════════════════════════════════════════╣ +;; ║ mem= ║ assignment of !n or phi to memory ║ +;; ║ dbg= ║ assignment of !n or phi to source variable ║ +;; ║ phi ║ phi of assignments (operands not traked)* ║ +;; ║ loc= ║ location to use is value (implicit location), stack home, or none ║ +;; ╚═════════════════════╩═══════════════════════════════════════════════════════════════════╝ +;; (*) A phi in the def column represents an assignment made by an untagged store. +;; +;; Variable 'a' (!21) +;; Check initial dbg and mem assignment values are propagated through all blocks. +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!63 dbg=!70 ║ mem=!63 dbg=!70 loc=val ║ +;; ║ do.end6 ║ mem=!63 dbg=!70 loc=val ║ mem=!63 ║ mem=!63 dbg=!63 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'b' (!22) +;; Check mem=dbg assignment on branch in nested loop causes a mem=phi (tested by looking +;; for value-based DBG_VALUE in do.end6). +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!65 dbg=!71 ║ mem=!65 dbg=!71 loc=val ║ +;; ║ if.then ║ mem=phi dbg=phi loc=none ║ mem=!66 dbg=!66 ║ mem=!66 dbg=!66 loc=mem ║ +;; ║ do.end6 ║ mem=phi dbg=phi loc=none ║ mem=!65 ║ mem=phi dbg=!65 loc=val ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'c' (!67) +;; Check initial dbg and mem assignment values are propagated through all blocks, with +;; dbg defs with the inital assignment ID put in do.cond and do.end6 (variable is always +;; in memory). +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!69 dbg=!69 ║ mem=!69 dbg=!69 loc=mem ║ +;; ║ do.cond ║ mem=!69 dbg=!69 loc=mem ║ dbg=!69 ║ mem=!69 dbg=!69 loc=mem ║ +;; ║ do.end6 ║ mem=!69 dbg=!69 loc=mem ║ dbg=!69 ║ mem=!69 dbg=!69 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'd' (!72) +;; Same as above, except the dbg def in do.cond has been swapped for a dbg=mem def (with +;; the initial assignment ID) and has been moved to if.else. +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!74 dbg=!74 ║ mem=!74 dbg=!74 loc=mem ║ +;; ║ if.else ║ mem=!74 dbg=!74 loc=mem ║ mem=!74 dbg=!74 ║ mem=!74 dbg=!74 loc=mem ║ +;; ║ do.end6 ║ mem=!74 dbg=!74 loc=mem ║ dbg=!74 ║ mem=!74 dbg=!74 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'e' (!75) +;; mem defs in entry, if.then and if.else with same ID (!77). Check these join correct +;; (tested using the dbg defs of the same ID - the memory location is valid at each of +;; these with that ID). +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!77 dbg=!78 ║ mem=!77 dbg=!78 loc=val ║ +;; ║ do.body ║ mem=!77 dbg=phi loc=none ║ dbg=!77 ║ mem=!77 dbg=!77 loc=mem ║ +;; ║ do.body1 ║ mem=!77 dbg=!77 loc=mem ║ dbg=!77 ║ mem=!77 dbg=!77 loc=mem ║ +;; ║ if.then ║ mem=!77 dbg=!77 loc=mem ║ mem=!77 ║ mem=!77 dbg=!77 loc=mem ║ +;; ║ if.else ║ mem=!77 dbg=!77 loc=mem ║ mem=!77 ║ mem=!77 dbg=!77 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'f' (!79) +;; mem def in entry and an untagged store in if.else (results in mem=phi, dbg=phi defs). +;; Use dbg defs in do.body, do.body1, do.cond and do.end6 to check the phi-ness +;; has been propagated (the memory loc at each is not a valid location). Check the memory +;; loc is used in if.else after the untagged store. +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=!81 dbg=!81 ║ mem=!81 dbg=!81 loc=mem ║ +;; ║ do.body ║ mem=phi dbg=phi loc=none ║ dbg=!81 ║ mem=phi dbg=!81 loc=val ║ +;; ║ do.body1 ║ mem=phi dbg=phi loc=none ║ dbg=!81 ║ mem=phi dbg=!81 loc=val ║ +;; ║ if.else ║ mem=phi dbg=phi loc=none ║ mem=phi dbg=phi ║ mem=phi dbg=phi loc=mem ║ +;; ║ do.cond ║ mem=phi dbg=phi loc=none ║ dbg=!81 ║ mem=phi dbg=!81 loc=val ║ +;; ║ do.end6 ║ mem=phi dbg=!81 loc=val ║ dbg=!81 ║ mem=!69 dbg=!81 loc=val ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ +;; +;; Variable 'g' (!82) +;; Check that joining loc=none with anything else results in loc=none. The out-loc of +;; entry is set up to be loc=none by following an untagged store with a tagged store, +;; with the linked dbg.assign in another block. The dbg.assign is in do.body - it follows +;; another store linked to it. Importantly, there are other instructions wedged between +;; them, which is how we test that the in-loc is loc=none. The result of encountering +;; a tagged store while the loc=none is to emit nothing. Thus, we check that no location +;; def is emitted in do.body until the dbg.assign is encountered (after the load that was +;; wedged between the store and intrinsic). +;; ╔═════════════╦══════════════════════════╦═════════════════╦══════════════════════════╗ +;; ║ block ║ in ║ def ║ out ║ +;; ╠═════════════╬══════════════════════════╬═════════════════╬══════════════════════════╣ +;; ║ entry ║ ║ mem=phi dbg=phi ║ mem=phi dbg=phi loc=none ║ +;; ║ do.body ║ mem=phi dbg=phi loc=none ║ mem=!84 dbg=!84 ║ mem=!84 dbg=!84 loc=mem ║ +;; ╚═════════════╩══════════════════════════╩═════════════════╩══════════════════════════╝ + +; CHECK-DAG: ![[a:[0-9]+]] = !DILocalVariable(name: "a", +; CHECK-DAG: ![[b:[0-9]+]] = !DILocalVariable(name: "b", +; CHECK-DAG: ![[c:[0-9]+]] = !DILocalVariable(name: "c", +; CHECK-DAG: ![[d:[0-9]+]] = !DILocalVariable(name: "d", +; CHECK-DAG: ![[e:[0-9]+]] = !DILocalVariable(name: "e", +; CHECK-DAG: ![[f:[0-9]+]] = !DILocalVariable(name: "f", +; CHECK-DAG: ![[g:[0-9]+]] = !DILocalVariable(name: "g", + +;; Variables 'c' (!67) and 'd' (!72) are always stack-homed. +; CHECK: - { id: 2, name: c.addr, type: default, offset: 0, size: 4, alignment: 4, +; CHECK-NEXT: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK-NEXT: debug-info-variable: '![[c]]', debug-info-expression: '!DIExpression()', +; CHECK: - { id: 3, name: d.addr, type: default, offset: 0, size: 4, alignment: 4, +; CHECK-NEXT: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK-NEXT: debug-info-variable: '![[d]]', debug-info-expression: '!DIExpression()', + +source_filename = "test.cpp" +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" + +@g_a = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 +@g_b = dso_local local_unnamed_addr global i32 0, align 4, !dbg !5 +@g_c = dso_local local_unnamed_addr global i32 0, align 4, !dbg !8 + +define dso_local noundef i32 @_Z3funii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 !dbg !17 { +entry: + %a.addr = alloca i32, align 4, !DIAssignID !58 ; VAR:a + call void @llvm.dbg.assign(metadata i1 undef, metadata !21, metadata !DIExpression(), metadata !58, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27 ; VAR:a + %b.addr = alloca i32, align 4, !DIAssignID !64 ; VAR:b + call void @llvm.dbg.assign(metadata i1 undef, metadata !22, metadata !DIExpression(), metadata !64, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + %c.addr = alloca i32, align 4, !DIAssignID !68 ; VAR:c + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(), metadata !68, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + %d.addr = alloca i32, align 4, !DIAssignID !73 ; VAR:d + call void @llvm.dbg.assign(metadata i1 undef, metadata !72, metadata !DIExpression(), metadata !73, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + %e.addr = alloca i32, align 4, !DIAssignID !76 ; VAR:e + call void @llvm.dbg.assign(metadata i1 undef, metadata !75, metadata !DIExpression(), metadata !76, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + %f.addr = alloca i32, align 4, !DIAssignID !80 ; VAR:f + call void @llvm.dbg.assign(metadata i1 undef, metadata !79, metadata !DIExpression(), metadata !80, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + %g.addr = alloca i32, align 4, !DIAssignID !83 ; VAR:g + call void @llvm.dbg.assign(metadata i1 undef, metadata !82, metadata !DIExpression(), metadata !83, metadata ptr %g.addr, metadata !DIExpression()), !dbg !27 ; VAR:g + store i32 1, ptr %a.addr, !DIAssignID !70 ; VAR:a + call void @llvm.dbg.assign(metadata i32 1, metadata !21, metadata !DIExpression(), metadata !70, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27 ; VAR:a + store i32 2, ptr %b.addr, !DIAssignID !71 ; VAR:b + call void @llvm.dbg.assign(metadata i32 2, metadata !22, metadata !DIExpression(), metadata !71, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + store i32 12, ptr %g.addr ; VAR:g + store i32 9, ptr %e.addr, !DIAssignID !78 ; VAR:e + call void @llvm.dbg.assign(metadata i32 9, metadata !75, metadata !DIExpression(), metadata !78, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + store i32 3, ptr %a.addr, !DIAssignID !63 ; VAR:a + store i32 4, ptr %b.addr, !DIAssignID !65 ; VAR:b + store i32 5, ptr %c.addr, !DIAssignID !69 ; VAR:c + call void @llvm.dbg.assign(metadata i32 5, metadata !67, metadata !DIExpression(), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + store i32 6, ptr %d.addr, !DIAssignID !74 ; VAR:d + call void @llvm.dbg.assign(metadata i32 6, metadata !72, metadata !DIExpression(), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + store i32 8, ptr %e.addr, !DIAssignID !77 ; VAR:e + store i32 13, ptr %g.addr, !DIAssignID !84 ; VAR:g + store i32 11, ptr %f.addr, !DIAssignID !81 ; VAR:f + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + br label %do.body, !dbg !24 +; CHECK-LABEL: bb.0.entry: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: DBG_VALUE %stack.1.b.addr, $noreg, ![[b]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: DBG_VALUE %stack.4.e.addr, $noreg, ![[e]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: DBG_VALUE %stack.5.f.addr, $noreg, ![[f]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: DBG_VALUE %stack.6.g.addr, $noreg, ![[g]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: MOV32mi %stack.0.a.addr, 1, $noreg, 0, $noreg, 3 +; CHECK-NEXT: DBG_VALUE 1, $noreg, ![[a]], !DIExpression() +; CHECK-NEXT: MOV32mi %stack.1.b.addr, 1, $noreg, 0, $noreg, 4 +; CHECK-NEXT: DBG_VALUE 2, $noreg, ![[b]], !DIExpression() +; CHECK-NEXT: MOV32mi %stack.2.c.addr, 1, $noreg, 0, $noreg, 5 +; CHECK-NEXT: MOV32mi %stack.3.d.addr, 1, $noreg, 0, $noreg, 6 +; CHECK-NEXT: MOV32mi %stack.4.e.addr, 1, $noreg, 0, $noreg, 8 +; CHECK-NEXT: DBG_VALUE 9, $noreg, ![[e]], !DIExpression() +; CHECK-NEXT: MOV32mi %stack.6.g.addr, 1, $noreg, 0, $noreg, 13 +; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[g]], !DIExpression() +; CHECK-NEXT: MOV32mi %stack.5.f.addr, 1, $noreg, 0, $noreg, 11 +; CHECK-NEXT: {{^ *$}} + +do.body: ; preds = %do.cond4, %entry + call void @llvm.dbg.assign(metadata i32 8, metadata !75, metadata !DIExpression(), metadata !77, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + store i32 13, ptr %g.addr, !DIAssignID !84 ; VAR:g + %.pre10 = load i32, ptr @g_a, align 4, !dbg !27 + call void @llvm.dbg.assign(metadata i32 11, metadata !82, metadata !DIExpression(), metadata !84, metadata ptr %g.addr, metadata !DIExpression()), !dbg !27 ; VAR:g + br label %do.body1, !dbg !34 +; CHECK-LABEL: bb.1.do.body: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: DBG_VALUE %stack.4.e.addr, $noreg, ![[e]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression() +; CHECK-NEXT: MOV32mi %stack.6.g.addr, 1, $noreg, 0, $noreg, 13 +; CHECK-NEXT: %0:gr32 = MOV32rm $rip, 1, $noreg, @g_a, $noreg +; CHECK-NEXT: DBG_VALUE %stack.6.g.addr, $noreg, ![[g]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: {{^ *$}} + +do.body1: ; preds = %do.cond, %do.body + %0 = phi i32 [ %.pre10, %do.body ], [ %1, %do.cond ], !dbg !27 + call void @llvm.dbg.assign(metadata i32 8, metadata !75, metadata !DIExpression(), metadata !77, metadata ptr %e.addr, metadata !DIExpression()), !dbg !27 ; VAR:e + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + %tobool.not = icmp eq i32 %0, 0, !dbg !27 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !35 +; CHECK-LABEL: bb.2.do.body1: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK: DBG_VALUE %stack.4.e.addr, $noreg, ![[e]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression() +; CHECK: JMP_1 +; CHECK-NEXT: {{^ *$}} + +if.then: ; preds = %do.body1 + %.pre = load i32, ptr @g_a, align 4, !dbg !27 + store i32 %.pre, ptr %b.addr, !DIAssignID !66 ; VAR:b + call void @llvm.dbg.assign(metadata i32 %.pre, metadata !22, metadata !DIExpression(), metadata !66, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + store i32 8, ptr %e.addr, !DIAssignID !77 ; VAR:e + br label %do.cond, !dbg !39 +; CHECK-LABEL: bb.3.if.then: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: %5:gr32 = MOV32rm +; 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 +; 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) +; CHECK-NEXT: JMP_1 %bb.5 +; CHECK-NEXT: {{^ *$}} + +if.else: ; preds = %do.body1 + store i32 6, ptr %d.addr, !DIAssignID !74 ; VAR:d + call void @llvm.dbg.assign(metadata i32 6, metadata !72, metadata !DIExpression(), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + store i32 8, ptr %e.addr, !DIAssignID !77 ; VAR:e + store i32 10, ptr %f.addr ; VAR:f + br label %do.cond +; CHECK-LABEL: bb.4.if.else: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: MOV32mi %stack.3.d.addr, 1, $noreg, 0, $noreg, 6 +; 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) +; CHECK-NEXT: MOV32mi %stack.5.f.addr, 1, $noreg, 0, $noreg, 10 +; CHECK-NEXT: DBG_VALUE %stack.5.f.addr, $noreg, !36, !DIExpression(DW_OP_deref) +; CHECK-NEXT: {{^ *$}} + +do.cond: ; preds = %if.then, %if.else + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + %1 = load i32, ptr @g_b, align 4, !dbg !43 + %tobool3.not = icmp eq i32 %1, 0, !dbg !43 + br i1 %tobool3.not, label %do.cond4, label %do.body1, !dbg !44, !llvm.loop !45 +; CHECK-LABEL: bb.5.do.cond: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression() +; CHECK: {{^ *$}} + +do.cond4: ; preds = %do.cond + %2 = load i32, ptr @g_c, align 4, !dbg !48 + %tobool5.not = icmp eq i32 %2, 0, !dbg !48 + br i1 %tobool5.not, label %do.end6, label %do.body, !dbg !49, !llvm.loop !50 +; CHECK-LABEL: bb.6.do.cond4: +; CHECK-NEXT: successors +; CHECK-NEXT: {{^ *$}} +; CHECK-NOT: DBG +; CHECK: {{^ *$}} + +do.end6: ; preds = %do.cond4 + call void @llvm.dbg.assign(metadata i32 3, metadata !21, metadata !DIExpression(), metadata !63, metadata ptr %a.addr, metadata !DIExpression()), !dbg !27; VAR:a + call void @llvm.dbg.assign(metadata i32 4, metadata !22, metadata !DIExpression(), metadata !65, metadata ptr %b.addr, metadata !DIExpression()), !dbg !27 ; VAR:b + call void @llvm.dbg.assign(metadata i1 undef, metadata !67, metadata !DIExpression(), metadata !69, metadata ptr %c.addr, metadata !DIExpression()), !dbg !27 ; VAR:c + call void @llvm.dbg.assign(metadata i32 6, metadata !72, metadata !DIExpression(), metadata !74, metadata ptr %d.addr, metadata !DIExpression()), !dbg !27 ; VAR:d + call void @llvm.dbg.assign(metadata i32 11, metadata !79, metadata !DIExpression(), metadata !81, metadata ptr %f.addr, metadata !DIExpression()), !dbg !27 ; VAR:f + ret i32 0, !dbg !53 +; CHECK-LABEL: bb.7.do.end6: +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[a]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: DBG_VALUE 4, $noreg, ![[b]], !DIExpression() +; CHECK-NEXT: DBG_VALUE 11, $noreg, ![[f]], !DIExpression() +} + +declare !dbg !54 void @_Z4calli(i32 noundef) local_unnamed_addr #1 +declare void @llvm.dbg.declare(metadata, metadata, metadata) +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 = !{!10, !11, !12, !13, !14, !15} +!llvm.ident = !{!16} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g_a", 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 16.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{!0, !5, !8} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "g_b", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression()) +!9 = distinct !DIGlobalVariable(name: "g_c", scope: !2, file: !3, line: 1, type: !7, isLocal: false, isDefinition: true) +!10 = !{i32 7, !"Dwarf Version", i32 5} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{i32 8, !"PIC Level", i32 2} +!14 = !{i32 7, !"PIE Level", i32 2} +!15 = !{i32 7, !"uwtable", i32 2} +!16 = !{!"clang version 16.0.0"} +!17 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funii", scope: !3, file: !3, line: 3, type: !18, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !20) +!18 = !DISubroutineType(types: !19) +!19 = !{!7, !7, !7} +!20 = !{!21, !22} +!21 = !DILocalVariable(name: "a", arg: 1, scope: !17, file: !3, line: 3, type: !7) +!22 = !DILocalVariable(name: "b", arg: 2, scope: !17, file: !3, line: 3, type: !7) +!23 = !DILocation(line: 0, scope: !17) +!24 = !DILocation(line: 4, column: 3, scope: !17) +!25 = !DILocation(line: 5, column: 5, scope: !26) +!26 = distinct !DILexicalBlock(scope: !17, file: !3, line: 4, column: 6) +!27 = !DILocation(line: 7, column: 11, scope: !28) +!28 = distinct !DILexicalBlock(scope: !29, file: !3, line: 7, column: 11) +!29 = distinct !DILexicalBlock(scope: !26, file: !3, line: 6, column: 8) +!34 = !DILocation(line: 6, column: 5, scope: !26) +!35 = !DILocation(line: 7, column: 11, scope: !29) +!36 = !DILocation(line: 8, column: 11, scope: !37) +!37 = distinct !DILexicalBlock(scope: !28, file: !3, line: 7, column: 16) +!38 = !DILocation(line: 9, column: 9, scope: !37) +!39 = !DILocation(line: 10, column: 7, scope: !37) +!40 = !DILocation(line: 11, column: 11, scope: !41) +!41 = distinct !DILexicalBlock(scope: !28, file: !3, line: 10, column: 14) +!42 = !DILocation(line: 0, scope: !28) +!43 = !DILocation(line: 13, column: 14, scope: !26) +!44 = !DILocation(line: 13, column: 5, scope: !29) +!45 = distinct !{!45, !34, !46, !47} +!46 = !DILocation(line: 13, column: 17, scope: !26) +!47 = !{!"llvm.loop.mustprogress"} +!48 = !DILocation(line: 14, column: 12, scope: !17) +!49 = !DILocation(line: 14, column: 3, scope: !26) +!50 = distinct !{!50, !24, !51, !47} +!51 = !DILocation(line: 14, column: 15, scope: !17) +!52 = !DILocation(line: 15, column: 12, scope: !17) +!53 = !DILocation(line: 15, column: 3, scope: !17) +!54 = !DISubprogram(name: "call", linkageName: "_Z4calli", scope: !3, file: !3, line: 2, type: !55, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !57) +!55 = !DISubroutineType(types: !56) +!56 = !{null, !7} +!57 = !{} +!58 = distinct !DIAssignID() +!59 = !DILocalVariable(name: "Arr", scope: !17, file: !3, line: 4, type: !60) +!60 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 96, elements: !61) +!61 = !{!62} +!62 = !DISubrange(count: 3) +!63 = distinct !DIAssignID() +!64 = distinct !DIAssignID() +!65 = distinct !DIAssignID() +!66 = distinct !DIAssignID() +!67 = !DILocalVariable(name: "c", scope: !17, file: !3, line: 3, type: !7) +!68 = distinct !DIAssignID() +!69 = distinct !DIAssignID() +!70 = distinct !DIAssignID() +!71 = distinct !DIAssignID() +!72 = !DILocalVariable(name: "d", scope: !17, file: !3, line: 3, type: !7) +!73 = distinct !DIAssignID() +!74 = distinct !DIAssignID() +!75 = !DILocalVariable(name: "e", scope: !17, file: !3, line: 3, type: !7) +!76 = distinct !DIAssignID() +!77 = distinct !DIAssignID() +!78 = distinct !DIAssignID() +!79 = !DILocalVariable(name: "f", scope: !17, file: !3, line: 3, type: !7) +!80 = distinct !DIAssignID() +!81 = distinct !DIAssignID() +!82 = !DILocalVariable(name: "g", scope: !17, file: !3, line: 3, type: !7) +!83 = distinct !DIAssignID() +!84 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/no-redundant-def-after-alloca.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/no-redundant-def-after-alloca.ll @@ -0,0 +1,48 @@ +; RUN: llc %s -o - -stop-after=finalize-isel \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG_ + +;; Hand written. Check that no unnecessary undef is inserted after an alloca +;; that has a linked dbg.assign that doesn't immediately follow it. + +; CHECK: CALL64pcrel32 @a +; CHECK-NEXT: ADJCALLSTACKUP64 +; CHECK-NEXT: DBG_VALUE %stack.0.c, $noreg, !{{.+}}, !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" + +define dso_local void @b() #0 !dbg !7 { +entry: + %c = alloca i8, align 1, !DIAssignID !10 + call void (...) @a(), !dbg !16 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !10, metadata ptr %c, metadata !DIExpression()), !dbg !13 + ret void, !dbg !17 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 +declare dso_local void @a(...) +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!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 12.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0)"} +!7 = distinct !DISubprogram(name: "b", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = distinct !DIAssignID() +!11 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !12) +!12 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!13 = !DILocation(line: 0, scope: !7) +!14 = !DILocation(line: 3, column: 8, scope: !7) +!15 = distinct !DIAssignID() +!16 = !DILocation(line: 4, column: 3, scope: !7) +!17 = !DILocation(line: 5, column: 1, scope: !7) Index: llvm/test/DebugInfo/assignment-tracking/X86/order-of-defs.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/order-of-defs.ll @@ -0,0 +1,59 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG_ + +;; Ensure that the order of several debug intrinsics between non-debug +;; instructions is maintained. + +; CHECK-DAG: ![[A:[0-9]+]] = !DILocalVariable(name: "a", +; CHECK-DAG: ![[B:[0-9]+]] = !DILocalVariable(name: "b", +; CHECK-DAG: ![[C:[0-9]+]] = !DILocalVariable(name: "c", + +; CHECK: DBG_VALUE $esi, $noreg, ![[B]], !DIExpression() +; CHECK-NEXT: DBG_VALUE $edx, $noreg, ![[C]], !DIExpression() +; CHECK-NEXT: DBG_VALUE $esi, $noreg, ![[A]], !DIExpression(DW_OP_LLVM_fragment, 0, 32) +; CHECK-NEXT: DBG_VALUE $edx, $noreg, ![[A]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) + +target triple = "x86_64-unknown-linux-gnu" + +define dso_local i32 @fun(i64 %a.coerce, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 !dbg !7 { +entry: + call void @llvm.dbg.assign(metadata i32 %b, metadata !17, metadata !DIExpression(), metadata !19, metadata ptr undef, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.assign(metadata i32 %c, metadata !18, metadata !DIExpression(), metadata !21, metadata ptr undef, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.assign(metadata i32 %b, metadata !16, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !22, metadata ptr undef, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.assign(metadata i32 %c, metadata !16, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !23, metadata ptr undef, metadata !DIExpression()), !dbg !20 + %mul = mul nsw i32 %c, %b, !dbg !24 + ret i32 %mul, !dbg !25 +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{!"clang version 14.0.0"} +!7 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) +!8 = !DISubroutineType(types: !9) +!9 = !{!10, !11, !10, !10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !1, line: 2, size: 64, elements: !12) +!12 = !{!13, !14} +!13 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !11, file: !1, line: 2, baseType: !10, size: 32) +!14 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !11, file: !1, line: 2, baseType: !10, size: 32, offset: 32) +!15 = !{!16, !17, !18} +!16 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 3, type: !11) +!17 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 3, type: !10) +!18 = !DILocalVariable(name: "c", arg: 3, scope: !7, file: !1, line: 3, type: !10) +!19 = distinct !DIAssignID() +!20 = !DILocation(line: 0, scope: !7) +!21 = distinct !DIAssignID() +!22 = distinct !DIAssignID() +!23 = distinct !DIAssignID() +!24 = !DILocation(line: 6, column: 14, scope: !7) +!25 = !DILocation(line: 6, column: 3, scope: !7) Index: llvm/test/DebugInfo/assignment-tracking/X86/remove-redundant-defs-to-prevent-reordering.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/remove-redundant-defs-to-prevent-reordering.ll @@ -0,0 +1,108 @@ +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=false \ +; RUN: | FileCheck %s --check-prefixes=CHECK,DBGVALUE --implicit-check-not="DBG_VALUE \$noreg" +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=true \ +; RUN: | FileCheck %s --check-prefixes=CHECK,INSTRREF --implicit-check-not="DBG_VALUE \$noreg" + +;; Found in the wild, but this test involves modifications from: +;; int b; +;; void ext(); +;; int fun(int a) { +;; if (b == 0) +;; ext(); +;; +;; a += b; +;; return a; +;; } +;; A `dbg.assign(undef, ...)` has been added by hand in if.end. + +;; For some variable we generate: +;; %inc = add nuw nsw i32 %i.0128, 1 +;; call void @llvm.dbg.value(metadata i32 undef, ... +;; call void @llvm.dbg.value(metadata i32 %inc, ... +;; +;; SelectionDAG swaps the dbg.value positions: +;; %31:gr32 = nuw nsw ADD32ri8 %30:gr32(tied-def 0), 1 +;; DBG_VALUE %31:gr32, ... +;; DBG_VALUE $noreg, ... +;; +;; Make sure to avoid this by removing redundant dbg.values after lowering +;; dbg.assigns. + +;; Check that there's a debug instruction (--implicit-check-not checks no +;; `DBG_VALUE $noreg, ...` has been added). +; CHECK: bb.2.if.end: +; DBGVALUE: DBG_VALUE +; INSTRREF: INSTR_REF + +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" + +@b = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 + +; Function Attrs: uwtable mustprogress +define dso_local i32 @_Z3funi(i32 %a) local_unnamed_addr #0 !dbg !11 { +entry: + call void @llvm.dbg.assign(metadata i1 undef, metadata !15, metadata !DIExpression(), metadata !16, metadata ptr undef, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.assign(metadata i32 %a, metadata !15, metadata !DIExpression(), metadata !18, metadata ptr undef, metadata !DIExpression()), !dbg !17 + %0 = load i32, ptr @b, align 4, !dbg !19 + %cmp = icmp eq i32 %0, 0, !dbg !25 + br i1 %cmp, label %if.then, label %if.end, !dbg !26 + +if.then: ; preds = %entry + tail call void @_Z3extv(), !dbg !27 + %.pre = load i32, ptr @b, align 4, !dbg !28 + br label %if.end, !dbg !27 + +if.end: ; preds = %if.then, %entry + %1 = phi i32 [ %.pre, %if.then ], [ %0, %entry ], !dbg !28 + %add = add nsw i32 %1, %a, !dbg !29 + ;; Added by hand: + call void @llvm.dbg.assign(metadata i32 undef, metadata !15, metadata !DIExpression(), metadata !30, metadata ptr undef, metadata !DIExpression()), !dbg !17 +call void @llvm.dbg.assign(metadata i32 %add, metadata !15, metadata !DIExpression(), metadata !30, metadata ptr undef, metadata !DIExpression()), !dbg !17 + ret i32 %add, !dbg !31 +} + +declare !dbg !32 dso_local void @_Z3extv() local_unnamed_addr #1 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 7, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{!"clang version 12.0.0"} +!11 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funi", scope: !3, file: !3, line: 3, type: !12, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !14) +!12 = !DISubroutineType(types: !13) +!13 = !{!6, !6} +!14 = !{!15} +!15 = !DILocalVariable(name: "a", arg: 1, scope: !11, file: !3, line: 3, type: !6) +!16 = distinct !DIAssignID() +!17 = !DILocation(line: 0, scope: !11) +!18 = distinct !DIAssignID() +!19 = !DILocation(line: 4, column: 7, scope: !20) +!20 = distinct !DILexicalBlock(scope: !11, file: !3, line: 4, column: 7) +!25 = !DILocation(line: 4, column: 9, scope: !20) +!26 = !DILocation(line: 4, column: 7, scope: !11) +!27 = !DILocation(line: 5, column: 5, scope: !20) +!28 = !DILocation(line: 7, column: 8, scope: !11) +!29 = !DILocation(line: 7, column: 5, scope: !11) +!30 = distinct !DIAssignID() +!31 = !DILocation(line: 8, column: 3, scope: !11) +!32 = !DISubprogram(name: "ext", linkageName: "_Z3extv", scope: !3, file: !3, line: 2, type: !33, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !4) +!33 = !DISubroutineType(types: !34) +!34 = !{null} Index: llvm/test/DebugInfo/assignment-tracking/X86/remove-undef-fragment.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/remove-undef-fragment.ll @@ -0,0 +1,106 @@ +; RUN: llc %s -o - -stop-after=finalize-isel \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG + +;; In the IR below, for variable n, we get dbg intrinsics that describe this: +;; +;; entry-block: +;; Frag (off=0, sz=32): non-undef +;; Frag (off=64, sz=64): undef +;; Frag (off=64, sz=32): non-undef +;; +;; The undef is redundant, as it doesn't close any open location ranges. Check +;; that it has been removed. Removing redundant undefs from the entry block +;; helps avoid losing coverage due to SelectionDAG doing weird (/bad) things. +;; Even if SelectionDAG is fixed, fewer redundant DBG instructions is still a +;; valuable goal. + +;; The test +;; -------- +;; We expect to see two DBG instructions, one for each non-undef fragment. We +;; don't bother checking the operands because it doesn't matter if either of +;; these have become undef as a result of SelectionDAG dropping the values +;; (which happens to be the case here). It's just important that SelectionDAG +;; was fed these fragments. + +; CHECK: DBG{{.*}}DIExpression(DW_OP_LLVM_fragment, 0, 32) +; CHECK: DBG{{.*}}DIExpression(DW_OP_LLVM_fragment, 64, 32) + +;; Source +;; ------ +;; IR llvm-reduced from optimized IR generated from, itself reduced from +;; CTMark's bullet source file btScaledBvhTriangleMeshShape.cpp: +;; class a { +;; public: +;; float b[4]; +;; __attribute__((nodebug)) a() {} +;; __attribute__((nodebug)) a(float c, float p2) { +;; b[0] = c; +;; b[2] = p2; +;; } +;; __attribute__((nodebug)) void operator+=(a) { +;; b[0] += 0; +;; b[2] += 2; +;; } +;; __attribute__((nodebug)) float d(a c) { return c.b[0] + c.b[2]; } +;; }; +;; +;; __attribute__((nodebug)) void operator-(a, a); +;; __attribute__((nodebug)) a operator*(float, a p2) { +;; a e(p2.b[0], p2.b[2]); +;; return e; +;; } +;; +;; __attribute__((nodebug)) a x(); +;; __attribute__((nodebug)) a y(int); +;; +;; void k() { +;; __attribute__((nodebug)) a l = x(); +;; __attribute__((nodebug)) a m = l; +;; __attribute__((nodebug)) a ag; +;; a n = 0.f * m; +;; +;; n += a(); +;; __attribute__((nodebug)) a ah(y(0).d(n), 0); +;; ag - ah; +;; } + +define void @_Z1kv({ <2 x float>, <2 x float> } %call, <2 x float> %0, float %n.sroa.6.8.vec.extract) !dbg !7 { +entry: + %call1 = tail call { <2 x float>, <2 x float> } poison(), !dbg !13 + %1 = extractvalue { <2 x float>, <2 x float> } %call, 1 + %add.i = fadd float poison, 0.000000e+00 + call void @llvm.dbg.assign(metadata float %add.i, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !14, metadata ptr undef, metadata !DIExpression()), !dbg !15 + %n.sroa.6.8.vec.extract2 = extractelement <2 x float> %0, i64 0 + %add4.i = fadd float %n.sroa.6.8.vec.extract, 0.000000e+00 + call void @llvm.dbg.value(metadata <2 x float> undef, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64)), !dbg !15 + call void @llvm.dbg.assign(metadata float %add4.i, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata !16, metadata ptr undef, metadata !DIExpression()), !dbg !15 + %add.i23 = fadd float 0.000000e+00, 0.000000e+00 + %ah.sroa.0.0.vec.insert = insertelement <2 x float> zeroinitializer, float %add4.i, i64 0 + tail call void poison(<2 x float> zeroinitializer, <2 x float> zeroinitializer, <2 x float> %ah.sroa.0.0.vec.insert, <2 x float> zeroinitializer) + ret void +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "reduce.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{i32 7, !"frame-pointer", i32 2} +!7 = distinct !DISubprogram(name: "k", linkageName: "_Z1kv", scope: !1, file: !1, line: 25, type: !8, scopeLine: 25, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "n", scope: !7, file: !1, line: 29, type: !12) +!12 = !DICompositeType(tag: DW_TAG_class_type, name: "a", file: !1, line: 1, size: 128, flags: DIFlagFwdDecl | DIFlagNonTrivial, identifier: "_ZTS1a") +!13 = !DILocation(line: 26, scope: !7) +!14 = distinct !DIAssignID() +!15 = !DILocation(line: 0, scope: !7) +!16 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/sdag-dangling-dbgassign.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/sdag-dangling-dbgassign.ll @@ -0,0 +1,218 @@ +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=false \ +; RUN: | FileCheck %s --check-prefixes=CHECK,DBGVALUE +; RUN: llc %s -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=true \ +; RUN: | FileCheck %s --check-prefixes=CHECK,INSTRREF + +;-------------------------------------------------------------------- +; Adapted from sdag-dangling-dbgvalue.ll to test dbg.assign intrinsics. This +; ensures that dbg.assigns with no linked store are treated as dbg.values. For +; ease of writing, all the dbg.assign intrinsics refer to the same DIAssignID +; !54. There is no linked store in any case. +; +; This test case is basically generated from the following C code. +; Compiled with "--target=x86_64-apple-darwin -S -g -O3" to get debug +; info for optimized code. +; +; struct SS { +; int a; +; int b; +; } S = { .a = 23, .b = -17 }; +; +; int test1() { +; struct SS* foo1 = &S; +; return (int)foo1; +; } +; +; int test2() { +; struct SS* foo2 = &S; +; struct SS* bar2 = &S; +; return (int)foo2 + (int)bar2; +; } +; +; int test3() { +; struct SS* bar3 = &S; +; struct SS* foo3 = &S; +; return (int)foo3 + (int)bar3; +; } +; +; int test4() { +; struct SS* foo4 = &S; +; struct SS* bar4 = &S; +; foo = 0; +; return (int)foo4 + (int)bar4; +; } +; +; int test5() { +; struct SS* bar5 = &S; +; struct SS* foo5 = &S; +; foo5 = 0; +; return (int)foo5 + (int)bar5; +; } +;-------------------------------------------------------------------- + +; CHECK: ![[FOO1:.*]] = !DILocalVariable(name: "foo1" +; CHECK: ![[BAR1:.*]] = !DILocalVariable(name: "bar1" +; CHECK: ![[FOO2:.*]] = !DILocalVariable(name: "foo2" +; CHECK: ![[BAR2:.*]] = !DILocalVariable(name: "bar2" +; CHECK: ![[FOO3:.*]] = !DILocalVariable(name: "bar3" +; CHECK: ![[BAR3:.*]] = !DILocalVariable(name: "foo3" +; CHECK: ![[FOO4:.*]] = !DILocalVariable(name: "foo4" +; CHECK: ![[BAR4:.*]] = !DILocalVariable(name: "bar4" +; CHECK: ![[BAR5:.*]] = !DILocalVariable(name: "bar5" +; CHECK: ![[FOO5:.*]] = !DILocalVariable(name: "foo5" + +source_filename = "sdag-dangling-dbgvalue.c" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.4.0" + +%struct.SS = type { i32, i32 } + +@S = global %struct.SS { i32 23, i32 -17 }, align 4, !dbg !0 + +; Verify that the def comes before the for foo1. +define i32 @test1() local_unnamed_addr #0 !dbg !17 { +; CHECK-LABEL: bb.0.entry1 +; CHECK-NEXT: DBG_VALUE 0, $noreg, ![[BAR1]], !DIExpression() +; CHECK-NEXT: [[REG1:%[0-9]+]]:gr64 = LEA64r +; INSTRREF-SAME: debug-instr-number 1 +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0, ![[FOO1]], !DIExpression() +; DBGVALUE-NEXT: DBG_VALUE [[REG1]], $noreg, ![[FOO1]], !DIExpression() +entry1: + call void @llvm.dbg.assign(metadata ptr @S, metadata !20, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !23 + call void @llvm.dbg.assign(metadata ptr null, metadata !22, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !24 + ret i32 ptrtoint (ptr @S to i32), !dbg !25 +} + +; Verify that the def comes before the for foo2 and bar2. +define i32 @test2() local_unnamed_addr #0 !dbg !26 { +; CHECK-LABEL: bb.0.entry2 +; CHECK-NEXT: [[REG2:%[0-9]+]]:gr64 = LEA64r +; INSTRREF-SAME: debug-instr-number 1 +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0, ![[FOO2]], !DIExpression() +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0, ![[BAR2]], !DIExpression() +; DBGVALUE-NEXT: DBG_VALUE [[REG2]], $noreg, ![[FOO2]], !DIExpression +; DBGVALUE-NEXT: DBG_VALUE [[REG2]], $noreg, ![[BAR2]], !DIExpression +entry2: + call void @llvm.dbg.assign(metadata ptr @S, metadata !28, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !30 + call void @llvm.dbg.assign(metadata ptr @S, metadata !29, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !31 + ret i32 add (i32 ptrtoint (ptr @S to i32), i32 ptrtoint (ptr @S to i32)), !dbg !32 +} + +; Verify that the def comes before the for foo3 and bar3. +define i32 @test3() local_unnamed_addr #0 !dbg !33 { +; CHECK-LABEL: bb.0.entry3 +; CHECK-NEXT: [[REG3:%[0-9]+]]:gr64 = LEA64r +; INSTRREF-SAME: debug-instr-number 1 +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0, ![[BAR3]], !DIExpression() +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0, ![[FOO3]], !DIExpression() +; DBGVALUE-NEXT: DBG_VALUE [[REG3]], $noreg, ![[BAR3]], !DIExpression() +; DBGVALUE-NEXT: DBG_VALUE [[REG3]], $noreg, ![[FOO3]], !DIExpression() +entry3: + call void @llvm.dbg.assign(metadata ptr @S, metadata !36, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !38 + call void @llvm.dbg.assign(metadata ptr @S, metadata !35, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !37 + ret i32 add (i32 ptrtoint (ptr @S to i32), i32 ptrtoint (ptr @S to i32)), !dbg !39 +} + +; Verify that the def comes before the for bar4. +define i32 @test4() local_unnamed_addr #0 !dbg !40 { +; CHECK-LABEL: bb.0.entry4 +;; NOTE: The check for `DBG_VALUE $noreg, $noreg, ![[FOO4]], !DIExpression()` +;; has been removed because AT lowering removes redundant debug intrinsics. +; CHECK-NEXT: DBG_VALUE 0, $noreg, ![[FOO4]], !DIExpression() +; CHECK-NEXT: [[REG4:%[0-9]+]]:gr64 = LEA64r +; INSTRREF-SAME: debug-instr-number 1 +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0, ![[BAR4]], !DIExpression() +; DBGVALUE-NEXT: DBG_VALUE [[REG4]], $noreg, ![[BAR4]], !DIExpression() +entry4: + call void @llvm.dbg.assign(metadata ptr @S, metadata !42, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !44 + call void @llvm.dbg.assign(metadata ptr @S, metadata !43, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !45 + call void @llvm.dbg.assign(metadata ptr null, metadata !42, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !44 + ret i32 ptrtoint (ptr @S to i32), !dbg !46 +} + +; Verify that we do not get a DBG_VALUE that maps foo5 to @S here. +define i32 @test5() local_unnamed_addr #0 !dbg !47 { +; CHECK-LABEL: bb.0.entry5: +; cHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[FOO5]], !DIExpression() +; CHECK-NEXT: DBG_VALUE 0, $noreg, ![[FOO5]], !DIExpression() +; CHECK-NEXT: [[REG5:%[0-9]+]]:gr64 = LEA64r +; INSTRREF-SAME: debug-instr-number 1 +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0, ![[BAR5]], !DIExpression() +; DBGVALUE-NEXT: DBG_VALUE [[REG5]], $noreg, ![[BAR5]], !DIExpression() +; CHECK-NOT: DBG_{{.*}} ![[FOO5]], !DIExpression() +; CHECK: RET +entry5: + call void @llvm.dbg.assign(metadata ptr @S, metadata !49, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !51 + call void @llvm.dbg.assign(metadata ptr @S, metadata !50, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !52 + call void @llvm.dbg.assign(metadata ptr null, metadata !50, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !52 + ret i32 ptrtoint (ptr @S to i32), !dbg !53 +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #1 + +attributes #0 = { nounwind readnone uwtable } +attributes #1 = { nounwind readnone speculatable } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!12, !13, !14, !15} +!llvm.ident = !{!16} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "S", scope: !2, file: !3, line: 4, type: !8, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0 (trunk 327229) (llvm/trunk 327239)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !7) +!3 = !DIFile(filename: "sdag-dangling-dbgvalue.c", directory: "/repo/uabbpet/llvm-master") +!4 = !{} +!5 = !{!6} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{!0} +!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "SS", file: !3, line: 1, size: 64, elements: !9) +!9 = !{!10, !11} +!10 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !8, file: !3, line: 2, baseType: !6, size: 32) +!11 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !8, file: !3, line: 3, baseType: !6, size: 32, offset: 32) +!12 = !{i32 2, !"Dwarf Version", i32 2} +!13 = !{i32 2, !"Debug Info Version", i32 3} +!14 = !{i32 1, !"wchar_size", i32 4} +!15 = !{i32 7, !"PIC Level", i32 2} +!16 = !{!"clang version 7.0.0 (trunk 327229) (llvm/trunk 327239)"} +!17 = distinct !DISubprogram(name: "test1", scope: !3, file: !3, line: 6, type: !18, isLocal: false, isDefinition: true, scopeLine: 6, isOptimized: true, unit: !2, retainedNodes: !19) +!18 = !DISubroutineType(types: !5) +!19 = !{!20, !22} +!20 = !DILocalVariable(name: "foo1", scope: !17, file: !3, line: 7, type: !21) +!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) +!22 = !DILocalVariable(name: "bar1", scope: !17, file: !3, line: 8, type: !21) +!23 = !DILocation(line: 7, column: 14, scope: !17) +!24 = !DILocation(line: 8, column: 14, scope: !17) +!25 = !DILocation(line: 9, column: 3, scope: !17) +!26 = distinct !DISubprogram(name: "test2", scope: !3, file: !3, line: 12, type: !18, isLocal: false, isDefinition: true, scopeLine: 12, isOptimized: true, unit: !2, retainedNodes: !27) +!27 = !{!28, !29} +!28 = !DILocalVariable(name: "foo2", scope: !26, file: !3, line: 13, type: !21) +!29 = !DILocalVariable(name: "bar2", scope: !26, file: !3, line: 14, type: !21) +!30 = !DILocation(line: 13, column: 14, scope: !26) +!31 = !DILocation(line: 14, column: 14, scope: !26) +!32 = !DILocation(line: 15, column: 3, scope: !26) +!33 = distinct !DISubprogram(name: "test3", scope: !3, file: !3, line: 18, type: !18, isLocal: false, isDefinition: true, scopeLine: 18, isOptimized: true, unit: !2, retainedNodes: !34) +!34 = !{!35, !36} +!35 = !DILocalVariable(name: "bar3", scope: !33, file: !3, line: 19, type: !21) +!36 = !DILocalVariable(name: "foo3", scope: !33, file: !3, line: 20, type: !21) +!37 = !DILocation(line: 19, column: 14, scope: !33) +!38 = !DILocation(line: 20, column: 14, scope: !33) +!39 = !DILocation(line: 21, column: 3, scope: !33) +!40 = distinct !DISubprogram(name: "test4", scope: !3, file: !3, line: 24, type: !18, isLocal: false, isDefinition: true, scopeLine: 24, isOptimized: true, unit: !2, retainedNodes: !41) +!41 = !{!42, !43} +!42 = !DILocalVariable(name: "foo4", scope: !40, file: !3, line: 25, type: !21) +!43 = !DILocalVariable(name: "bar4", scope: !40, file: !3, line: 26, type: !21) +!44 = !DILocation(line: 25, column: 14, scope: !40) +!45 = !DILocation(line: 26, column: 14, scope: !40) +!46 = !DILocation(line: 28, column: 3, scope: !40) +!47 = distinct !DISubprogram(name: "test5", scope: !3, file: !3, line: 31, type: !18, isLocal: false, isDefinition: true, scopeLine: 31, isOptimized: true, unit: !2, retainedNodes: !48) +!48 = !{!49, !50} +!49 = !DILocalVariable(name: "bar5", scope: !47, file: !3, line: 32, type: !21) +!50 = !DILocalVariable(name: "foo5", scope: !47, file: !3, line: 33, type: !21) +!51 = !DILocation(line: 32, column: 14, scope: !47) +!52 = !DILocation(line: 33, column: 14, scope: !47) +!53 = !DILocation(line: 35, column: 3, scope: !47) +!54 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/sdag-ir-salvage-assign.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/sdag-ir-salvage-assign.ll @@ -0,0 +1,72 @@ +; RUN: llc -mtriple=x86_64-unknown-unknown -start-after=codegenprepare \ +; RUN: -experimental-assignment-tracking \ +; RUN: -stop-before finalize-isel %s -o - \ +; RUN: -experimental-debug-variable-locations=false \ +; RUN: | FileCheck %s --check-prefixes=CHECK,DBGVALUE +; RUN: llc -mtriple=x86_64-unknown-unknown -start-after=codegenprepare \ +; RUN: -experimental-assignment-tracking \ +; RUN: -stop-before finalize-isel %s -o - \ +; RUN: -experimental-debug-variable-locations=true \ +; RUN: | FileCheck %s --check-prefixes=CHECK,INSTRREF + +; Adapted from sdag-ir-salvage.ll to test dbg.assign intrinsics. This ensures +; that dbg.assigns with no linked store are treated as dbg.values. + +; Test that the dbg.value for %baz, which doesn't exist in the 'next' bb, +; can be salvaged back to the underlying argument vreg. + +; CHECK: ![[AAAVAR:.*]] = !DILocalVariable(name: "aaa", +; CHECK-LABEL: bb.0.entry: +; INSTRREF: DBG_PHI $rdi, 1 +; CHECK-LABEL: bb.1.next: +; INSTRREF: DBG_INSTR_REF 1, 0, ![[AAAVAR]] +; DBGVALUE: DBG_VALUE %{{[0-9]+}}, $noreg, ![[AAAVAR]] + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-linux-gnu" + +define i8 @f(ptr %foo) local_unnamed_addr !dbg !6 { +entry: + %bar = getelementptr i32, ptr %foo, i32 4 + %baz = bitcast ptr %bar to ptr + %quux = load i8, ptr %baz + br label %next + +next: ; preds = %entry + tail call void @llvm.dbg.assign(metadata ptr %baz, metadata !15, metadata !DIExpression(), metadata !31, metadata ptr undef, metadata !DIExpression()), !dbg !30 + %xyzzy = add i8 %quux, 123 + br label %fin + +fin: ; preds = %next + %trains = getelementptr i32, ptr %foo, i32 3 + %planes = bitcast ptr %trains to ptr + %cars = load i8, ptr %planes + %ret = add i8 %xyzzy, %cars + ret i8 %ret +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #0 + +attributes #0 = { nounwind readnone speculatable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!25, !26, !27, !28} +!llvm.ident = !{!29} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "test.c", directory: ".") +!2 = !{} +!6 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 18, type: !7, scopeLine: 19, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14) +!7 = !DISubroutineType(types: !8) +!8 = !{!13} +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_unsigned) +!14 = !{!15} +!15 = !DILocalVariable(name: "aaa", scope: !6, file: !1, line: 18, type: !13) +!25 = !{i32 2, !"Dwarf Version", i32 4} +!26 = !{i32 2, !"Debug Info Version", i32 3} +!27 = !{i32 1, !"wchar_size", i32 4} +!28 = !{i32 7, !"PIC Level", i32 2} +!29 = !{!"clang"} +!30 = !DILocation(line: 18, column: 14, scope: !6) +!31 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/sdag-transfer-dbgassign.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/sdag-transfer-dbgassign.ll @@ -0,0 +1,77 @@ +; RUN: llc %s -start-after=codegenprepare -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=false \ +; RUN: | FileCheck %s --check-prefixes=CHECK,DBGVALUE +; RUN: llc %s -start-after=codegenprepare -stop-before finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: -experimental-debug-variable-locations=true \ +; RUN: | FileCheck %s --check-prefixes=CHECK,INSTRREF + +; Adapted from sdag-transfer-dbgvalue.ll to test dbg.assign intrinsics. This +; ensures that dbg.assigns with no linked store are treated as dbg.values. + +; This tests that transferDbgValues() changes order of SDDbgValue transferred +; to another node and debug info for 'ADD32ri' appears *after* the instruction. +; +; This test case was generated from the following program +; using: clang -g -O3 -S -emit-llvm test.c +; +; int foo(int a, int *b) { +; int c = a + 512; +; if (c != 0) +; *b = a; +; return c; +; } + +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" + +; CHECK-LABEL: bb.0.entry: +; DBGVALUE: %[[REG:[0-9]+]]:gr32 = ADD32ri %1, 512 +; DBGVALUE-NEXT: DBG_VALUE %[[REG]] +; INSTRREF: ADD32ri %1, 512, {{.*}}debug-instr-number 1 +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0 + +; Function Attrs: nofree norecurse nounwind uwtable writeonly +define dso_local i32 @foo(i32 %a, ptr nocapture %b) local_unnamed_addr !dbg !7 { +entry: + %add = add nsw i32 %a, 512, !dbg !18 + call void @llvm.dbg.assign(metadata i32 %add, metadata !16, metadata !DIExpression(), metadata !19, metadata ptr undef, metadata !DIExpression()), !dbg !17 + %cmp = icmp eq i32 %add, 0, !dbg !18 + br i1 %cmp, label %if.end, label %if.then, !dbg !18 + +if.then: ; preds = %entry + store i32 %a, ptr %b, align 4, !dbg !18 + br label %if.end, !dbg !18 + +if.end: ; preds = %entry, %if.then + ret i32 %add, !dbg !18 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!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 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "test.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 10.0.0"} +!7 = distinct !DISubprogram(name: "foo", scope: !8, file: !8, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) +!8 = !DIFile(filename: "test.c", directory: "/") +!9 = !DISubroutineType(types: !10) +!10 = !{!11, !11, !12} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64) +!13 = !{!14, !15, !16} +!14 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !8, line: 1, type: !11) +!15 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !8, line: 1, type: !12) +!16 = !DILocalVariable(name: "c", scope: !7, file: !8, line: 2, type: !11) +!17 = !DILocation(line: 0, scope: !7) +!18 = !DILocation(line: 2, column: 13, scope: !7) +!19 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/single-memory-location-2.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/single-memory-location-2.ll @@ -0,0 +1,111 @@ +; RUN: llc -stop-after=finalize-isel %s -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; Check that a dbg.assign for a fully stack-homed variable causes the variable +;; location to appear in the Machine Function side table. Similar to +;; single-memory-location.ll except this has slightly more complicated input +;; (there's a loop and an assignment). +;; +;; $ cat test.cpp +;; int get(); +;; void esc(int*); +;; void doSomething(int); +;; void fun() { +;; int local; +;; esc(&local); +;; while (local) { +;; local = get(); +;; doSomething(local); +;; esc(&local); +;; } +;; } +;; $ clang++ -O2 -g -emit-llvm -S -c -Xclang -fexperimental-assignment-tracking + +; CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "local", +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: local, type: default, offset: 0, size: 4, alignment: 4, +; CHECK-NEXT: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK-NEXT: debug-info-variable: '![[VAR]]', debug-info-expression: '!DIExpression()', +; CHECK-NEXT: debug-info-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" + +; Function Attrs: uwtable mustprogress +define dso_local void @_Z3funv() local_unnamed_addr #0 !dbg !7 { +entry: + %local = alloca i32, align 4, !DIAssignID !13 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !13, metadata ptr %local, metadata !DIExpression()), !dbg !14 + %0 = bitcast ptr %local to ptr, !dbg !15 + call void @llvm.lifetime.start.p0i8(i64 4, ptr nonnull %0) #4, !dbg !15 + call void @_Z3escPi(ptr nonnull %local), !dbg !16 + %1 = load i32, ptr %local, align 4, !dbg !17 + %tobool.not1 = icmp eq i32 %1, 0, !dbg !17 + br i1 %tobool.not1, label %while.end, label %while.body, !dbg !22 + +while.body: ; preds = %entry, %while.body + %call = call i32 @_Z3getv(), !dbg !23 + store i32 %call, ptr %local, align 4, !dbg !25, !DIAssignID !26 + call void @llvm.dbg.assign(metadata i32 %call, metadata !11, metadata !DIExpression(), metadata !26, metadata ptr %local, metadata !DIExpression()), !dbg !14 + call void @_Z11doSomethingi(i32 %call), !dbg !27 + call void @_Z3escPi(ptr nonnull %local), !dbg !28 + %2 = load i32, ptr %local, align 4, !dbg !17 + %tobool.not = icmp eq i32 %2, 0, !dbg !17 + br i1 %tobool.not, label %while.end, label %while.body, !dbg !22, !llvm.loop !29 + +while.end: ; preds = %while.body, %entry + call void @llvm.lifetime.end.p0i8(i64 4, ptr nonnull %0) #4, !dbg !32 + ret void, !dbg !32 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) +declare !dbg !33 dso_local void @_Z3escPi(ptr) local_unnamed_addr +declare !dbg !37 dso_local i32 @_Z3getv() local_unnamed_addr +declare !dbg !40 dso_local void @_Z11doSomethingi(i32) local_unnamed_addr +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture) +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !1, file: !1, line: 4, type: !8, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "local", scope: !7, file: !1, line: 5, type: !12) +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = distinct !DIAssignID() +!14 = !DILocation(line: 0, scope: !7) +!15 = !DILocation(line: 5, column: 3, scope: !7) +!16 = !DILocation(line: 6, column: 3, scope: !7) +!17 = !DILocation(line: 7, column: 10, scope: !7) +!22 = !DILocation(line: 7, column: 3, scope: !7) +!23 = !DILocation(line: 8, column: 13, scope: !24) +!24 = distinct !DILexicalBlock(scope: !7, file: !1, line: 7, column: 17) +!25 = !DILocation(line: 8, column: 11, scope: !24) +!26 = distinct !DIAssignID() +!27 = !DILocation(line: 9, column: 5, scope: !24) +!28 = !DILocation(line: 10, column: 5, scope: !24) +!29 = distinct !{!29, !22, !30, !31} +!30 = !DILocation(line: 11, column: 3, scope: !7) +!31 = !{!"llvm.loop.mustprogress"} +!32 = !DILocation(line: 12, column: 1, scope: !7) +!33 = !DISubprogram(name: "esc", linkageName: "_Z3escPi", scope: !1, file: !1, line: 2, type: !34, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!34 = !DISubroutineType(types: !35) +!35 = !{null, !36} +!36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!37 = !DISubprogram(name: "get", linkageName: "_Z3getv", scope: !1, file: !1, line: 1, type: !38, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!38 = !DISubroutineType(types: !39) +!39 = !{!12} +!40 = !DISubprogram(name: "doSomething", linkageName: "_Z11doSomethingi", scope: !1, file: !1, line: 3, type: !41, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!41 = !DISubroutineType(types: !42) +!42 = !{null, !12} Index: llvm/test/DebugInfo/assignment-tracking/X86/single-memory-location.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/single-memory-location.ll @@ -0,0 +1,80 @@ +; RUN: llc -stop-after=finalize-isel %s -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; Check that a dbg.assign for a fully stack-homed variable causes the variable +;; location to appear in the Machine Function side table. +;; +;; $ cat test.cpp +;; void maybe_writes(int*); +;; void ext(int, int, int, int, int, int, int, int, int, int); +;; int example() { +;; int local; +;; maybe_writes(&local); +;; ext(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +;; return local; +;; } +;; $ clang++ -O2 -g -emit-llvm -S -c -Xclang -fexperimental-assignment-tracking + +; CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "local", +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: local, type: default, offset: 0, size: 4, alignment: 4, +; CHECK-NEXT: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK-NEXT: debug-info-variable: '![[VAR]]', debug-info-expression: '!DIExpression()', +; CHECK-NEXT: debug-info-location: '!{{.+}}' } + +source_filename = "test.cpp" +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" + +define dso_local i32 @_Z7examplev() local_unnamed_addr !dbg !7 { +entry: + %local = alloca i32, align 4, !DIAssignID !13 + call void @llvm.dbg.assign(metadata i1 undef, metadata !12, metadata !DIExpression(), metadata !13, metadata ptr %local, metadata !DIExpression()), !dbg !14 + %0 = bitcast ptr %local to ptr, !dbg !15 + call void @llvm.lifetime.start.p0i8(i64 4, ptr nonnull %0), !dbg !15 + call void @_Z12maybe_writesPi(ptr nonnull %local), !dbg !16 + call void @_Z3extiiiiiiiiii(i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9), !dbg !17 + %1 = load i32, ptr %local, align 4, !dbg !18 + call void @llvm.lifetime.end.p0i8(i64 4, ptr nonnull %0), !dbg !23 + ret i32 %1, !dbg !24 +} + +declare !dbg !25 dso_local void @_Z12maybe_writesPi(ptr) local_unnamed_addr +declare !dbg !29 dso_local void @_Z3extiiiiiiiiii(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) local_unnamed_addr +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture) +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "example", linkageName: "_Z7examplev", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DILocalVariable(name: "local", scope: !7, file: !1, line: 4, type: !10) +!13 = distinct !DIAssignID() +!14 = !DILocation(line: 0, scope: !7) +!15 = !DILocation(line: 4, column: 4, scope: !7) +!16 = !DILocation(line: 5, column: 4, scope: !7) +!17 = !DILocation(line: 6, column: 4, scope: !7) +!18 = !DILocation(line: 7, column: 11, scope: !7) +!23 = !DILocation(line: 8, column: 1, scope: !7) +!24 = !DILocation(line: 7, column: 4, scope: !7) +!25 = !DISubprogram(name: "maybe_writes", linkageName: "_Z12maybe_writesPi", scope: !1, file: !1, line: 1, type: !26, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!26 = !DISubroutineType(types: !27) +!27 = !{null, !28} +!28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) +!29 = !DISubprogram(name: "ext", linkageName: "_Z3extiiiiiiiiii", scope: !1, file: !1, line: 2, type: !30, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!30 = !DISubroutineType(types: !31) +!31 = !{null, !10, !10, !10, !10, !10, !10, !10, !10, !10, !10} Index: llvm/test/DebugInfo/assignment-tracking/X86/split-alloca.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/split-alloca.ll @@ -0,0 +1,58 @@ +; RUN: llc %s -o - -stop-after=finalize-isel \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG + +;; Hand written. Check that we fall back to emitting a list of defs for +;; variables with split allocas (i.e. we want to see DBG_VALUEs and no +;; debug-info-variable entry in the stack slot table). + +; CHECK: stack: +; CHECK: - { id: 0, name: a, type: default, offset: 0, size: 4, alignment: 4, +; CHECK: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK: debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +; CHECK: - { id: 1, name: c, type: default, offset: 0, size: 4, alignment: 4, +; CHECK: stack-id: default, callee-saved-register: '', callee-saved-restored: true, +; CHECK: debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +; CHECK: DBG_VALUE %stack.0.a, $noreg, !{{.*}}, !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32) +; CHECK: DBG_VALUE %stack.1.c, $noreg, !{{.*}}, !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 64, 32) + +target triple = "x86_64-unknown-linux-gnu" + +define dso_local void @fun() !dbg !7 { +entry: + %a = alloca i32, align 4, !DIAssignID !16 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !16, metadata ptr %a, metadata !DIExpression()), !dbg !17 + %c = alloca i32, align 4, !DIAssignID !20 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata !20, metadata ptr %c, metadata !DIExpression()), !dbg !17 + store i32 5, ptr %a, !DIAssignID !21 + ret void, !dbg !19 +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{!"clang version 14.0.0"} +!7 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "local", scope: !7, file: !1, line: 2, type: !12) +!12 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 96, elements: !14) +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !{!15} +!15 = !DISubrange(count: 3) +!16 = distinct !DIAssignID() +!17 = !DILocation(line: 0, scope: !7) +!18 = !DILocation(line: 2, column: 3, scope: !7) +!19 = !DILocation(line: 3, column: 1, scope: !7) +!20 = distinct !DIAssignID() +!21 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-frag.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-frag.ll @@ -0,0 +1,82 @@ +; RUN: llc %s -stop-after=finalize-isel -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG_ + +;; Hand-written to test untagged store handling on a simple case. Here's what +;; we're looking at in the IR: + +;; 1. mem(a): bits [0, 64) = !14 +;; 2. dbg(a): bits [0, 64) = !14 ; Use memory loc +;; 3. dbg(a): bits [0, 32) = ; Use implicit loc, dbg.value has no ID +;; 4. dbg(a): bits [32, 64) = !16 ; These bits don't use mem loc. +;; ; Linked to a def that comes later ---+ +;; ... ; | +;; 5. mem(a): bits [0, 32) = ; Untagged store ; | +;; .. ; | +;; 6. mem(a): bits [32, 64) = !16 ; <-----------------------------------+ + +;; Taking the '.' above as the 'position', check we get defs that look +;; like this: +;; Position | bits [0, 32) | bits [32, 64) +;; ---------+--------------+--------------- +;; 2. | Mem | Mem +;; 3. | Value | Mem +;; 4. | Value | Value +;; 5. | Mem | Value +;; 6. | Mem | Mem + +; CHECK-DAG: ![[A:[0-9]+]] = !DILocalVariable(name: "a", + +; CHECK: DBG_VALUE %stack.0.a.addr, $noreg, ![[A]], !DIExpression(DW_OP_deref) +; CHECK-NEXT: DBG_VALUE 5, $noreg, ![[A]], !DIExpression(DW_OP_LLVM_fragment, 0, 32) +; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[A]], !DIExpression(DW_OP_LLVM_fragment, 32, 32) +; CHECK-NEXT: MOV32mi %stack.0.a.addr, 1, $noreg, 0, $noreg, 123 +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[A]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 32) +; CHECK-NEXT: MOV32mr %stack.0.a.addr, 1, $noreg, 4, $noreg, %1 :: (store (s32) into %ir.add.ptr, align 8) +; CHECK-NEXT: DBG_VALUE %stack.0.a.addr, $noreg, ![[A]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 32, 32) + +;; NOTE: The second and third DBG_VALUE combined make the first redundant. If +;; removeRedundantDbgInstrs gets smarter, add an instruction between the first +;; dbg.assign and the subsequent dbg.value. + +target triple = "x86_64-unknown-linux-gnu" + +define dso_local noundef i64 @_Z1fl(i64 noundef %a, i32 %b) #0 !dbg !8 { +entry: + %a.addr = alloca i64, align 8, !DIAssignID !13 + call void @llvm.dbg.assign(metadata i1 undef, metadata !14, metadata !DIExpression(), metadata !13, metadata ptr %a.addr, metadata !DIExpression()), !dbg !15 + call void @llvm.dbg.value(metadata i64 5, metadata !14, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !15 + call void @llvm.dbg.assign(metadata i1 undef, metadata !14, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !16, metadata ptr %a.addr, metadata !DIExpression()), !dbg !15 + %frag.addr = bitcast ptr %a.addr to ptr + store i32 123, ptr %frag.addr, align 8 + %0 = bitcast ptr %a.addr to ptr + %add.ptr = getelementptr inbounds i32, ptr %0, i64 1 + store i32 %b, ptr %add.ptr, align 8, !DIAssignID !16 + %1 = load i64, ptr %a.addr, align 8 + ret i64 %1 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #1 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{i32 7, !"frame-pointer", i32 2} +!7 = !{!"clang version 14.0.0"} +!8 = distinct !DISubprogram(name: "f", linkageName: "_Z1fl", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !12) +!9 = !DISubroutineType(types: !10) +!10 = !{!11, !11} +!11 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) +!12 = !{} +!13 = distinct !DIAssignID() +!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 1, type: !11) +!15 = !DILocation(line: 0, scope: !8) +!16 = distinct !DIAssignID() +!17 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 1, type: !11) Index: llvm/test/DebugInfo/assignment-tracking/X86/use-known-value-at-early-mem-def-2.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/use-known-value-at-early-mem-def-2.ll @@ -0,0 +1,93 @@ +; RUN: llc %s -stop-after=finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG_VALUE + +;; Check that sandwiching instructions between a linked store and dbg.assign +;; results in a dbg.value(prev_value) being inserted at the store, and a +;; dbg.value(deref) at the dbg.assign. +;; Same as use-known-value-at-early-mem-def.ll except the "early mem def" is +;; for a fragment of the variable rather than the whole. + +; CHECK: bb.0.entry: +; CHECK-NEXT: DBG_VALUE %stack.0.c, $noreg, ![[var:[0-9]+]], !DIExpression(DW_OP_deref), debug-location +; CHECK: MOV64mi32 %stack.0.c, 1, $noreg, 0, $noreg, 5 +;; No DBG_VALUE required because the stack location is still valid. + +; CHECK: MOV32mi %stack.0.c, 1, $noreg, 0, $noreg, 1 +; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[var]], !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location +;; This DBG_VALUE is added by the frag-agg pass because bits [32, 64) are still +;; 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) + +; 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 + +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" + +; Function Attrs: nounwind uwtable +define dso_local void @b() local_unnamed_addr #0 !dbg !7 { +entry: + %c = alloca i64, align 1, !DIAssignID !13 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !13, metadata i64* %c, metadata !DIExpression()), !dbg !14 + call void @llvm.lifetime.start.p0i64(i64 1, i64* nonnull %c) #4, !dbg !15 + store i64 5, i64* %c, align 1, !dbg !16, !DIAssignID !20 + call void @llvm.dbg.assign(metadata i64 5, metadata !11, metadata !DIExpression(), metadata !20, metadata i64* %c, metadata !DIExpression()), !dbg !14 + tail call void (...) @d() #4, !dbg !21 + + ; --- VV Hand written VV --- ; + %bc = bitcast i64* %c to i32* + store i32 1, i32* %bc, align 1, !dbg !16, !DIAssignID !31 + ;; Check that a dbg.value(undef, frag(0, 32)) is inserted here. The value of + ;; the fragment is "unknown". TODO: In this case the value of the fragment is + ;; still obviously 5; a future improvement could be to be smarter and work + ;; this out. But that's a lot of work for an uncommon case. + tail call void (...) @d() #4, !dbg !21 + call void @llvm.dbg.assign(metadata i32 1, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !31, metadata i32* %bc, metadata !DIExpression()), !dbg !14 + ; --- AA Hand written AA --- ; + + call void @a(i64* nonnull %c) #4, !dbg !22 + call void @llvm.lifetime.end.p0i64(i64 1, i64* nonnull %c) #4, !dbg !23 + ret void, !dbg !23 +} + +declare void @llvm.lifetime.start.p0i64(i64 immarg, i64* nocapture) #1 +declare !dbg !24 dso_local void @d(...) local_unnamed_addr #2 +declare !dbg !27 dso_local void @a(i64*) local_unnamed_addr #2 +declare void @llvm.lifetime.end.p0i64(i64 immarg, i64* nocapture) #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +!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 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "reduce.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "b", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 4, type: !12) +!12 = !DIBasicType(name: "char", size: 64, encoding: DW_ATE_unsigned) +!13 = distinct !DIAssignID() +!14 = !DILocation(line: 0, scope: !7) +!15 = !DILocation(line: 4, column: 3, scope: !7) +!16 = !DILocation(line: 4, column: 8, scope: !7) +!20 = distinct !DIAssignID() +!21 = !DILocation(line: 5, column: 3, scope: !7) +!22 = !DILocation(line: 6, column: 3, scope: !7) +!23 = !DILocation(line: 7, column: 1, scope: !7) +!24 = !DISubprogram(name: "d", scope: !1, file: !1, line: 2, type: !25, spFlags: DISPFlagOptimized, retainedNodes: !2) +!25 = !DISubroutineType(types: !26) +!26 = !{null, null} +!27 = !DISubprogram(name: "a", scope: !1, file: !1, line: 1, type: !28, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!28 = !DISubroutineType(types: !29) +!29 = !{null, !30} +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!31 = distinct !DIAssignID() Index: llvm/test/DebugInfo/assignment-tracking/X86/use-known-value-at-early-mem-def.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/assignment-tracking/X86/use-known-value-at-early-mem-def.ll @@ -0,0 +1,83 @@ +; RUN: llc %s -stop-after=finalize-isel -o - \ +; RUN: -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not=DBG_VALUE + +;; Check that sandwiching instructions between a linked store and dbg.assign +;; results in a dbg.value(prev_value) being inserted at the store, and a +;; dbg.value(deref) at the dbg.assign. + +; CHECK: bb.0.entry: +; CHECK-NEXT: DBG_VALUE %stack.0.c, $noreg, ![[var:[0-9]+]], !DIExpression(DW_OP_deref), debug-location +; CHECK: MOV8mi %stack.0.c, 1, $noreg, 0, $noreg, 0 +;; No DBG_VALUE required because the stack location is still valid. + +; CHECK: MOV8mi %stack.0.c, 1, $noreg, 0, $noreg, 1 +; CHECK-NEXT: DBG_VALUE 0, $noreg, ![[var]], !DIExpression(), debug-location +; CHECK: CALL64pcrel32 @d +; CHECK-NEXT: ADJCALLSTACKUP64 +; 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" + +; Function Attrs: nounwind uwtable +define dso_local void @b() local_unnamed_addr #0 !dbg !7 { +entry: + %c = alloca i8, align 1, !DIAssignID !13 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !13, metadata i8* %c, metadata !DIExpression()), !dbg !14 + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %c) #4, !dbg !15 + store i8 0, i8* %c, align 1, !dbg !16, !DIAssignID !20 + call void @llvm.dbg.assign(metadata i8 0, metadata !11, metadata !DIExpression(), metadata !20, metadata i8* %c, metadata !DIExpression()), !dbg !14 + tail call void (...) @d() #4, !dbg !21 + + ; --- VV Hand written VV --- ; + store i8 1, i8* %c, align 1, !dbg !16, !DIAssignID !31 + ; Check that a dbg.value(0) is inserted here. + tail call void (...) @d() #4, !dbg !21 + call void @llvm.dbg.assign(metadata i8 1, metadata !11, metadata !DIExpression(), metadata !31, metadata i8* %c, metadata !DIExpression()), !dbg !14 + ; --- AA Hand written AA --- ; + + call void @a(i8* nonnull %c) #4, !dbg !22 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %c) #4, !dbg !23 + ret void, !dbg !23 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 +declare !dbg !24 dso_local void @d(...) local_unnamed_addr #2 +declare !dbg !27 dso_local void @a(i8*) local_unnamed_addr #2 +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +!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 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "reduce.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "b", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 4, type: !12) +!12 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!13 = distinct !DIAssignID() +!14 = !DILocation(line: 0, scope: !7) +!15 = !DILocation(line: 4, column: 3, scope: !7) +!16 = !DILocation(line: 4, column: 8, scope: !7) +!20 = distinct !DIAssignID() +!21 = !DILocation(line: 5, column: 3, scope: !7) +!22 = !DILocation(line: 6, column: 3, scope: !7) +!23 = !DILocation(line: 7, column: 1, scope: !7) +!24 = !DISubprogram(name: "d", scope: !1, file: !1, line: 2, type: !25, spFlags: DISPFlagOptimized, retainedNodes: !2) +!25 = !DISubroutineType(types: !26) +!26 = !{null, null} +!27 = !DISubprogram(name: "a", scope: !1, file: !1, line: 1, type: !28, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!28 = !DISubroutineType(types: !29) +!29 = !{null, !30} +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!31 = distinct !DIAssignID()