[llvm] r371193 - [DebugInfo] LiveDebugValues: explicitly terminate overwritten stack locations

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 6 03:08:23 PDT 2019


Author: jmorse
Date: Fri Sep  6 03:08:22 2019
New Revision: 371193

URL: http://llvm.org/viewvc/llvm-project?rev=371193&view=rev
Log:
[DebugInfo] LiveDebugValues: explicitly terminate overwritten stack locations

If a stack spill location is overwritten by another spill instruction,
any variable locations pointing at that slot should be terminated. We
cannot rely on spills always being restored to registers or variable
locations being moved by a DBG_VALUE: the register allocator is entitled
to spill a value and then forget about it when it goes out of liveness.

To address this, scan for memory writes to spill locations, even those we
don't consider to be normal "spills". isSpillInstruction and
isLocationSpill distinguish the two now. After identifying spill
overwrites, terminate the open range, and insert a $noreg DBG_VALUE for
that variable.

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

Added:
    llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-stack-clobber.mir
Modified:
    llvm/trunk/lib/CodeGen/LiveDebugValues.cpp

Modified: llvm/trunk/lib/CodeGen/LiveDebugValues.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/LiveDebugValues.cpp?rev=371193&r1=371192&r2=371193&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/LiveDebugValues.cpp (original)
+++ llvm/trunk/lib/CodeGen/LiveDebugValues.cpp Fri Sep  6 03:08:22 2019
@@ -361,8 +361,18 @@ private:
     }
   };
 
-  bool isSpillInstruction(const MachineInstr &MI, MachineFunction *MF,
-                          unsigned &Reg);
+  /// Tests whether this instruction is a spill to a stack location.
+  bool isSpillInstruction(const MachineInstr &MI, MachineFunction *MF);
+
+  /// Decide if @MI is a spill instruction and return true if it is. We use 2
+  /// criteria to make this decision:
+  /// - Is this instruction a store to a spill slot?
+  /// - Is there a register operand that is both used and killed?
+  /// TODO: Store optimization can fold spills into other stores (including
+  /// other spills). We do not handle this yet (more than one memory operand).
+  bool isLocationSpill(const MachineInstr &MI, MachineFunction *MF,
+                       unsigned &Reg);
+
   /// If a given instruction is identified as a spill, return the spill location
   /// and set \p Reg to the spilled register.
   Optional<VarLoc::SpillLoc> isRestoreInstruction(const MachineInstr &MI,
@@ -779,16 +789,8 @@ void LiveDebugValues::transferRegisterDe
   }
 }
 
-/// Decide if @MI is a spill instruction and return true if it is. We use 2
-/// criteria to make this decision:
-/// - Is this instruction a store to a spill slot?
-/// - Is there a register operand that is both used and killed?
-/// TODO: Store optimization can fold spills into other stores (including
-/// other spills). We do not handle this yet (more than one memory operand).
 bool LiveDebugValues::isSpillInstruction(const MachineInstr &MI,
-                                         MachineFunction *MF, unsigned &Reg) {
-  SmallVector<const MachineMemOperand*, 1> Accesses;
-
+                                         MachineFunction *MF) {
   // TODO: Handle multiple stores folded into one.
   if (!MI.hasOneMemOperand())
     return false;
@@ -797,6 +799,14 @@ bool LiveDebugValues::isSpillInstruction
     return false; // This is not a spill instruction, since no valid size was
                   // returned from either function.
 
+  return true;
+}
+
+bool LiveDebugValues::isLocationSpill(const MachineInstr &MI,
+                                      MachineFunction *MF, unsigned &Reg) {
+  if (!isSpillInstruction(MI, MF))
+    return false;
+
   auto isKilledReg = [&](const MachineOperand MO, unsigned &Reg) {
     if (!MO.isReg() || !MO.isUse()) {
       Reg = 0;
@@ -865,7 +875,39 @@ void LiveDebugValues::transferSpillOrRes
 
   LLVM_DEBUG(dbgs() << "Examining instruction: "; MI.dump(););
 
-  if (isSpillInstruction(MI, MF, Reg)) {
+  // First, if there are any DBG_VALUEs pointing at a spill slot that is
+  // written to, then close the variable location. The value in memory
+  // will have changed.
+  VarLocSet KillSet;
+  if (isSpillInstruction(MI, MF)) {
+    Loc = extractSpillBaseRegAndOffset(MI);
+    for (unsigned ID : OpenRanges.getVarLocs()) {
+      const VarLoc &VL = VarLocIDs[ID];
+      if (VL.Kind == VarLoc::SpillLocKind && VL.Loc.SpillLocation == *Loc) {
+        // This location is overwritten by the current instruction -- terminate
+        // the open range, and insert an explicit DBG_VALUE $noreg.
+        //
+        // Doing this at a later stage would require re-interpreting all
+        // DBG_VALUes and DIExpressions to identify whether they point at
+        // memory, and then analysing all memory writes to see if they
+        // overwrite that memory, which is expensive.
+        //
+        // At this stage, we already know which DBG_VALUEs are for spills and
+        // where they are located; it's best to fix handle overwrites now.
+        KillSet.set(ID);
+        MachineInstr *NewDebugInstr =
+            BuildMI(*MF, VL.MI.getDebugLoc(), VL.MI.getDesc(),
+                    VL.MI.isIndirectDebugValue(), 0, // $noreg
+                    VL.MI.getDebugVariable(), VL.MI.getDebugExpression());
+        Transfers.push_back({&MI, NewDebugInstr});
+      }
+    }
+    OpenRanges.erase(KillSet, VarLocIDs);
+  }
+
+  // Try to recognise spill and restore instructions that may create a new
+  // variable location.
+  if (isLocationSpill(MI, MF, Reg)) {
     TKind = TransferKind::TransferSpill;
     LLVM_DEBUG(dbgs() << "Recognized as spill: "; MI.dump(););
     LLVM_DEBUG(dbgs() << "Register: " << Reg << " " << printReg(Reg, TRI)

Added: llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-stack-clobber.mir
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-stack-clobber.mir?rev=371193&view=auto
==============================================================================
--- llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-stack-clobber.mir (added)
+++ llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-stack-clobber.mir Fri Sep  6 03:08:22 2019
@@ -0,0 +1,200 @@
+# RUN: llc -mtriple=x86_64-unknown-unknown %s -o - -run-pass=livedebugvalues | FileCheck %s
+#
+# Fix some of PR42772. Consider the code below: the arguments are forced onto
+# the stack by the FORCE_SPILL macro, and go out of liveness if the
+# "bees == 2" conditional is not taken. A spill slot is then re-used to
+# preserve quux over the second FORCE_SPILL, over-writing the value of either
+# "a" or "b". LiveDebugValues should detect when this happens, and terminate
+# stack-spill locations when they get overwritten by a new value.
+#
+# --------8<--------
+# #define FORCE_SPILL() \
+#   __asm volatile("" : : : \
+#                    "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "r8", \
+#                    "r9", "r10", "r11", "r12", "r13", "r14", "r15")
+# 
+# volatile int bees = 0;
+# 
+# long int f(long int a, long int b) {
+#   if (bees == 12)
+#    return 3;
+# 
+#   FORCE_SPILL();
+#   if (bees == 2)
+#     return a - b;
+# 
+#   int quux = sum(1, 2);
+#   FORCE_SPILL();
+#   bees++;
+#   return quux;
+# }
+# -------->8--------
+#
+# CHECK:       ![[ANUM:[0-9]+]] = !DILocalVariable(name: "a"
+# CHECK:       ![[BNUM:[0-9]+]] = !DILocalVariable(name: "b"
+#
+# These variables should be spilt,
+# CHECK-LABEL: bb.1.if.end:
+# CHECK:       MOV64mr $rsp, 1, $noreg, 16, $noreg, killed renamable $rsi
+# CHECK-NEXT:  DBG_VALUE $rsp, 0, ![[BNUM]], !DIExpression(
+# CHECK-NEXT:  MOV64mr $rsp, 1, $noreg, 8, $noreg, killed renamable $rdi
+# CHECK-NEXT:  DBG_VALUE $rsp, 0, ![[ANUM]], !DIExpression(
+# CHECK-NEXT:  INLINEASM
+#
+# Then the location of "a" should be terminated when overwritten
+# CHECK-LABEL: bb.3.if.end3:
+# CHECK:       CALL64pcrel32 @sum
+# CHECK-NEXT:  MOV64mr $rsp, 1, $noreg, 8, $noreg, $rax
+# CHECK-NEXT:  DBG_VALUE $noreg, $noreg, ![[ANUM]], !DIExpression()
+# CHECK-NEXT:  INLINEASM
+
+--- |
+  target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+  target triple = "x86_64-unknown-linux-gnu"
+  
+  @bees = external global i32, !dbg !0
+  
+  ; Function Attrs: noinline norecurse nounwind readnone uwtable
+  declare i64 @sum(i64, i64)
+  
+  ; Function Attrs: noinline nounwind uwtable
+  define i64 @f(i64 %a, i64 %b) !dbg !12 {
+  entry:
+    br label %if.end
+  if.end:
+    br label %if.then2
+  if.then2:
+    br label %if.end3
+  if.end3:
+    br label %return
+  return:
+    ret i64 0
+  }
+  
+  !llvm.dbg.cu = !{!2}
+  !llvm.module.flags = !{!8, !9, !10}
+  
+  !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+  !1 = distinct !DIGlobalVariable(name: "bees", scope: !2, file: !3, line: 6, type: !6, isLocal: false, isDefinition: true)
+  !2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None)
+  !3 = !DIFile(filename: "pr42772.c", directory: ".")
+  !4 = !{}
+  !5 = !{!0}
+  !6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7)
+  !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !8 = !{i32 2, !"Dwarf Version", i32 4}
+  !9 = !{i32 2, !"Debug Info Version", i32 3}
+  !10 = !{i32 1, !"wchar_size", i32 4}
+  !12 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 15, type: !13, scopeLine: 15, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !16)
+  !13 = !DISubroutineType(types: !14)
+  !14 = !{!15, !15, !15}
+  !15 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed)
+  !16 = !{!17, !18, !19}
+  !17 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !3, line: 15, type: !15)
+  !18 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !3, line: 15, type: !15)
+  !19 = !DILocalVariable(name: "quux", scope: !12, file: !3, line: 23, type: !7)
+  !28 = !DILocation(line: 1, column: 1, scope: !12)
+
+...
+---
+name:            f
+tracksRegLiveness: true
+liveins:
+  - { reg: '$rdi', virtual-reg: '' }
+  - { reg: '$rsi', virtual-reg: '' }
+frameInfo:
+  stackSize:       72
+  offsetAdjustment: -72
+  maxAlignment:    8
+  adjustsStack:    true
+  hasCalls:        true
+  cvBytesOfCalleeSavedRegisters: 48
+fixedStack:
+  - { id: 0, type: spill-slot, offset: -56, size: 8, alignment: 8, stack-id: default, 
+      callee-saved-register: '$rbx', callee-saved-restored: true, debug-info-variable: '', 
+      debug-info-expression: '', debug-info-location: '' }
+  - { id: 1, type: spill-slot, offset: -48, size: 8, alignment: 16, stack-id: default, 
+      callee-saved-register: '$r12', callee-saved-restored: true, debug-info-variable: '', 
+      debug-info-expression: '', debug-info-location: '' }
+  - { id: 2, type: spill-slot, offset: -40, size: 8, alignment: 8, stack-id: default, 
+      callee-saved-register: '$r13', callee-saved-restored: true, debug-info-variable: '', 
+      debug-info-expression: '', debug-info-location: '' }
+  - { id: 3, type: spill-slot, offset: -32, size: 8, alignment: 16, stack-id: default, 
+      callee-saved-register: '$r14', callee-saved-restored: true, debug-info-variable: '', 
+      debug-info-expression: '', debug-info-location: '' }
+  - { id: 4, type: spill-slot, offset: -24, size: 8, alignment: 8, stack-id: default, 
+      callee-saved-register: '$r15', callee-saved-restored: true, debug-info-variable: '', 
+      debug-info-expression: '', debug-info-location: '' }
+  - { id: 5, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default, 
+      callee-saved-register: '$rbp', callee-saved-restored: true, debug-info-variable: '', 
+      debug-info-expression: '', debug-info-location: '' }
+stack:
+  - { id: 0, name: '', type: spill-slot, offset: -72, size: 8, alignment: 8, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 1, name: '', type: spill-slot, offset: -64, 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.entry:
+    successors: %bb.4, %bb.1
+    liveins: $rdi, $rsi, $rbp, $r15, $r14, $r13, $r12, $rbx
+  
+    DBG_VALUE $rdi, $noreg, !17, !DIExpression(), debug-location !28
+    DBG_VALUE $rdi, $noreg, !17, !DIExpression(), debug-location !28
+    DBG_VALUE $rsi, $noreg, !18, !DIExpression(), debug-location !28
+    DBG_VALUE $rsi, $noreg, !18, !DIExpression(), debug-location !28
+    frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp, debug-location !28
+    frame-setup PUSH64r killed $r15, implicit-def $rsp, implicit $rsp, debug-location !28
+    frame-setup PUSH64r killed $r14, implicit-def $rsp, implicit $rsp, debug-location !28
+    frame-setup PUSH64r killed $r13, implicit-def $rsp, implicit $rsp, debug-location !28
+    frame-setup PUSH64r killed $r12, implicit-def $rsp, implicit $rsp, debug-location !28
+    frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp, debug-location !28
+    $rsp = frame-setup SUB64ri8 $rsp, 24, implicit-def dead $eflags
+    renamable $ecx = MOV32rm $rip, 1, $noreg, @bees, $noreg, debug-location !28 :: (volatile dereferenceable load 4 from @bees)
+    $eax = MOV32ri 3, implicit-def $rax
+    CMP32ri8 killed renamable $ecx, 12, implicit-def $eflags, debug-location !28
+    JCC_1 %bb.4, 4, implicit $eflags, debug-location !28
+  
+  bb.1.if.end:
+    successors: %bb.2, %bb.3
+    liveins: $rdi, $rsi
+  
+    MOV64mr $rsp, 1, $noreg, 16, $noreg, killed renamable $rsi :: (store 8 into %stack.1)
+    MOV64mr $rsp, 1, $noreg, 8, $noreg, killed renamable $rdi :: (store 8 into %stack.0)
+    INLINEASM &"", 1, 12, implicit-def dead early-clobber $rax, 12, implicit-def dead early-clobber $rbx, 12, implicit-def dead early-clobber $rcx, 12, implicit-def dead early-clobber $rdx, 12, implicit-def dead early-clobber $rsi, 12, implicit-def dead early-clobber $rdi, 12, implicit-def dead early-clobber $rbp, 12, implicit-def dead early-clobber $r8, 12, implicit-def dead early-clobber $r9, 12, implicit-def dead early-clobber $r10, 12, implicit-def dead early-clobber $r11, 12, implicit-def dead early-clobber $r12, 12, implicit-def dead early-clobber $r13, 12, implicit-def dead early-clobber $r14, 12, implicit-def dead early-clobber $r15, 12, implicit-def dead early-clobber $df, 12, implicit-def dead early-clobber $fpsw, 12, implicit-def dead early-clobber $eflags, debug-location !28
+    renamable $eax = MOV32rm $rip, 1, $noreg, @bees, $noreg, debug-location !28 :: (volatile dereferenceable load 4 from @bees)
+    CMP32ri8 killed renamable $eax, 2, implicit-def $eflags, debug-location !28
+    JCC_1 %bb.3, 5, implicit killed $eflags, debug-location !28
+  
+  bb.2.if.then2:
+    successors: %bb.4
+  
+    renamable $rax = MOV64rm $rsp, 1, $noreg, 8, $noreg :: (load 8 from %stack.0)
+    renamable $rax = SUB64rm killed renamable $rax, $rsp, 1, $noreg, 16, $noreg, implicit-def dead $eflags, debug-location !28 :: (load 8 from %stack.1)
+    JMP_1 %bb.4
+  
+  bb.3.if.end3:
+    successors: %bb.4
+  
+    $edi = MOV32ri 1, implicit-def $rdi, debug-location !28
+    $esi = MOV32ri 2, implicit-def $rsi, debug-location !28
+    CALL64pcrel32 @sum, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax, debug-location !28
+    MOV64mr $rsp, 1, $noreg, 8, $noreg, $rax :: (store 8 into %stack.0)
+    INLINEASM &"", 1, 12, implicit-def dead early-clobber $rax, 12, implicit-def dead early-clobber $rbx, 12, implicit-def dead early-clobber $rcx, 12, implicit-def dead early-clobber $rdx, 12, implicit-def dead early-clobber $rsi, 12, implicit-def dead early-clobber $rdi, 12, implicit-def dead early-clobber $rbp, 12, implicit-def dead early-clobber $r8, 12, implicit-def dead early-clobber $r9, 12, implicit-def dead early-clobber $r10, 12, implicit-def dead early-clobber $r11, 12, implicit-def dead early-clobber $r12, 12, implicit-def dead early-clobber $r13, 12, implicit-def dead early-clobber $r14, 12, implicit-def dead early-clobber $r15, 12, implicit-def dead early-clobber $df, 12, implicit-def dead early-clobber $fpsw, 12, implicit-def dead early-clobber $eflags, debug-location !28
+    ADD32mi8 $rip, 1, $noreg, @bees, $noreg, 1, implicit-def dead $eflags, debug-location !28 :: (volatile store 4 into @bees), (volatile dereferenceable load 4 from @bees)
+    renamable $rax = MOVSX64rm32 $rsp, 1, $noreg, 8, $noreg, debug-location !28 :: (load 4 from %stack.0, align 8)
+  
+  bb.4.return:
+    liveins: $rax
+  
+    $rsp = frame-destroy ADD64ri8 $rsp, 24, implicit-def dead $eflags, debug-location !28
+    $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28
+    $r12 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28
+    $r13 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28
+    $r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28
+    $r15 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28
+    $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28
+    RETQ $rax, debug-location !28
+
+...




More information about the llvm-commits mailing list