[llvm] e9641c9 - [DebugInfo][InstrRef][2/4] Use subreg substitutions in LiveDebugValues

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 1 05:07:30 PDT 2021


Author: Jeremy Morse
Date: 2021-07-01T13:07:16+01:00
New Revision: e9641c911ef4127da1d98c4e4d37039989e6052b

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

LOG: [DebugInfo][InstrRef][2/4] Use subreg substitutions in LiveDebugValues

Added in 47c3fe2a22cf, we sometimes need to describe a variable value
substitution with a subregister qualifier, to say that "the value is the
lower 32 bits of this 64 bit register def" for example. That then needs
support during LiveDebugValues to interpret the subregister qualifiers,
which is what this patch adds.

Whenever we encounter a DBG_INSTR_REF and find its value by using a
substitution, collect any subregister qualifiers seen. Then, accumulate the
effects of the qualifiers to work out what offset and what size should be
extracted from the defined register. Finally, for the target ValueIDNum,
extract whatever subregister is in the correct position

Currently, describing a subregister field of a larger value that has been
spilt to the stack, is unimplemented.

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

Added: 
    llvm/test/DebugInfo/MIR/InstrRef/livedebugvalues_subreg_substitutions.mir

Modified: 
    llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
index b92614ee124d..b8fa02860ff1 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
@@ -1829,9 +1829,13 @@ bool InstrRefBasedLDV::transferDebugInstrRef(MachineInstr &MI,
   // recorded in the value substitution table. Apply any substitutions to
   // the instruction / operand number in this DBG_INSTR_REF.
   auto Sub = MF.DebugValueSubstitutions.find(std::make_pair(InstNo, OpNo));
+  // Collect any subregister extractions performed during optimization.
+  SmallVector<unsigned, 4> SeenSubregs;
   while (Sub != MF.DebugValueSubstitutions.end()) {
-    InstNo = Sub->second.Dest.first;
-    OpNo = Sub->second.Dest.second;
+    std::tie(InstNo, OpNo) = Sub->second.Dest;
+    unsigned Subreg = Sub->second.Subreg;
+    if (Subreg)
+      SeenSubregs.push_back(Subreg);
     Sub = MF.DebugValueSubstitutions.find(std::make_pair(InstNo, OpNo));
   }
 
@@ -1865,6 +1869,77 @@ bool InstrRefBasedLDV::transferDebugInstrRef(MachineInstr &MI,
                            MI, InstNo);
   }
 
+  // Apply any subregister extractions, in reverse. We might have seen code
+  // like this:
+  //    CALL64 @foo, implicit-def $rax
+  //    %0:gr64 = COPY $rax
+  //    %1:gr32 = COPY %0.sub_32bit
+  //    %2:gr16 = COPY %1.sub_16bit
+  //    %3:gr8  = COPY %2.sub_8bit
+  // In which case each copy would have been recorded as a substitution with
+  // a subregister qualifier. Apply those qualifiers now.
+  if (NewID && !SeenSubregs.empty()) {
+    unsigned Offset = 0;
+    unsigned Size = 0;
+
+    // Look at each subregister that we passed through, and progressively
+    // narrow in, accumulating any offsets that occur. Substitutions should
+    // only ever be the same or narrower width than what they read from;
+    // iterate in reverse order so that we go from wide to small.
+    for (unsigned Subreg : reverse(SeenSubregs)) {
+      unsigned ThisSize = TRI->getSubRegIdxSize(Subreg);
+      unsigned ThisOffset = TRI->getSubRegIdxOffset(Subreg);
+      Offset += ThisOffset;
+      Size = (Size == 0) ? ThisSize : std::min(Size, ThisSize);
+    }
+
+    // If that worked, look for an appropriate subregister with the register
+    // where the define happens. Don't look at values that were defined during
+    // a stack write: we can't currently express register locations within
+    // spills.
+    LocIdx L = NewID->getLoc();
+    if (NewID && !MTracker->isSpill(L)) {
+      // Find the register class for the register where this def happened.
+      // FIXME: no index for this?
+      Register Reg = MTracker->LocIdxToLocID[L];
+      const TargetRegisterClass *TRC = nullptr;
+      for (auto *TRCI : TRI->regclasses())
+        if (TRCI->contains(Reg))
+          TRC = TRCI;
+      assert(TRC && "Couldn't find target register class?");
+
+      // If the register we have isn't the right size or in the right place,
+      // Try to find a subregister inside it.
+      unsigned MainRegSize = TRI->getRegSizeInBits(*TRC);
+      if (Size != MainRegSize || Offset) {
+        // Enumerate all subregisters, searching.
+        Register NewReg = 0;
+        for (MCSubRegIterator SRI(Reg, TRI, false); SRI.isValid(); ++SRI) {
+          unsigned Subreg = TRI->getSubRegIndex(Reg, *SRI);
+          unsigned SubregSize = TRI->getSubRegIdxSize(Subreg);
+          unsigned SubregOffset = TRI->getSubRegIdxOffset(Subreg);
+          if (SubregSize == Size && SubregOffset == Offset) {
+            NewReg = *SRI;
+            break;
+          }
+        }
+
+        // If we didn't find anything: there's no way to express our value.
+        if (!NewReg) {
+          NewID = None;
+        } else {
+          // Re-state the value as being defined within the subregister
+          // that we found.
+          LocIdx NewLoc = MTracker->lookupOrTrackRegister(NewReg);
+          NewID = ValueIDNum(NewID->getBlock(), NewID->getInst(), NewLoc);
+        }
+      }
+    } else {
+      // If we can't handle subregisters, unset the new value.
+      NewID = None;
+    }
+  }
+
   // We, we have a value number or None. Tell the variable value tracker about
   // it. The rest of this LiveDebugValues implementation acts exactly the same
   // for DBG_INSTR_REFs as DBG_VALUEs (just, the former can refer to values that

diff  --git a/llvm/test/DebugInfo/MIR/InstrRef/livedebugvalues_subreg_substitutions.mir b/llvm/test/DebugInfo/MIR/InstrRef/livedebugvalues_subreg_substitutions.mir
new file mode 100644
index 000000000000..df160699402e
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/InstrRef/livedebugvalues_subreg_substitutions.mir
@@ -0,0 +1,106 @@
+# RUN: llc %s -march=x86-64 -run-pass=livedebugvalues -experimental-debug-variable-locations -o - 2>&1 | FileCheck %s
+#
+# Test that when we have a subregister qualifiers in substitutions, that
+# InstrRefBasedLDV correctly applies them to the variable location. Below, a
+# call defines all of $rax, but the variable locations should only apply to
+# the low order 8 bits.
+--- |
+  define i8 @test(i32 %bar) local_unnamed_addr !dbg !7 {
+  entry:
+    ret i8 0, !dbg !12
+  }
+
+  declare dso_local void @ext(i64)
+
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!3, !4, !5, !6}
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+  !1 = !DIFile(filename: "foo.cpp", directory: ".")
+  !2 = !DIBasicType(name: "int", size: 8, encoding: DW_ATE_signed)
+  !3 = !{i32 2, !"Dwarf Version", i32 4}
+  !4 = !{i32 2, !"Debug Info Version", i32 3}
+  !5 = !{i32 1, !"wchar_size", i32 2}
+  !6 = !{i32 7, !"PIC Level", i32 2}
+  !7 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: !1, file: !1, line: 6, type: !8, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10)
+  !8 = !DISubroutineType(types: !9)
+  !9 = !{!2, !2}
+  !10 = !{!11}
+  !11 = !DILocalVariable(name: "baz", scope: !7, file: !1, line: 7, type: !2)
+  !12 = !DILocation(line: 10, scope: !7)
+...
+---
+name: test
+tracksRegLiveness: true
+liveins:
+  - { reg: '$rdi', virtual-reg: '' }
+debugValueSubstitutions:
+  - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 0, subreg: 1 } # sub_8bit
+  - { srcinst: 2, srcop: 0, dstinst: 3, dstop: 0, subreg: 4 } # sub_16bit
+  - { srcinst: 3, srcop: 0, dstinst: 4, dstop: 5, subreg: 6 } # sub_32bit
+  # Substitution involving sub_8bit_hi, should land in $ah
+  - { srcinst: 5, srcop: 0, dstinst: 6, dstop: 0, subreg: 2 } # sub_8bit_hi
+  - { srcinst: 6, srcop: 0, dstinst: 7, dstop: 0, subreg: 4 } # sub_16bit
+  - { srcinst: 7, srcop: 0, dstinst: 4, dstop: 5, subreg: 6 } # sub_32bit
+  # Several redundant substitutions, representing extractions from a small
+  # register, followed by larger spurious ones, for example:
+  # %0:gr64 = COPY $rax
+  # %1:gr32 = COPY %0.sub_32bit
+  # %2:gr16 = COPY %1.sub_16bit
+  # %3:gr64 = SUBREG_TO_REG %2, sub_8bit_hi
+  # %4:gr32 = COPY %3.sub_32bit
+  # %5:gr16 = COPY %2.sub_16bit
+  # Should still come out as ah.
+  - { srcinst: 8, srcop: 0, dstinst: 9, dstop: 0, subreg: 4 } # sub_16bit
+  - { srcinst: 9, srcop: 0, dstinst: 10,dstop: 0, subreg: 6 } # sub_32bit
+  - { srcinst: 10,srcop: 0, dstinst: 11,dstop: 0, subreg: 2 } # sub_8bit_hi
+  - { srcinst: 11,srcop: 0, dstinst: 12,dstop: 0, subreg: 4 } # sub_16bit
+  - { srcinst: 12,srcop: 0, dstinst: 4, dstop: 5, subreg: 6 } # sub_32bit
+  # If some kind of really mal-formed code appears that extracts the high bits
+  # out of a too-small location, we should drop it. It's not clear whether this
+  # scenario could ever happen; but if it did, best to not emit a known bad
+  # variable location. Should generate a DBG_VALUE $noreg.
+  - { srcinst: 13, srcop: 0, dstinst: 14,dstop: 0, subreg: 5 } # sub_16bit_hi
+  - { srcinst: 14, srcop: 0, dstinst: 15,dstop: 0, subreg: 6 } # sub_32bit
+  - { srcinst: 15, srcop: 0, dstinst: 4, dstop: 5, subreg: 1 } # sub_8bit
+stack:
+  - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+body:  |
+  bb.0:
+  liveins: $rdi, $rax
+    CALL64pcrel32 @ext, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rax, debug-instr-number 4, debug-location !12
+    ; CHECK:      CALL64pcrel32
+    DBG_INSTR_REF 1, 0, !11, !DIExpression(), debug-location !12
+    ; CHECK-NEXT: DBG_INSTR_REF 1, 0
+    ; CHECK-NEXT: DBG_VALUE $al
+    DBG_INSTR_REF 5, 0, !11, !DIExpression(), debug-location !12
+    ; CHECK-NEXT: DBG_INSTR_REF 5, 0
+    ; CHECK-NEXT: DBG_VALUE $ah
+    DBG_INSTR_REF 8, 0, !11, !DIExpression(), debug-location !12
+    ; CHECK-NEXT: DBG_INSTR_REF 8, 0
+    ; CHECK-NEXT: DBG_VALUE $ah
+    DBG_INSTR_REF 13, 0, !11, !DIExpression(), debug-location !12
+    ; CHECK-NEXT: DBG_INSTR_REF 13, 0
+    ; CHECK-NEXT: DBG_VALUE $noreg
+    MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax :: (store 8 into %stack.0)
+    $rax = MOV64ri 0, debug-location !12
+    ; CHECK:      $rax = MOV64ri 0
+    ; The value is now located in a spill slot; currently InstrRefBasedLDV
+    ; can't express subregister locations inside spills. These should all
+    ; end up being $noreg, but could be improved in the future.
+    DBG_INSTR_REF 1, 0, !11, !DIExpression(), debug-location !12
+    ; CHECK-NEXT: DBG_INSTR_REF 1, 0
+    ; CHECK-NEXT: DBG_VALUE $noreg
+    DBG_INSTR_REF 5, 0, !11, !DIExpression(), debug-location !12
+    ; CHECK-NEXT: DBG_INSTR_REF 5, 0
+    ; CHECK-NEXT: DBG_VALUE $noreg
+    DBG_INSTR_REF 8, 0, !11, !DIExpression(), debug-location !12
+    ; CHECK-NEXT: DBG_INSTR_REF 8, 0
+    ; CHECK-NEXT: DBG_VALUE $noreg
+    DBG_INSTR_REF 13, 0, !11, !DIExpression(), debug-location !12
+    ; CHECK-NEXT: DBG_INSTR_REF 13, 0
+    ; CHECK-NEXT: DBG_VALUE $noreg
+    $rax = MOV64rm $rsp, 1, $noreg, 8, $noreg :: (load 8 from %stack.0)
+    RETQ $rax, debug-location !12
+...


        


More information about the llvm-commits mailing list