[llvm] 231bf52 - [InstrRef][FastISel] Support emitting DBG_INSTR_REF from fast-isel

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 16 05:56:48 PDT 2021


Author: Jeremy Morse
Date: 2021-07-16T13:56:15+01:00
New Revision: 231bf52119ee09ed4b4977195d83dd7f42a2e13c

URL: https://github.com/llvm/llvm-project/commit/231bf52119ee09ed4b4977195d83dd7f42a2e13c
DIFF: https://github.com/llvm/llvm-project/commit/231bf52119ee09ed4b4977195d83dd7f42a2e13c.diff

LOG: [InstrRef][FastISel] Support emitting DBG_INSTR_REF from fast-isel

If you attach __attribute__((optnone)) to a function when using
optimisations, that function will use fast-isel instead of the usual
SelectionDAG method. This is a problem for instruction referencing,
because it means DBG_VALUEs of virtual registers will be created,
triggering some safety assertions in LiveDebugVariables. Those assertions
exist to detect exactly this scenario, where an unexpected piece of code is
generating virtual register references in instruction referencing mode.

Fix this by transforming the DBG_VALUEs created by fast-isel into
half-formed DBG_INSTR_REFs, after which they get patched up in
finalizeDebugInstrRefs. The test modified adds a fast-isel mode to the
instruction referencing isel test.

Differential Revision: https://reviews.llvm.org/D105694

Added: 
    

Modified: 
    llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
    llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 8e0bc53f2f2b..4ca731cfdf62 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1256,9 +1256,21 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
              "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 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
     } 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
index 303e706e1270..e661955173f6 100644
--- 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 @@ entry:
 ; 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 @@ entry:
 ; 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 
diff erent (6 for
+;; normal isel, 4 for fast-isel). This isn't because of debug-info 
diff erences,
+;; 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 @@ shoes:
   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 
diff erent 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 @@ shoes:
 !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)


        


More information about the llvm-commits mailing list