diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1256,9 +1256,21 @@ "Expected inlined-at fields to agree"); // A dbg.declare describes the address of a source variable, so lower it // into an indirect DBG_VALUE. - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(TargetOpcode::DBG_VALUE), /*IsIndirect*/ true, - *Op, DI->getVariable(), DI->getExpression()); + auto Builder = + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(TargetOpcode::DBG_VALUE), /*IsIndirect*/ true, *Op, + DI->getVariable(), DI->getExpression()); + + // If using instruction referencing, mutate this into a DBG_INSTR_REF, + // to be later patched up by finalizeDebugInstrRefs. Tack a deref onto + // the expression, we don't have an "indirect" flag in DBG_INSTR_REF. + if (TM.Options.ValueTrackingVariableLocations && Op->isReg()) { + Builder->setDesc(TII.get(TargetOpcode::DBG_INSTR_REF)); + Builder->getOperand(1).ChangeToImmediate(0); + auto *NewExpr = + DIExpression::prepend(DI->getExpression(), DIExpression::DerefBefore); + Builder->getOperand(3).setMetadata(NewExpr); + } } else { // We can't yet handle anything else here because it would require // generating code, thus altering codegen because of debug info. @@ -1301,8 +1313,16 @@ } else if (Register Reg = lookUpRegForValue(V)) { // FIXME: This does not handle register-indirect values at offset 0. bool IsIndirect = false; - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II, IsIndirect, Reg, - DI->getVariable(), DI->getExpression()); + auto Builder = + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II, IsIndirect, Reg, + DI->getVariable(), DI->getExpression()); + + // If using instruction referencing, mutate this into a DBG_INSTR_REF, + // to be later patched up by finalizeDebugInstrRefs. + if (TM.Options.ValueTrackingVariableLocations) { + Builder->setDesc(TII.get(TargetOpcode::DBG_INSTR_REF)); + Builder->getOperand(1).ChangeToImmediate(0); + } } else { // We don't know how to handle other cases, so we drop. LLVM_DEBUG(dbgs() << "Dropping debug info for " << *DI << "\n"); diff --git a/llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll b/llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll --- a/llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll +++ b/llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll @@ -6,6 +6,18 @@ ; RUN: -experimental-debug-variable-locations -verify-machineinstrs \ ; RUN: | FileCheck %s --check-prefix=INSTRREF \ ; RUN: --implicit-check-not=DBG_VALUE +; RUN: llc %s -mtriple=x86_64-unknown-unknown -o - -stop-before=finalize-isel \ +; RUN: -experimental-debug-variable-locations -verify-machineinstrs \ +; RUN: -fast-isel \ +; RUN: | FileCheck %s --check-prefix=FASTISEL-INSTRREF \ +; RUN: --implicit-check-not=DBG_VALUE + +; NORMAL: ![[SOCKS:[0-9]+]] = !DILocalVariable(name: "socks", +; NORMAL: ![[KNEES:[0-9]+]] = !DILocalVariable(name: "knees", +; INSTRREF: ![[SOCKS:[0-9]+]] = !DILocalVariable(name: "socks", +; INSTRREF: ![[KNEES:[0-9]+]] = !DILocalVariable(name: "knees", +; FASTISEL-INSTRREF: ![[SOCKS:[0-9]+]] = !DILocalVariable(name: "socks", +; FASTISEL-INSTRREF: ![[KNEES:[0-9]+]] = !DILocalVariable(name: "knees", ; Test that SelectionDAG produces DBG_VALUEs normally, but DBG_INSTR_REFs when ; asked. @@ -30,11 +42,23 @@ ; INSTRREF-SAME: debug-instr-number 2 ; INSTRREF-NEXT: DBG_INSTR_REF 2, 0 +; Test that fast-isel will produce DBG_INSTR_REFs too. + +; FASTISEL-INSTRREF-LABEL: name: foo + +; FASTISEL-INSTRREF: ADD32rr +; FASTISEL-INSTRREF-SAME: debug-instr-number 1 +; FASTISEL-INSTRREF-NEXT: DBG_INSTR_REF 1, 0 +; FASTISEL-INSTRREF-NEXT: ADD32rr +; FASTISEL-INSTRREF-SAME: debug-instr-number 2 +; FASTISEL-INSTRREF-NEXT: DBG_INSTR_REF 2, 0 + @glob32 = global i32 0 @glob16 = global i16 0 @glob8 = global i8 0 declare void @llvm.dbg.value(metadata, metadata, metadata) +declare void @llvm.dbg.declare(metadata, metadata, metadata) define i32 @foo(i32 %bar, i32 %baz, i32 %qux) !dbg !7 { entry: @@ -87,6 +111,29 @@ ; INSTRREF: DBG_INSTR_REF 4, 0 ; INSTRREF: DBG_INSTR_REF 6, 0 +;; In fast-isel, we get four DBG_INSTR_REFs (compared to three and one +;; DBG_VALUE with normal isel). We get additional substitutions as a result: + +; FASTISEL-INSTRREF: debugValueSubstitutions: +; FASTISEL-INSTRREF-NEXT: - { srcinst: 3, srcop: 0, dstinst: 2, dstop: 0, subreg: 6 } +; FASTISEL-INSTRREF-NEXT: - { srcinst: 5, srcop: 0, dstinst: 4, dstop: 0, subreg: 6 } +; FASTISEL-INSTRREF-NEXT: - { srcinst: 6, srcop: 0, dstinst: 5, dstop: 0, subreg: 4 } +; FASTISEL-INSTRREF-NEXT - { srcinst: 8, srcop: 0, dstinst: 7, dstop: 0, subreg: 6 } +; FASTISEL-INSTRREF-NEXT - { srcinst: 9, srcop: 0, dstinst: 8, dstop: 0, subreg: 4 } +; FASTISEL-INSTRREF-NEXT - { srcinst: 10, srcop: 0, dstinst: 9, dstop: 0, subreg: 1 } + +;; Those substitutions are anchored against these DBG_PHIs: + +; FASTISEL-INSTRREF: DBG_PHI $rdi, 7 +; FASTISEL-INSTRREF-NEXT: DBG_PHI $rdi, 4 +; FASTISEL-INSTRREF-NEXT: DBG_PHI $rdi, 2 +; FASTISEL-INSTRREF-NEXT: DBG_PHI $rdi, 1 + +; FASTISEL-INSTRREF: DBG_INSTR_REF 1, 0 +; FASTISEL-INSTRREF: DBG_INSTR_REF 3, 0 +; FASTISEL-INSTRREF: DBG_INSTR_REF 6, 0 +; FASTISEL-INSTRREF: DBG_INSTR_REF 10, 0 + define i32 @bar(i64 %bar) !dbg !20 { entry: call void @llvm.dbg.value(metadata i64 %bar, metadata !21, metadata !DIExpression()), !dbg !22 @@ -124,9 +171,23 @@ ; INSTRREF: debugValueSubstitutions: ; INSTRREF-NEXT: - { srcinst: 2, srcop: 0, dstinst: 1, dstop: 6, subreg: 4 } -; INSTRREF: CALL64pcrel32 target-flags(x86-plt) @xyzzy, {{.*}} debug-instr-number 1 +; INSTRREF: CALL64pcrel32 target-flags(x86-plt) @xyzzy, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def $rax, debug-instr-number 1 ; INSTRREF: DBG_INSTR_REF 2, 0 +;; Fast-isel produces the same arrangement, a DBG_INSTR_REF pointing back to +;; the call instruction. However: the operand numbers are different (6 for +;; normal isel, 4 for fast-isel). This isn't because of debug-info differences, +;; it's because normal isel implicit-defs the stack registers, and fast-isel +;; does not. The meaning is the same. + +; FASTISEL-INSTRREF-LABEL: name: baz + +; FASTISEL-INSTRREF: debugValueSubstitutions: +; FASTISEL-INSTRREF-NEXT: - { srcinst: 2, srcop: 0, dstinst: 1, dstop: 4, subreg: 4 } + +; FASTISEL-INSTRREF: CALL64pcrel32 target-flags(x86-plt) @xyzzy, csr_64, implicit $rsp, implicit $ssp, implicit-def $rax, debug-instr-number 1 +; FASTISEL-INSTRREF: DBG_INSTR_REF 2, 0 + declare i64 @xyzzy() define i32 @baz() !dbg !30 { @@ -144,6 +205,59 @@ br label %slippers } +;; Test for dbg.declare of non-stack-slot Values. These turn up with NRVO and +;; other ABI scenarios where something is technically in memory, but we don't +;; refer to it relative to the stack pointer. We refer to these either with an +;; indirect DBG_VAUE, or a DBG_INSTR_REF with DW_OP_deref prepended. +;; +;; Test an inlined dbg.declare in a different scope + block, to test behaviours +;; where the debug intrinsic isn't in the first block. The normal-mode DBG_VALUE +;; is hoisted into the entry block for that. This is fine because the variable +;; location is never re-assigned. (FIXME: do we scope-trim / fail-to-propagate +;; these hoisted locations later?). + +; NORMAL-LABEL: name: qux +; +; NORMAL: DBG_VALUE $rdi, 0, ![[SOCKS]], !DIExpression(), +; NORMAL-NEXT: %0:gr64 = COPY $rdi +; NORMAL-NEXT: DBG_VALUE %0, 0, ![[SOCKS]], !DIExpression(), +; NORMAL-NEXT: DBG_VALUE %0, 0, ![[KNEES]], !DIExpression(), + +;; In instruction referencing mode, the "real" argument becomes a DBG_VALUE, +;; but the hoisted variable location from the inlined scope is a DBG_INSTR_REF. + +; INSTRREF-LABEL: name: qux + +; INSTRREF: DBG_PHI $rdi, 1 +; INSTRREF-NEXT: DBG_VALUE $rdi, 0, ![[SOCKS]], !DIExpression(), +; INSTRREF-NEXT: %0:gr64 = COPY $rdi +; INSTRREF-NEXT: DBG_INSTR_REF 1, 0, ![[KNEES]], !DIExpression(DW_OP_deref), + +; In fast-isel mode, neither variable are hoisted or forwarded to a physreg. + +; FASTISEL-INSTRREF-LABEL: name: qux + +; FASTISEL-INSTRREF: DBG_PHI $rdi, 2 +; FASTISEL-INSTRREF-NEXT: DBG_PHI $rdi, 1 +; FASTISEL-INSTRREF: DBG_INSTR_REF 1, 0, ![[SOCKS]], !DIExpression(DW_OP_deref), + +; FASTISEL-INSTRREF-LABEL: bb.1.lala: +; FASTISEL-INSTRREF: DBG_INSTR_REF 2, 0, ![[KNEES]], !DIExpression(DW_OP_deref), +declare i64 @cheddar(i32 *%arg) + +define void @qux(i32* noalias sret(i32) %agg.result) !dbg !40 { +entry: + call void @llvm.dbg.declare(metadata i32 *%agg.result, metadata !41, metadata !DIExpression()), !dbg !42 + %foo = call i64 @cheddar(i32 *%agg.result), !dbg !42 + br label %lala + +lala: + call void @llvm.dbg.declare(metadata i32 *%agg.result, metadata !45, metadata !DIExpression()), !dbg !44 + ret void, !dbg !44 +} + + + !llvm.dbg.cu = !{!0} !llvm.module.flags = !{!3, !4} @@ -165,3 +279,9 @@ !30 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) !31 = !DILocalVariable(name: "xyzzy", scope: !30, file: !1, line: 6, type: !10) !32 = !DILocation(line: 1, scope: !30) +!40 = distinct !DISubprogram(name: "qux", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!41 = !DILocalVariable(name: "socks", scope: !40, file: !1, line: 6, type: !10) +!42 = !DILocation(line: 1, scope: !40) +!43 = distinct !DISubprogram(name: "inlined", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!44 = !DILocation(line: 0, scope: !43, inlinedAt: !42) +!45 = !DILocalVariable(name: "knees", scope: !43, file: !1, line: 6, type: !10)