[llvm] r363256 - [DebugInfo] Honour variable fragments in LiveDebugValues

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 13 05:51:57 PDT 2019


Author: jmorse
Date: Thu Jun 13 05:51:57 2019
New Revision: 363256

URL: http://llvm.org/viewvc/llvm-project?rev=363256&view=rev
Log:
[DebugInfo] Honour variable fragments in LiveDebugValues

This patch makes the LiveDebugValues pass consider fragments when propagating
DBG_VALUE insts between blocks, fixing PR41979. Fragment info for a variable
location is added to the open-ranges key, which allows distinct fragments to be
tracked separately. To handle overlapping fragments things become slightly
funkier. To avoid excessive searching for overlaps in the data-flow part of
LiveDebugValues, this patch:
 * Pre-computes pairings of fragments that overlap, for each DILocalVariable
 * During data-flow, whenever something happens that causes an open range to
   be terminated (via erase), any fragments pre-determined to overlap are
   also terminated.

The effect of which is that when encountering a DBG_VALUE fragment that
overlaps others, the overlapped fragments do not get propagated to other
blocks. We still rely on later location-list building to correctly handle
overlapping fragments within blocks.

It's unclear whether a mixture of DBG_VALUEs with and without fragmented
expressions are legitimate. To avoid suprises, this patch interprets a
DBG_VALUE with no fragment as overlapping any DBG_VALUE _with_ a fragment.

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

Added:
    llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-fragments.mir
Modified:
    llvm/trunk/include/llvm/IR/DebugInfoMetadata.h
    llvm/trunk/lib/CodeGen/LiveDebugValues.cpp
    llvm/trunk/test/DebugInfo/ARM/partial-subreg.ll

Modified: llvm/trunk/include/llvm/IR/DebugInfoMetadata.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/DebugInfoMetadata.h?rev=363256&r1=363255&r2=363256&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/DebugInfoMetadata.h (original)
+++ llvm/trunk/include/llvm/IR/DebugInfoMetadata.h Thu Jun 13 05:51:57 2019
@@ -2523,17 +2523,14 @@ public:
   createFragmentExpression(const DIExpression *Expr, unsigned OffsetInBits,
                            unsigned SizeInBits);
 
-  /// Determine the relative position of the fragments described by this
-  /// DIExpression and \p Other.
+  /// Determine the relative position of the fragments passed in.
   /// Returns -1 if this is entirely before Other, 0 if this and Other overlap,
   /// 1 if this is entirely after Other.
-  int fragmentCmp(const DIExpression *Other) const {
-    auto Fragment1 = *getFragmentInfo();
-    auto Fragment2 = *Other->getFragmentInfo();
-    unsigned l1 = Fragment1.OffsetInBits;
-    unsigned l2 = Fragment2.OffsetInBits;
-    unsigned r1 = l1 + Fragment1.SizeInBits;
-    unsigned r2 = l2 + Fragment2.SizeInBits;
+  static int fragmentCmp(const FragmentInfo &A, const FragmentInfo &B) {
+    uint64_t l1 = A.OffsetInBits;
+    uint64_t l2 = B.OffsetInBits;
+    uint64_t r1 = l1 + A.SizeInBits;
+    uint64_t r2 = l2 + B.SizeInBits;
     if (r1 <= l2)
       return -1;
     else if (r2 <= l1)
@@ -2542,6 +2539,19 @@ public:
       return 0;
   }
 
+  /// Check if fragments overlap between a pair of FragmentInfos.
+  static bool fragmentsOverlap(const FragmentInfo &A, const FragmentInfo &B) {
+    return fragmentCmp(A, B) == 0;
+  }
+
+  /// Determine the relative position of the fragments described by this
+  /// DIExpression and \p Other. Calls static fragmentCmp implementation.
+  int fragmentCmp(const DIExpression *Other) const {
+    auto Fragment1 = *getFragmentInfo();
+    auto Fragment2 = *Other->getFragmentInfo();
+    return fragmentCmp(Fragment1, Fragment2);
+  }
+
   /// Check if fragments overlap between this DIExpression and \p Other.
   bool fragmentsOverlap(const DIExpression *Other) const {
     if (!isFragment() || !Other->isFragment())
@@ -2550,6 +2560,33 @@ public:
   }
 };
 
+inline bool operator==(const DIExpression::FragmentInfo &A,
+                       const struct DIExpression::FragmentInfo &B) {
+  return std::tie(A.SizeInBits, A.OffsetInBits) ==
+         std::tie(B.SizeInBits, B.OffsetInBits);
+}
+
+inline bool operator<(const struct DIExpression::FragmentInfo &A,
+                      const struct DIExpression::FragmentInfo &B) {
+  return std::tie(A.SizeInBits, A.OffsetInBits) <
+         std::tie(B.SizeInBits, B.OffsetInBits);
+}
+
+template <> struct DenseMapInfo<struct DIExpression::FragmentInfo> {
+  using FragInfo = struct DIExpression::FragmentInfo;
+  static const uint64_t MaxVal = std::numeric_limits<uint64_t>::max();
+
+  static inline FragInfo getEmptyKey() { return {MaxVal, MaxVal}; }
+
+  static inline FragInfo getTombstoneKey() { return {MaxVal - 1, MaxVal - 1}; }
+
+  static unsigned getHashValue(const FragInfo &Frag) {
+    return (Frag.SizeInBits & 0xffff) << 16 | (Frag.OffsetInBits & 0xffff);
+  }
+
+  static bool isEqual(const FragInfo &A, const FragInfo &B) { return A == B; }
+};
+
 /// Global variables.
 ///
 /// TODO: Remove DisplayName.  It's always equal to Name.

Modified: llvm/trunk/lib/CodeGen/LiveDebugValues.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/LiveDebugValues.cpp?rev=363256&r1=363255&r2=363256&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/LiveDebugValues.cpp (original)
+++ llvm/trunk/lib/CodeGen/LiveDebugValues.cpp Thu Jun 13 05:51:57 2019
@@ -20,6 +20,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/SparseBitVector.h"
 #include "llvm/ADT/Statistic.h"
@@ -57,6 +58,7 @@
 #include <cstdint>
 #include <functional>
 #include <queue>
+#include <tuple>
 #include <utility>
 #include <vector>
 
@@ -107,24 +109,63 @@ private:
     }
   };
 
-  /// Based on std::pair so it can be used as an index into a DenseMap.
-  using DebugVariableBase =
-      std::pair<const DILocalVariable *, const DILocation *>;
-  /// A potentially inlined instance of a variable.
-  struct DebugVariable : public DebugVariableBase {
-    DebugVariable(const DILocalVariable *Var, const DILocation *InlinedAt)
-        : DebugVariableBase(Var, InlinedAt) {}
-
-    const DILocalVariable *getVar() const { return this->first; }
-    const DILocation *getInlinedAt() const { return this->second; }
-
-    bool operator<(const DebugVariable &DV) const {
-      if (getVar() == DV.getVar())
-        return getInlinedAt() < DV.getInlinedAt();
-      return getVar() < DV.getVar();
+  using FragmentInfo = DIExpression::FragmentInfo;
+  using OptFragmentInfo = Optional<DIExpression::FragmentInfo>;
+
+  /// Storage for identifying a potentially inlined instance of a variable,
+  /// or a fragment thereof.
+  class DebugVariable {
+    const DILocalVariable *Variable;
+    OptFragmentInfo Fragment;
+    const DILocation *InlinedAt;
+
+    /// Fragment that will overlap all other fragments. Used as default when
+    /// caller demands a fragment.
+    static const FragmentInfo DefaultFragment;
+
+  public:
+    DebugVariable(const DILocalVariable *Var, OptFragmentInfo &&FragmentInfo,
+                  const DILocation *InlinedAt)
+        : Variable(Var), Fragment(FragmentInfo), InlinedAt(InlinedAt) {}
+
+    DebugVariable(const DILocalVariable *Var, OptFragmentInfo &FragmentInfo,
+                  const DILocation *InlinedAt)
+        : Variable(Var), Fragment(FragmentInfo), InlinedAt(InlinedAt) {}
+
+    DebugVariable(const DILocalVariable *Var, const DIExpression *DIExpr,
+                  const DILocation *InlinedAt)
+        : DebugVariable(Var, DIExpr->getFragmentInfo(), InlinedAt) {}
+
+    DebugVariable(const MachineInstr &MI)
+        : DebugVariable(MI.getDebugVariable(),
+                        MI.getDebugExpression()->getFragmentInfo(),
+                        MI.getDebugLoc()->getInlinedAt()) {}
+
+    const DILocalVariable *getVar() const { return Variable; }
+    const OptFragmentInfo &getFragment() const { return Fragment; }
+    const DILocation *getInlinedAt() const { return InlinedAt; }
+
+    const FragmentInfo getFragmentDefault() const {
+      return Fragment.getValueOr(DefaultFragment);
+    }
+
+    static bool isFragmentDefault(FragmentInfo &F) {
+      return F == DefaultFragment;
+    }
+
+    bool operator==(const DebugVariable &Other) const {
+      return std::tie(Variable, Fragment, InlinedAt) ==
+             std::tie(Other.Variable, Other.Fragment, Other.InlinedAt);
+    }
+
+    bool operator<(const DebugVariable &Other) const {
+      return std::tie(Variable, Fragment, InlinedAt) <
+             std::tie(Other.Variable, Other.Fragment, Other.InlinedAt);
     }
   };
 
+  friend struct llvm::DenseMapInfo<DebugVariable>;
+
   /// A pair of debug variable and value location.
   struct VarLoc {
     // The location at which a spilled variable resides. It consists of a
@@ -159,8 +200,7 @@ private:
     } Loc;
 
     VarLoc(const MachineInstr &MI, LexicalScopes &LS)
-        : Var(MI.getDebugVariable(), MI.getDebugLoc()->getInlinedAt()), MI(MI),
-          UVS(MI.getDebugLoc(), LS) {
+        : Var(MI), MI(MI), UVS(MI.getDebugLoc(), LS) {
       static_assert((sizeof(Loc) == sizeof(uint64_t)),
                     "hash does not cover all members of Loc");
       assert(MI.isDebugValue() && "not a DBG_VALUE");
@@ -183,8 +223,7 @@ private:
     /// The constructor for spill locations.
     VarLoc(const MachineInstr &MI, unsigned SpillBase, int SpillOffset,
            LexicalScopes &LS)
-        : Var(MI.getDebugVariable(), MI.getDebugLoc()->getInlinedAt()), MI(MI),
-          UVS(MI.getDebugLoc(), LS) {
+        : Var(MI), MI(MI), UVS(MI.getDebugLoc(), LS) {
       assert(MI.isDebugValue() && "not a DBG_VALUE");
       assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE");
       Kind = SpillLocKind;
@@ -232,26 +271,35 @@ private:
   };
   using TransferMap = SmallVector<TransferDebugPair, 4>;
 
+  // Types for recording sets of variable fragments that overlap. For a given
+  // local variable, we record all other fragments of that variable that could
+  // overlap it, to reduce search time.
+  using FragmentOfVar =
+      std::pair<const DILocalVariable *, DIExpression::FragmentInfo>;
+  using OverlapMap =
+      DenseMap<FragmentOfVar, SmallVector<DIExpression::FragmentInfo, 1>>;
+
+  // Helper while building OverlapMap, a map of all fragments seen for a given
+  // DILocalVariable.
+  using VarToFragments =
+      DenseMap<const DILocalVariable *, SmallSet<FragmentInfo, 4>>;
+
   /// This holds the working set of currently open ranges. For fast
   /// access, this is done both as a set of VarLocIDs, and a map of
   /// DebugVariable to recent VarLocID. Note that a DBG_VALUE ends all
   /// previous open ranges for the same variable.
   class OpenRangesSet {
     VarLocSet VarLocs;
-    SmallDenseMap<DebugVariableBase, unsigned, 8> Vars;
+    SmallDenseMap<DebugVariable, unsigned, 8> Vars;
+    OverlapMap &OverlappingFragments;
 
   public:
+    OpenRangesSet(OverlapMap &_OLapMap) : OverlappingFragments(_OLapMap) {}
+
     const VarLocSet &getVarLocs() const { return VarLocs; }
 
     /// Terminate all open ranges for Var by removing it from the set.
-    void erase(DebugVariable Var) {
-      auto It = Vars.find(Var);
-      if (It != Vars.end()) {
-        unsigned ID = It->second;
-        VarLocs.reset(ID);
-        Vars.erase(It);
-      }
-    }
+    void erase(DebugVariable Var);
 
     /// Terminate all open ranges listed in \c KillSet by removing
     /// them from the set.
@@ -262,7 +310,7 @@ private:
     }
 
     /// Insert a new range into the set.
-    void insert(unsigned VarLocID, DebugVariableBase Var) {
+    void insert(unsigned VarLocID, DebugVariable Var) {
       VarLocs.set(VarLocID);
       Vars.insert({Var, VarLocID});
     }
@@ -310,6 +358,9 @@ private:
                VarLocInMBB &OutLocs, VarLocMap &VarLocIDs,
                TransferMap &Transfers, bool transferChanges);
 
+  void accumulateFragmentMap(MachineInstr &MI, VarToFragments &SeenFragments,
+                             OverlapMap &OLapMap);
+
   bool join(MachineBasicBlock &MBB, VarLocInMBB &OutLocs, VarLocInMBB &InLocs,
             const VarLocMap &VarLocIDs,
             SmallPtrSet<const MachineBasicBlock *, 16> &Visited,
@@ -343,10 +394,46 @@ public:
 
 } // end anonymous namespace
 
+namespace llvm {
+
+template <> struct DenseMapInfo<LiveDebugValues::DebugVariable> {
+  using DV = LiveDebugValues::DebugVariable;
+  using OptFragmentInfo = LiveDebugValues::OptFragmentInfo;
+  using FragmentInfo = LiveDebugValues::FragmentInfo;
+
+  // Empty key: no key should be generated that has no DILocalVariable.
+  static inline DV getEmptyKey() {
+    return DV(nullptr, OptFragmentInfo(), nullptr);
+  }
+
+  // Difference in tombstone is that the Optional is meaningful
+  static inline DV getTombstoneKey() {
+    return DV(nullptr, OptFragmentInfo({0, 0}), nullptr);
+  }
+
+  static unsigned getHashValue(const DV &D) {
+    unsigned HV = 0;
+    const OptFragmentInfo &Fragment = D.getFragment();
+    if (Fragment)
+      HV = DenseMapInfo<FragmentInfo>::getHashValue(*Fragment);
+
+    return hash_combine(D.getVar(), HV, D.getInlinedAt());
+  }
+
+  static bool isEqual(const DV &A, const DV &B) { return A == B; }
+};
+
+}; // namespace llvm
+
 //===----------------------------------------------------------------------===//
 //            Implementation
 //===----------------------------------------------------------------------===//
 
+const DIExpression::FragmentInfo
+    LiveDebugValues::DebugVariable::DefaultFragment = {
+        std::numeric_limits<uint64_t>::max(),
+        std::numeric_limits<uint64_t>::min()};
+
 char LiveDebugValues::ID = 0;
 
 char &llvm::LiveDebugValuesID = LiveDebugValues::ID;
@@ -366,6 +453,39 @@ void LiveDebugValues::getAnalysisUsage(A
   MachineFunctionPass::getAnalysisUsage(AU);
 }
 
+/// Erase a variable from the set of open ranges, and additionally erase any
+/// fragments that may overlap it.
+void LiveDebugValues::OpenRangesSet::erase(DebugVariable Var) {
+  // Erasure helper.
+  auto DoErase = [this](DebugVariable VarToErase) {
+    auto It = Vars.find(VarToErase);
+    if (It != Vars.end()) {
+      unsigned ID = It->second;
+      VarLocs.reset(ID);
+      Vars.erase(It);
+    }
+  };
+
+  // Erase the variable/fragment that ends here.
+  DoErase(Var);
+
+  // Extract the fragment. Interpret an empty fragment as one that covers all
+  // possible bits.
+  FragmentInfo ThisFragment = Var.getFragmentDefault();
+
+  // There may be fragments that overlap the designated fragment. Look them up
+  // in the pre-computed overlap map, and erase them too.
+  auto MapIt = OverlappingFragments.find({Var.getVar(), ThisFragment});
+  if (MapIt != OverlappingFragments.end()) {
+    for (auto Fragment : MapIt->second) {
+      LiveDebugValues::OptFragmentInfo FragmentHolder;
+      if (!DebugVariable::isFragmentDefault(Fragment))
+        FragmentHolder = LiveDebugValues::OptFragmentInfo(Fragment);
+      DoErase({Var.getVar(), FragmentHolder, Var.getInlinedAt()});
+    }
+  }
+}
+
 //===----------------------------------------------------------------------===//
 //            Debug Range Extension Implementation
 //===----------------------------------------------------------------------===//
@@ -416,13 +536,14 @@ void LiveDebugValues::transferDebugValue
   if (!MI.isDebugValue())
     return;
   const DILocalVariable *Var = MI.getDebugVariable();
+  const DIExpression *Expr = MI.getDebugExpression();
   const DILocation *DebugLoc = MI.getDebugLoc();
   const DILocation *InlinedAt = DebugLoc->getInlinedAt();
   assert(Var->isValidLocationForIntrinsic(DebugLoc) &&
          "Expected inlined-at fields to agree");
 
   // End all previous ranges of Var.
-  DebugVariable V(Var, InlinedAt);
+  DebugVariable V(Var, Expr, InlinedAt);
   OpenRanges.erase(V);
 
   // Add the VarLoc to OpenRanges from this DBG_VALUE.
@@ -464,8 +585,7 @@ void LiveDebugValues::insertTransferDebu
     unsigned LocId = VarLocIDs.insert(VL);
 
     // Close this variable's previous location range.
-    DebugVariable V(DebugInstr->getDebugVariable(),
-                    DebugInstr->getDebugLoc()->getInlinedAt());
+    DebugVariable V(*DebugInstr);
     OpenRanges.erase(V);
 
     OpenRanges.insert(LocId, VL.Var);
@@ -757,6 +877,66 @@ bool LiveDebugValues::transferTerminator
   return Changed;
 }
 
+/// Accumulate a mapping between each DILocalVariable fragment and other
+/// fragments of that DILocalVariable which overlap. This reduces work during
+/// the data-flow stage from "Find any overlapping fragments" to "Check if the
+/// known-to-overlap fragments are present".
+/// \param MI A previously unprocessed DEBUG_VALUE instruction to analyze for
+///           fragment usage.
+/// \param SeenFragments Map from DILocalVariable to all fragments of that
+///           Variable which are known to exist.
+/// \param OverlappingFragments The overlap map being constructed, from one
+///           Var/Fragment pair to a vector of fragments known to overlap.
+void LiveDebugValues::accumulateFragmentMap(MachineInstr &MI,
+                                            VarToFragments &SeenFragments,
+                                            OverlapMap &OverlappingFragments) {
+  DebugVariable MIVar(MI);
+  FragmentInfo ThisFragment = MIVar.getFragmentDefault();
+
+  // If this is the first sighting of this variable, then we are guaranteed
+  // there are currently no overlapping fragments either. Initialize the set
+  // of seen fragments, record no overlaps for the current one, and return.
+  auto SeenIt = SeenFragments.find(MIVar.getVar());
+  if (SeenIt == SeenFragments.end()) {
+    SmallSet<FragmentInfo, 4> OneFragment;
+    OneFragment.insert(ThisFragment);
+    SeenFragments.insert({MIVar.getVar(), OneFragment});
+
+    OverlappingFragments.insert({{MIVar.getVar(), ThisFragment}, {}});
+    return;
+  }
+
+  // If this particular Variable/Fragment pair already exists in the overlap
+  // map, it has already been accounted for.
+  auto IsInOLapMap =
+      OverlappingFragments.insert({{MIVar.getVar(), ThisFragment}, {}});
+  if (!IsInOLapMap.second)
+    return;
+
+  auto &ThisFragmentsOverlaps = IsInOLapMap.first->second;
+  auto &AllSeenFragments = SeenIt->second;
+
+  // Otherwise, examine all other seen fragments for this variable, with "this"
+  // fragment being a previously unseen fragment. Record any pair of
+  // overlapping fragments.
+  for (auto &ASeenFragment : AllSeenFragments) {
+    // Does this previously seen fragment overlap?
+    if (DIExpression::fragmentsOverlap(ThisFragment, ASeenFragment)) {
+      // Yes: Mark the current fragment as being overlapped.
+      ThisFragmentsOverlaps.push_back(ASeenFragment);
+      // Mark the previously seen fragment as being overlapped by the current
+      // one.
+      auto ASeenFragmentsOverlaps =
+          OverlappingFragments.find({MIVar.getVar(), ASeenFragment});
+      assert(ASeenFragmentsOverlaps != OverlappingFragments.end() &&
+             "Previously seen var fragment has no vector of overlaps");
+      ASeenFragmentsOverlaps->second.push_back(ThisFragment);
+    }
+  }
+
+  AllSeenFragments.insert(ThisFragment);
+}
+
 /// This routine creates OpenRanges and OutLocs.
 bool LiveDebugValues::process(MachineInstr &MI, OpenRangesSet &OpenRanges,
                               VarLocInMBB &OutLocs, VarLocMap &VarLocIDs,
@@ -888,11 +1068,15 @@ bool LiveDebugValues::ExtendRanges(Machi
   bool OLChanged = false;
   bool MBBJoined = false;
 
-  VarLocMap VarLocIDs;      // Map VarLoc<>unique ID for use in bitvectors.
-  OpenRangesSet OpenRanges; // Ranges that are open until end of bb.
-  VarLocInMBB OutLocs;      // Ranges that exist beyond bb.
-  VarLocInMBB InLocs;       // Ranges that are incoming after joining.
-  TransferMap Transfers;    // DBG_VALUEs associated with spills.
+  VarLocMap VarLocIDs;         // Map VarLoc<>unique ID for use in bitvectors.
+  OverlapMap OverlapFragments; // Map of overlapping variable fragments
+  OpenRangesSet OpenRanges(OverlapFragments);
+                              // Ranges that are open until end of bb.
+  VarLocInMBB OutLocs;        // Ranges that exist beyond bb.
+  VarLocInMBB InLocs;         // Ranges that are incoming after joining.
+  TransferMap Transfers;      // DBG_VALUEs associated with spills.
+
+  VarToFragments SeenFragments;
 
   // Blocks which are artificial, i.e. blocks which exclusively contain
   // instructions without locations, or with line 0 locations.
@@ -914,10 +1098,14 @@ bool LiveDebugValues::ExtendRanges(Machi
   // over the BBs. The LiveDebugVariables pass has already created DBG_VALUE
   // instructions for spills of registers that are known to be user variables
   // within the BB in which the spill occurs.
-  for (auto &MBB : MF)
-    for (auto &MI : MBB)
+  for (auto &MBB : MF) {
+    for (auto &MI : MBB) {
       process(MI, OpenRanges, OutLocs, VarLocIDs, Transfers,
               dontTransferChanges);
+      if (MI.isDebugValue())
+        accumulateFragmentMap(MI, SeenFragments, OverlapFragments);
+    }
+  }
 
   auto hasNonArtificialLocation = [](const MachineInstr &MI) -> bool {
     if (const DebugLoc &DL = MI.getDebugLoc())

Modified: llvm/trunk/test/DebugInfo/ARM/partial-subreg.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/DebugInfo/ARM/partial-subreg.ll?rev=363256&r1=363255&r2=363256&view=diff
==============================================================================
--- llvm/trunk/test/DebugInfo/ARM/partial-subreg.ll (original)
+++ llvm/trunk/test/DebugInfo/ARM/partial-subreg.ll Thu Jun 13 05:51:57 2019
@@ -10,30 +10,6 @@
 ; CHECK:  DW_TAG_formal_parameter
 ; CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset]	({{.*}}
 ; CHECK-NEXT:  [0x{{.*}}, 0x{{.*}}): DW_OP_regx D16, DW_OP_piece 0x8, DW_OP_regx D17, DW_OP_piece 0x4
-; CHECK-NEXT:  [0x{{.*}}, 0x{{.*}}): DW_OP_regx D16, DW_OP_piece 0x8, DW_OP_regx D17, DW_OP_piece 0x4
-
-; FIXME: The second location list entry should not be emitted.
-;
-; The input to LiveDebugValues is:
-;
-; bb.0.entry:
-;   [...]
-;   Bcc %bb.2, 1, killed $cpsr, debug-location !10; simd.swift:5900:12
-; bb.1:
-;   [...]
-;   DBG_VALUE $q8, $noreg, !"self", !DIExpression(DW_OP_LLVM_fragment, 0, 96)
-;   B %bb.3
-; bb.2.select.false:
-;   [...]
-;   DBG_VALUE $q8, $noreg, !"self", !DIExpression(DW_OP_LLVM_fragment, 0, 96)
-; bb.3.select.end:
-;   [...]
-;
-; The two DBG_VALUEs in the blocks describe different fragments of the
-; variable. However, LiveDebugValues is not aware of fragments, so it will
-; incorrectly insert a copy of the first DBG_VALUE in bb.3.select.end, since
-; the debug values in its predecessor blocks are described by the same
-; register.
 
 source_filename = "simd.ll"
 target datalayout = "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32"

Added: llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-fragments.mir
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-fragments.mir?rev=363256&view=auto
==============================================================================
--- llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-fragments.mir (added)
+++ llvm/trunk/test/DebugInfo/MIR/X86/live-debug-values-fragments.mir Thu Jun 13 05:51:57 2019
@@ -0,0 +1,258 @@
+# RUN: llc %s -o - -run-pass=livedebugvalues | FileCheck %s
+#
+# The first func tests that, for two independent variable fragments defined in
+# blocks 1 and 2, _both_ their locations are propagated into the exit block.
+# LiveDebugValues previously ignored fragments and only propagated the last
+# variable location seen.
+#
+# The second func tests that overlapping variable fragments are handled
+# correctly -- that the redefinition of a particular fragment also clobbers
+# any overlaps. The dbg value of $cx in block one should not be propagated to
+# block two, because an overlapping dbg value inst has been encountered.
+#
+# The third function checks that DBG_VALUEs without fragments are seen as
+# overlapping any DBG_VALUE with a fragment.
+#
+# CHECK-LABEL: foo
+# CHECK-LABEL: bb.3.bb3:
+# CHECK:      DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 32, 32)
+# CHECK-NEXT: DBG_VALUE $eax, $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 0, 32)
+# CHECK-NEXT: XOR32rr
+# CHECK-NEXT: RETQ
+#
+# CHECK-LABEL: bar
+# CHECK-LABEL: bb.0.entry:
+# CHECK:      DBG_VALUE $cx, $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 0, 16)
+
+# CHECK-LABEL: bb.1.bb1:
+# CHECK:      DBG_VALUE $cx, $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 0, 16)
+# CHECK-NEXT: MOV32rr
+# CHECK-NEXT: DBG_VALUE $ax, $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 8, 16)
+# CHECK-NEXT: JMP_1
+
+# CHECK-LABEL: bb.2.bb2:
+# CHECK-NOT:  DBG_VALUE
+# CHECK:      DBG_VALUE $ax, $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 8, 16)
+# CHECK-NEXT: MOV32rr
+# CHECK-NEXT: ADD32ri8
+# CHECK-NEXT: DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
+# CHECK-SAME                  !DIExpression(DW_OP_LLVM_fragment, 32, 32)
+# CHECK-NEXT: JMP_1
+
+# CHECK-LABEL: bb.3.bb3:
+# CHECK:      DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
+# CHECK-SAME                  !DIExpression(DW_OP_LLVM_fragment, 32, 32)
+# CHECK-NEXT: DBG_VALUE $ax, $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 8, 16)
+# CHECK-NEXT: XOR32rr
+# CHECK-NEXT: RETQ
+
+# CHECK-LABEL: baz
+# CHECK-LABEL: bb.0.entry:
+# CHECK:      DBG_VALUE $cx,  $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 0, 16)
+# CHECK-NEXT: JMP_1 %bb.1
+#
+# CHECK-LABEL: bb.1.bb1:
+# CHECK:      DBG_VALUE $cx,  $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 0, 16)
+# CHECK-NEXT: MOV32rr
+# CHECK-NEXT: DBG_VALUE $rdi, $noreg, !{{[0-9]+}}, !DIExpression()
+# CHECK-NEXT: JMP_1 %bb.2
+#
+# CHECK-LABEL: bb.2.bb2:
+# CHECK-NOT:  DBG_VALUE $cx
+# CHECK:      DBG_VALUE $rdi, $noreg, !{{[0-9]+}}, !DIExpression()
+# CHECK-NEXT: MOV32rr
+# CHECK-NEXT: ADD32ri8
+# CHECK-NEXT: DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 32, 32)
+# CHECK-NEXT: JMP_1 %bb.3
+#
+# CHECK-LABEL: bb.3.bb3:
+# CHECK-NOT:  DBG_VALUE $rdi
+# CHECK-NOT:  DBG_VALUE $cx
+# CHECK:      DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
+# CHECK-SAME:                 !DIExpression(DW_OP_LLVM_fragment, 32, 32)
+# CHECK-NEXT: XOR32rr
+# CHECK-NEXT: RETQ
+
+--- |
+  target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+  
+  define i32 @foo(i32* %bees, i32* %output) !dbg !4 {
+  entry:
+    br label %bb1
+  bb1:
+    br label %bb2
+  bb2:
+    br label %bb3
+  bb3:
+    ret i32 0
+  }
+
+  define i32 @bar(i32* %bees, i32* %output) !dbg !40 {
+  entry:
+    br label %bb1
+  bb1:
+    br label %bb2
+  bb2:
+    br label %bb3
+  bb3:
+    ret i32 0
+  }
+
+  define i32 @baz(i32* %bees, i32* %output) !dbg !80 {
+  entry:
+    br label %bb1
+  bb1:
+    br label %bb2
+  bb2:
+    br label %bb3
+  bb3:
+    ret i32 0
+  }
+
+  !llvm.module.flags = !{!0, !100}
+  !llvm.dbg.cu = !{!1}
+  
+  !100 = !{i32 2, !"Dwarf Version", i32 4}
+  !0 = !{i32 2, !"Debug Info Version", i32 3}
+  !1 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !2, producer: "beards", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+  !2 = !DIFile(filename: "bees.cpp", directory: ".")
+  !3 = !DILocalVariable(name: "flannel", scope: !4, file: !2, line: 1, type: !16)
+  !4 = distinct !DISubprogram(name: "nope", scope: !2, file: !2, line: 1, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !13, type: !14, isDefinition: true)
+  !8 = !DILocation(line: 4, scope: !4)
+  !13 = !{!3}
+  !14 = !DISubroutineType(types: !15)
+  !15 = !{!16}
+  !16 = !DIBasicType(name: "looong", size: 64, align: 64, encoding: DW_ATE_signed)
+  !40 = distinct !DISubprogram(name: "toast", scope: !2, file: !2, line: 1, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !53, type: !14, isDefinition: true)
+  !43 = !DILocalVariable(name: "charm", scope: !40, file: !2, line: 1, type: !16)
+  !48 = !DILocation(line: 4, scope: !40)
+  !53 = !{!43}
+  !80 = distinct !DISubprogram(name: "mort", scope: !2, file: !2, line: 1, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !93, type: !14, isDefinition: true)
+  !83 = !DILocalVariable(name: "bodkin", scope: !80, file: !2, line: 1, type: !16)
+  !88 = !DILocation(line: 4, scope: !80)
+  !93 = !{!83}
+
+...
+---
+name:            foo
+tracksRegLiveness: true
+registers:       []
+liveins:         
+  - { reg: '$rdi', virtual-reg: '' }
+body:             |
+  bb.0.entry:
+    successors: %bb.1
+    liveins: $rdi
+  
+    $ecx = XOR32rr undef $ecx, undef $ecx, implicit-def $eflags
+    JMP_1 %bb.1
+  
+  bb.1.bb1 (align 4):
+    successors: %bb.2
+    liveins: $ecx, $rdi
+  
+    $eax = MOV32rr killed $ecx, implicit-def $rax
+    DBG_VALUE $eax, $noreg, !3, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !8
+    JMP_1 %bb.2
+  
+  bb.2.bb2:
+    successors: %bb.3
+    liveins: $eax
+  
+    $ebx = MOV32rr $eax
+    $ebx = ADD32ri8 $ebx, 3, implicit-def dead $eflags, implicit killed $rbx, implicit-def $rbx
+    DBG_VALUE $ebx, $noreg, !3, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !8
+    JMP_1 %bb.3
+
+  bb.3.bb3:
+    liveins: $eax, $ebx
+    $eax = XOR32rr killed $eax, killed $ebx, implicit-def $eflags
+    RETQ $eax, debug-location !8
+
+...
+---
+name:            bar
+tracksRegLiveness: true
+registers:       []
+liveins:         
+  - { reg: '$rdi', virtual-reg: '' }
+body:             |
+  bb.0.entry:
+    successors: %bb.1
+    liveins: $rdi
+  
+    $ecx = XOR32rr undef $ecx, undef $ecx, implicit-def $eflags
+    DBG_VALUE $cx, $noreg, !43, !DIExpression(DW_OP_LLVM_fragment, 0, 16), debug-location !48
+    JMP_1 %bb.1
+  
+  bb.1.bb1:
+    successors: %bb.2
+    liveins: $ecx, $rdi
+  
+    $eax = MOV32rr killed $ecx, implicit-def $rax
+    DBG_VALUE $ax, $noreg, !43, !DIExpression(DW_OP_LLVM_fragment, 8, 16), debug-location !48
+    JMP_1 %bb.2
+  
+  bb.2.bb2:
+    successors: %bb.3
+    liveins: $eax
+  
+    $ebx = MOV32rr $eax
+    $ebx = ADD32ri8 $ebx, 3, implicit-def dead $eflags, implicit killed $rbx, implicit-def $rbx
+    DBG_VALUE $ebx, $noreg, !43, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !48
+    JMP_1 %bb.3
+
+  bb.3.bb3:
+    liveins: $eax, $ebx
+    $eax = XOR32rr killed $eax, killed $ebx, implicit-def $eflags
+    RETQ $eax, debug-location !48
+
+...
+---
+name:            baz
+tracksRegLiveness: true
+registers:       []
+liveins:         
+  - { reg: '$rdi', virtual-reg: '' }
+body:             |
+  bb.0.entry:
+    successors: %bb.1
+    liveins: $rdi
+  
+    $ecx = XOR32rr undef $ecx, undef $ecx, implicit-def $eflags
+    DBG_VALUE $cx, $noreg, !83, !DIExpression(DW_OP_LLVM_fragment, 0, 16), debug-location !88
+    JMP_1 %bb.1
+  
+  bb.1.bb1:
+    successors: %bb.2
+    liveins: $ecx, $rdi
+  
+    $eax = MOV32rr killed $ecx, implicit-def $rax
+    DBG_VALUE $rdi, $noreg, !83, !DIExpression(), debug-location !88
+    JMP_1 %bb.2
+  
+  bb.2.bb2:
+    successors: %bb.3
+    liveins: $eax
+  
+    $ebx = MOV32rr $eax
+    $ebx = ADD32ri8 $ebx, 3, implicit-def dead $eflags, implicit killed $rbx, implicit-def $rbx
+    DBG_VALUE $ebx, $noreg, !83, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !88
+    JMP_1 %bb.3
+
+  bb.3.bb3:
+    liveins: $eax, $ebx
+    $eax = XOR32rr killed $eax, killed $ebx, implicit-def $eflags
+    RETQ $eax, debug-location !88
+
+...




More information about the llvm-commits mailing list