[llvm] 571eaea - Reapply "[Assignment Tracking] Fix fragment error for some DSE-shortened stores"

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 19 05:38:01 PDT 2023


Author: OCHyams
Date: 2023-04-19T13:36:47+01:00
New Revision: 571eaead173ba2c9c1df38ab230e1a40e24d3323

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

LOG: Reapply "[Assignment Tracking] Fix fragment error for some DSE-shortened stores"

This reverts commit 6db6ab4815a44bfcaabfcdd84a0ff458394f6f52 which reverts
D148536.

Build issues addressed in D148698.

Added: 
    llvm/test/DebugInfo/Generic/assignment-tracking/dse/shorten-offset.ll

Modified: 
    llvm/include/llvm/IR/DebugInfo.h
    llvm/include/llvm/IR/DebugInfoMetadata.h
    llvm/include/llvm/IR/IntrinsicInst.h
    llvm/lib/IR/DebugInfo.cpp
    llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
    llvm/lib/Transforms/Scalar/SROA.cpp
    llvm/test/DebugInfo/Generic/assignment-tracking/dse/dse-after-memcpyopt-merge.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h
index e717407e686c5..26a7cfbbb3502 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -224,6 +224,20 @@ void RAUW(DIAssignID *Old, DIAssignID *New);
 /// Remove all Assignment Tracking related intrinsics and metadata from \p F.
 void deleteAll(Function *F);
 
+/// Calculate the fragment of the variable in \p DAI covered
+/// from (Dest + SliceOffsetInBits) to
+///   to (Dest + SliceOffsetInBits + SliceSizeInBits)
+///
+/// Return false if it can't be calculated for any reason.
+/// Result is set to nullopt if the intersect equals the variable fragment (or
+/// variable size) in DAI.
+///
+/// Result contains a zero-sized fragment if there's no intersect.
+bool calculateFragmentIntersect(
+    const DataLayout &DL, const Value *Dest, uint64_t SliceOffsetInBits,
+    uint64_t SliceSizeInBits, const DbgAssignIntrinsic *DAI,
+    std::optional<DIExpression::FragmentInfo> &Result);
+
 /// Helper struct for trackAssignments, below. We don't use the similar
 /// DebugVariable class because trackAssignments doesn't (yet?) understand
 /// partial variables (fragment info) as input and want to make that clear and

diff  --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index ebf011c487dd8..21d2904d4c8c0 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2796,6 +2796,16 @@ class DIExpression : public MDNode {
     /// Return the index of the bit after the end of the fragment, e.g. for
     /// fragment offset=16 and size=32 return their sum, 48.
     uint64_t endInBits() const { return OffsetInBits + SizeInBits; }
+
+    /// Returns a zero-sized fragment if A and B don't intersect.
+    static DIExpression::FragmentInfo intersect(DIExpression::FragmentInfo A,
+                                                DIExpression::FragmentInfo B) {
+      uint64_t StartInBits = std::max(A.OffsetInBits, B.OffsetInBits);
+      uint64_t EndInBits = std::min(A.endInBits(), B.endInBits());
+      if (EndInBits <= StartInBits)
+        return {0, 0};
+      return DIExpression::FragmentInfo(EndInBits - StartInBits, StartInBits);
+    }
   };
 
   /// Retrieve the details of this fragment expression.

diff  --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h
index 548d88ebff1cf..9b349c8846480 100644
--- a/llvm/include/llvm/IR/IntrinsicInst.h
+++ b/llvm/include/llvm/IR/IntrinsicInst.h
@@ -376,6 +376,19 @@ class DbgVariableIntrinsic : public DbgInfoIntrinsic {
     return getExpression()->getFragmentInfo();
   }
 
+  /// Get the FragmentInfo for the variable if it exists, otherwise return a
+  /// FragmentInfo that covers the entire variable if the variable size is
+  /// known, otherwise return a zero-sized fragment.
+  DIExpression::FragmentInfo getFragmentOrEntireVariable() const {
+    DIExpression::FragmentInfo VariableSlice(0, 0);
+    // Get the fragment or variable size, or zero.
+    if (auto Sz = getFragmentSizeInBits())
+      VariableSlice.SizeInBits = *Sz;
+    if (auto Frag = getExpression()->getFragmentInfo())
+      VariableSlice.OffsetInBits = Frag->OffsetInBits;
+    return VariableSlice;
+  }
+
   /// \name Casting methods
   /// @{
   static bool classof(const IntrinsicInst *I) {

diff  --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index 3206b23f67de5..bd6987ab4f361 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -19,6 +19,7 @@
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DIBuilder.h"
@@ -1793,6 +1794,140 @@ void at::deleteAll(Function *F) {
     DAI->eraseFromParent();
 }
 
+bool at::calculateFragmentIntersect(
+    const DataLayout &DL, const Value *Dest, uint64_t SliceOffsetInBits,
+    uint64_t SliceSizeInBits, const DbgAssignIntrinsic *DAI,
+    std::optional<DIExpression::FragmentInfo> &Result) {
+  // There are multiple offsets at play in this function, so let's break it
+  // down. Starting with how variables may be stored in allocas:
+  //
+  //   1 Simplest case: variable is alloca sized and starts at offset 0.
+  //   2 Variable is larger than the alloca: the alloca holds just a part of it.
+  //   3 Variable is smaller than the alloca: the alloca may hold multiple
+  //   variables.
+  //
+  // Imagine we have a store to the entire alloca. In case (3) the store
+  // affects bits outside of the bounds of each variable. In case (2), where
+  // the alloca holds the Xth bit to the Yth bit of a variable, the
+  // zero-offset store doesn't represent an assignment at offset zero to the
+  // variable. It is an assignment to offset X.
+  //
+  // # Example 1
+  // Obviously, not all stores are alloca-sized and have zero offset. Imagine
+  // the lower 32 bits of this store are dead and are going to be DSEd:
+  //
+  //    store i64 %v, ptr %dest, !DIAssignID !1
+  //    dbg.assign(..., !DIExpression(fragment, 128, 32), !1, %dest,
+  //               !DIExpression(DW_OP_plus_uconst, 4))
+  //
+  // Goal: Given our dead bits at offset:0 size:32 for the store, determine the
+  // part of the variable, which fits in the fragment expressed by the
+  // dbg.assign, that has been killed, if any.
+  //
+  //     calculateFragmentIntersect(..., SliceOffsetInBits=0,
+  //                 SliceSizeInBits=32, Dest=%dest, DAI=dbg.assign)
+  //
+  // Drawing the store (s) in memory followed by the shortened version ($),
+  // then the dbg.assign (d), with the fragment information on a seperate scale
+  // underneath:
+  //
+  // Memory
+  // offset
+  //   from
+  //   dest 0      63
+  //        |      |
+  //       s[######] - Original stores 64 bits to Dest.
+  //       $----[##] - DSE says the lower 32 bits are dead, to be removed.
+  //       d    [##] - DAI's address-modifying expression adds 4 bytes to dest.
+  // Variable   |  |
+  // Fragment   128|
+  //  Offsets      159
+  //
+  // The answer is achieved in a few steps:
+  // 1. Add the fragment offset to the store offset:
+  //      SliceOffsetInBits:0 + VarFrag.OffsetInBits:128 = 128
+  //
+  // 2. Subtract the address-modifying expression offset plus 
diff erence
+  //    between d.address and dest:
+  //      128 - (expression_offset:32 + (d.address - dest):0) = 96
+  //
+  // 3. That offset along with the store size (32) represents the bits of the
+  //    variable that'd be affected by the store. Call it SliceOfVariable.
+  //    Intersect that with DAI's fragment info:
+  //      SliceOfVariable ∩ DAI_fragment = none
+  //
+  // In this case: none of the dead bits of the store affect DAI.
+  //
+  // # Example 2
+  // Similar example with the same goal. This time the upper 16 bits
+  // of the store are going to be DSE'd.
+  //
+  //    store i64 %v, ptr %dest, !DIAssignID !1
+  //    dbg.assign(..., !DIExpression(fragment, 128, 32), !1, %dest,
+  //               !DIExpression(DW_OP_plus_uconst, 4))
+  //
+  //     calculateFragmentIntersect(..., SliceOffsetInBits=48,
+  //                 SliceSizeInBits=16, Dest=%dest, DAI=dbg.assign)
+  //
+  // Memory
+  // offset
+  //   from
+  //   dest 0      63
+  //        |      |
+  //       s[######] - Original stores 64 bits to Dest.
+  //       $[####]-- - DSE says the upper 16 bits are dead, to be removed.
+  //       d    [##] - DAI's address-modifying expression adds 4 bytes to dest.
+  // Variable   |  |
+  // Fragment   128|
+  //  Offsets      159
+  //
+  // Using the same steps in the first example:
+  // 1. SliceOffsetInBits:48 + VarFrag.OffsetInBits:128 = 176
+  // 2. 176 - (expression_offset:32 + (d.address - dest):0) = 144
+  // 3. SliceOfVariable offset = 144, size = 16:
+  //      SliceOfVariable ∩ DAI_fragment = (offset: 144, size: 16)
+  // SliceOfVariable tells us the bits of the variable described by DAI that are
+  // affected by the DSE.
+  if (DAI->isKillAddress())
+    return false;
+
+  DIExpression::FragmentInfo VarFrag = DAI->getFragmentOrEntireVariable();
+  if (VarFrag.SizeInBits == 0)
+    return false; // Variable size is unknown.
+
+  // Calculate the 
diff erence between Dest and the dbg.assign address +
+  // address-modifying expression.
+  int64_t PointerOffsetInBits;
+  {
+    auto DestOffsetInBytes = DAI->getAddress()->getPointerOffsetFrom(Dest, DL);
+    if (!DestOffsetInBytes)
+      return false; // Can't calculate 
diff erence in addresses.
+
+    int64_t ExprOffsetInBytes;
+    if (!DAI->getAddressExpression()->extractIfOffset(ExprOffsetInBytes))
+      return false;
+
+    int64_t PointerOffsetInBytes = *DestOffsetInBytes + ExprOffsetInBytes;
+    PointerOffsetInBits = PointerOffsetInBytes * 8;
+  }
+
+  // Adjust the slice offset so that we go from describing the a slice
+  // of memory to a slice of the variable.
+  int64_t NewOffsetInBits =
+      SliceOffsetInBits + VarFrag.OffsetInBits - PointerOffsetInBits;
+  if (NewOffsetInBits < 0)
+    return false; // Fragment offsets can only be positive.
+  DIExpression::FragmentInfo SliceOfVariable(SliceSizeInBits, NewOffsetInBits);
+  // Intersect the variable slice with DAI's fragment to trim it down to size.
+  DIExpression::FragmentInfo TrimmedSliceOfVariable =
+      DIExpression::FragmentInfo::intersect(SliceOfVariable, VarFrag);
+  if (TrimmedSliceOfVariable == VarFrag)
+    Result = std::nullopt;
+  else
+    Result = TrimmedSliceOfVariable;
+  return true;
+}
+
 /// Collect constant properies (base, size, offset) of \p StoreDest.
 /// Return std::nullopt if any properties are not constants.
 static std::optional<AssignmentInfo>

diff  --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index 56ea24193bda8..d0c249f736006 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -482,41 +482,75 @@ memoryIsNotModifiedBetween(Instruction *FirstI, Instruction *SecondI,
   return true;
 }
 
-static void shortenAssignment(Instruction *Inst, uint64_t OldOffsetInBits,
-                              uint64_t OldSizeInBits, uint64_t NewSizeInBits,
-                              bool IsOverwriteEnd) {
-  DIExpression::FragmentInfo DeadFragment;
-  DeadFragment.SizeInBits = OldSizeInBits - NewSizeInBits;
-  DeadFragment.OffsetInBits =
+static void shortenAssignment(Instruction *Inst, Value *OriginalDest,
+                              uint64_t OldOffsetInBits, uint64_t OldSizeInBits,
+                              uint64_t NewSizeInBits, bool IsOverwriteEnd) {
+  const DataLayout &DL = Inst->getModule()->getDataLayout();
+  uint64_t DeadSliceSizeInBits = OldSizeInBits - NewSizeInBits;
+  uint64_t DeadSliceOffsetInBits =
       OldOffsetInBits + (IsOverwriteEnd ? NewSizeInBits : 0);
-
-  auto CreateDeadFragExpr = [Inst, DeadFragment]() {
-    // FIXME: This should be using the DIExpression in the Alloca's dbg.assign
-    // for the variable, since that could also contain a fragment?
-    return *DIExpression::createFragmentExpression(
-        DIExpression::get(Inst->getContext(), std::nullopt),
+  auto SetDeadFragExpr = [](DbgAssignIntrinsic *DAI,
+                            DIExpression::FragmentInfo DeadFragment) {
+    // createFragmentExpression expects an offset relative to the existing
+    // fragment offset if there is one.
+    uint64_t RelativeOffset = DeadFragment.OffsetInBits -
+                              DAI->getExpression()
+                                  ->getFragmentInfo()
+                                  .value_or(DIExpression::FragmentInfo(0, 0))
+                                  .OffsetInBits;
+    if (auto NewExpr = DIExpression::createFragmentExpression(
+            DAI->getExpression(), RelativeOffset, DeadFragment.SizeInBits)) {
+      DAI->setExpression(*NewExpr);
+      return;
+    }
+    // Failed to create a fragment expression for this so discard the value,
+    // making this a kill location.
+    auto *Expr = *DIExpression::createFragmentExpression(
+        DIExpression::get(DAI->getContext(), std::nullopt),
         DeadFragment.OffsetInBits, DeadFragment.SizeInBits);
+    DAI->setExpression(Expr);
+    DAI->setKillLocation();
   };
 
   // A DIAssignID to use so that the inserted dbg.assign intrinsics do not
   // link to any instructions. Created in the loop below (once).
   DIAssignID *LinkToNothing = nullptr;
+  LLVMContext &Ctx = Inst->getContext();
+  auto GetDeadLink = [&Ctx, &LinkToNothing]() {
+    if (!LinkToNothing)
+      LinkToNothing = DIAssignID::getDistinct(Ctx);
+    return LinkToNothing;
+  };
 
   // Insert an unlinked dbg.assign intrinsic for the dead fragment after each
-  // overlapping dbg.assign intrinsic.
-  for (auto *DAI : at::getAssignmentMarkers(Inst)) {
-    if (auto FragInfo = DAI->getExpression()->getFragmentInfo()) {
-      if (!DIExpression::fragmentsOverlap(*FragInfo, DeadFragment))
-        continue;
+  // overlapping dbg.assign intrinsic. The loop invalidates the iterators
+  // returned by getAssignmentMarkers so save a copy of the markers to iterate
+  // over.
+  auto LinkedRange = at::getAssignmentMarkers(Inst);
+  SmallVector<DbgAssignIntrinsic *> Linked(LinkedRange.begin(),
+                                           LinkedRange.end());
+  for (auto *DAI : Linked) {
+    std::optional<DIExpression::FragmentInfo> NewFragment;
+    if (!at::calculateFragmentIntersect(DL, OriginalDest, DeadSliceOffsetInBits,
+                                        DeadSliceSizeInBits, DAI,
+                                        NewFragment) ||
+        !NewFragment) {
+      // We couldn't calculate the intersecting fragment for some reason. Be
+      // cautious and unlink the whole assignment from the store.
+      DAI->setKillAddress();
+      DAI->setAssignId(GetDeadLink());
+      continue;
     }
+    // No intersect.
+    if (NewFragment->SizeInBits == 0)
+      continue;
 
     // Fragments overlap: insert a new dbg.assign for this dead part.
     auto *NewAssign = cast<DbgAssignIntrinsic>(DAI->clone());
     NewAssign->insertAfter(DAI);
-    if (!LinkToNothing)
-      LinkToNothing = DIAssignID::getDistinct(Inst->getContext());
-    NewAssign->setAssignId(LinkToNothing);
-    NewAssign->setExpression(CreateDeadFragExpr());
+    NewAssign->setAssignId(GetDeadLink());
+    if (NewFragment)
+      SetDeadFragExpr(NewAssign, *NewFragment);
     NewAssign->setKillAddress();
   }
 }
@@ -593,8 +627,8 @@ static bool tryToShorten(Instruction *DeadI, int64_t &DeadStart,
   DeadIntrinsic->setLength(TrimmedLength);
   DeadIntrinsic->setDestAlignment(PrefAlign);
 
+  Value *OrigDest = DeadIntrinsic->getRawDest();
   if (!IsOverwriteEnd) {
-    Value *OrigDest = DeadIntrinsic->getRawDest();
     Type *Int8PtrTy =
         Type::getInt8PtrTy(DeadIntrinsic->getContext(),
                            OrigDest->getType()->getPointerAddressSpace());
@@ -613,7 +647,7 @@ static bool tryToShorten(Instruction *DeadI, int64_t &DeadStart,
   }
 
   // Update attached dbg.assign intrinsics. Assume 8-bit byte.
-  shortenAssignment(DeadI, DeadStart * 8, DeadSize * 8, NewSize * 8,
+  shortenAssignment(DeadI, OrigDest, DeadStart * 8, DeadSize * 8, NewSize * 8,
                     IsOverwriteEnd);
 
   // Finally update start and size of dead access.

diff  --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp
index d5615c902ad15..db518a4c101eb 100644
--- a/llvm/lib/Transforms/Scalar/SROA.cpp
+++ b/llvm/lib/Transforms/Scalar/SROA.cpp
@@ -130,6 +130,7 @@ namespace {
 /// UseFrag - Use Target as the new fragment.
 /// UseNoFrag - The new slice already covers the whole variable.
 /// Skip - The new alloca slice doesn't include this variable.
+/// FIXME: Can we use calculateFragmentIntersect instead?
 enum FragCalcResult { UseFrag, UseNoFrag, Skip };
 static FragCalcResult
 calculateFragment(DILocalVariable *Variable,

diff  --git a/llvm/test/DebugInfo/Generic/assignment-tracking/dse/dse-after-memcpyopt-merge.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/dse/dse-after-memcpyopt-merge.ll
index 0d6f6fd910595..f1704f4bcc98b 100644
--- a/llvm/test/DebugInfo/Generic/assignment-tracking/dse/dse-after-memcpyopt-merge.ll
+++ b/llvm/test/DebugInfo/Generic/assignment-tracking/dse/dse-after-memcpyopt-merge.ll
@@ -1,4 +1,4 @@
-; RUN: opt %s -S -passes=dse -o - | FileCheck %s
+; RUN: opt %s -S -passes=dse -o - | FileCheck %s --implicit-check-not="call void @llvm.dbg"
 
 ;; Observed in the wild, but test is created by running memcpyopt on
 ;; assignment-tracking/memcpyopt/merge-stores.ll then manually inserting
@@ -7,19 +7,17 @@
 ;; A memory intrinsic (or vector instruction) might have multiple dbg.assigns
 ;; linked to it if it has been created as a result of merging scalar stores,
 ;; such as in this example. DSE is going to shorten the memset because there's
-;; a later store that overwrites part of it. Insert a linked dbg.assign after
-;; each overlapping fragment to undef the dead part's memory location without
-;; needing to work out how to split the fragments exactly.
+;; a later store that overwrites part of it. Unlink the dbg.assigns that
+;; describe the overlapping fragments.
 
 ;; Check that there's an unlinked dbg.assign inserted after each overlapping
 ;; fragment of the shortened store.
 ;;
-; CHECK:      call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata ![[ID:[0-9]+]], metadata ptr %arrayidx5.i, metadata !DIExpression())
-; CHECK-NEXT: call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata ![[UniqueID1:[0-9]+]], metadata ptr undef, metadata !DIExpression())
-
-; CHECK:      call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata ![[ID]], metadata ptr %arrayidx7.i, metadata !DIExpression())
-; CHECK-NEXT: call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata ![[UniqueID2:[0-9]+]], metadata ptr undef, metadata !DIExpression())
-
+; CHECK: llvm.dbg.assign({{.*}}, metadata ptr %g, metadata !DIExpression())
+; CHECK: llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[#]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata ![[ID:[0-9]+]], metadata ptr %arrayidx.i, metadata !DIExpression())
+; CHECK: llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[#]], metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata ![[ID]], metadata ptr %arrayidx3.i, metadata !DIExpression())
+; CHECK: llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[#]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata ![[UniqueID1:[0-9]+]], metadata ptr undef, metadata !DIExpression())
+; CHECK: llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[#]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata ![[UniqueID2:[0-9]+]], metadata ptr undef, metadata !DIExpression())
 ; CHECK: call void @llvm.memset{{.*}}, !DIAssignID ![[ID]]
 
 ; CHECK-DAG: ![[ID]] = distinct !DIAssignID()
@@ -34,10 +32,7 @@ define dso_local void @_Z1fv() local_unnamed_addr !dbg !7 {
 entry:
   %g = alloca %struct.v, align 4, !DIAssignID !23
   call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !23, metadata ptr %g, metadata !DIExpression()), !dbg !24
-  call void @llvm.lifetime.start.p0i8(i64 16, ptr nonnull %g), !dbg !25
-  call void @llvm.dbg.assign(metadata ptr %g, metadata !26, metadata !DIExpression(), metadata !35, metadata ptr undef, metadata !DIExpression()), !dbg !32
-  call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !29, metadata !DIExpression(), metadata !36, metadata ptr undef, metadata !DIExpression()), !dbg !32
-  %arrayidx.i = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 2, !dbg !37
+   %arrayidx.i = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 2, !dbg !37
   call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata !39, metadata ptr %arrayidx.i, metadata !DIExpression()), !dbg !24
   %arrayidx3.i = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 1, !dbg !40
   call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !39, metadata ptr %arrayidx3.i, metadata !DIExpression()), !dbg !24
@@ -50,19 +45,14 @@ entry:
   ;; -- Start modification
   %arrayidx7 = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 3, !dbg !24
   store float 0.000000e+00, ptr %arrayidx7, align 4, !dbg !24, !DIAssignID !49
-  call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !49, metadata ptr %arrayidx7, metadata !DIExpression()), !dbg !24
   %arrayidx = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 0, !dbg !24
   store float 0.000000e+00, ptr %arrayidx, align 4, !dbg !24, !DIAssignID !50
-  call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !50, metadata ptr %arrayidx, metadata !DIExpression()), !dbg !24
   ;; -- End modification
   call void @_Z3escP1v(ptr nonnull %g), !dbg !43
-  call void @llvm.lifetime.end.p0i8(i64 16, ptr nonnull %0), !dbg !45
   ret void, !dbg !45
 }
 
-declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture)
 declare !dbg !64 dso_local void @_Z3escP1v(ptr) local_unnamed_addr
-declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture)
 declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
 declare void @llvm.memset.p0i8.i64(ptr nocapture writeonly, i8, i64, i1 immarg)
 

diff  --git a/llvm/test/DebugInfo/Generic/assignment-tracking/dse/shorten-offset.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/dse/shorten-offset.ll
new file mode 100644
index 0000000000000..b6dc56acf7636
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/assignment-tracking/dse/shorten-offset.ll
@@ -0,0 +1,134 @@
+; RUN: opt %s -S -passes=dse -o - | FileCheck %s --implicit-check-not="call void @llvm.dbg"
+
+;; Based on the test shorten.ll with some adjustments.
+;;
+;; $ cat test.cpp
+;; void esc(char*);
+;; void shortenEnd() {
+;;   char local[80];                      //        bits    frag
+;;   __builtin_memset(local + 8,  0, 24); // local:  64-160 ( 64, 96)
+;;   __builtin_memset(local + 16, 8, 40); // local: 128-160 (128, 32)
+;;   esc(local);
+;; }
+;; void shortenStart() {
+;;   char local2[40];                 //          bits   frag
+;;   __builtin_memset(local2, 0, 40); // local2:  0-160  (0, 160)
+;;   __builtin_memset(local2, 8, 16); // local2:  0-128  (0, 128)
+;;   esc(local2);
+;; }
+
+;; The variables and intrinsics have been adjusted with by hand to test
+;; what happens when the variable doesn't fill the whole alloca, and
+;; when offsets are encoded with both the address component of the dbg.assign
+;; and the address modifying DIExpression.
+
+;; DeadStoreElimination will shorten the first store in shortenEnd from [64,
+;; 192) bits to [64, 128) bits. Variable 'local' has been adjusted to be 160
+;; bits large. Check that we get an unlinked dbg.assign covering the deleted
+;; bits that overlap the dbg.assign's fagment: [128, 160) (offset=128 size=32).
+
+; CHECK: @_Z10shortenEndv
+; CHECK:      call void @llvm.dbg.assign({{.*}}, metadata ptr %local, metadata !DIExpression())
+; CHECK:      call void @llvm.memset{{.*}}, !DIAssignID ![[ID:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 96), metadata ![[ID:[0-9]+]], metadata ptr %offset_4_bytes, metadata !DIExpression(DW_OP_plus_uconst, 4))
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 32), metadata ![[UniqueID1:[0-9]+]], metadata ptr undef, metadata !DIExpression({{.*}}))
+
+;; DSE will shorten the first store in shortenStart from [0, 160) bits to [128,
+;; 160) bits. Variable 'local2' has been adjusted to be 160 bits.  Check we get
+;; an unlinked dbg.assign covering the deleted bits that overlap the
+;; dbg.assign's fragment (no fragment in this case, i.e. the whole variable):
+;; [0, 128) (offset=0, size=128).
+
+; CHECK: @_Z12shortenStartv
+; CHECK:      call void @llvm.dbg.assign({{.*}}, metadata ptr %local2, metadata !DIExpression())
+; CHECK:      call void @llvm.memset{{.*}}, !DIAssignID ![[ID2:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR2:[0-9]+]], metadata !DIExpression(), metadata ![[ID2]], metadata ptr %local2, metadata !DIExpression())
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR2]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 128), metadata ![[UniqueID2:[0-9]+]], metadata ptr undef, metadata !DIExpression())
+
+; CHECK-DAG: ![[ID]] = distinct !DIAssignID()
+; CHECK-DAG: ![[UniqueID1]] = distinct !DIAssignID()
+; CHECK-DAG: ![[UniqueID2]] = distinct !DIAssignID()
+
+define dso_local void @_Z10shortenEndv() local_unnamed_addr #0 !dbg !7 {
+entry:
+  %local = alloca [80 x i8], align 16, !DIAssignID !16
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !16, metadata ptr %local, metadata !DIExpression()), !dbg !17
+  %arraydecay = getelementptr inbounds [80 x i8], ptr %local, i64 0, i64 0, !dbg !19
+  %offset_4_bytes = getelementptr inbounds [80 x i8], ptr %local, i64 0, i64 4, !dbg !21
+  %offset_8_bytes = getelementptr inbounds [80 x i8], ptr %local, i64 0, i64 8, !dbg !21
+  call void @llvm.memset.p0i8.i64(ptr noundef nonnull align 16 dereferenceable(24) %offset_8_bytes, i8 0, i64 72, i1 false), !dbg !19, !DIAssignID !20
+  call void @llvm.dbg.assign(metadata i8 0, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 96), metadata !20, metadata ptr %offset_4_bytes, metadata !DIExpression(DW_OP_plus_uconst, 4)), !dbg !17
+  %offset_16_bytes = getelementptr inbounds [80 x i8], ptr %local, i64 0, i64 4, !dbg !21
+  call void @llvm.memset.p0i8.i64(ptr noundef nonnull align 16 dereferenceable(40) %offset_16_bytes, i8 8, i64 64, i1 false), !dbg !22, !DIAssignID !23
+  call void @_Z3escPi(ptr noundef nonnull %arraydecay), !dbg !24
+  ret void, !dbg !25
+}
+
+declare void @llvm.memset.p0i8.i64(ptr nocapture writeonly, i8, i64, i1 immarg)
+declare !dbg !26 dso_local void @_Z3escPi(ptr noundef) local_unnamed_addr
+
+define dso_local void @_Z12shortenStartv() local_unnamed_addr #0 !dbg !31 {
+entry:
+  %local2 = alloca [40 x i8], align 16, !DIAssignID !37
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !33, metadata !DIExpression(), metadata !37, metadata ptr %local2, metadata !DIExpression()), !dbg !38
+  %arraydecay = getelementptr inbounds [40 x i8], ptr %local2, i64 0, i64 0, !dbg !40
+  call void @llvm.memset.p0i8.i64(ptr noundef nonnull align 16 dereferenceable(40) %local2, i8 0, i64 36, i1 false), !dbg !40, !DIAssignID !41
+  call void @llvm.dbg.assign(metadata i8 0, metadata !33, metadata !DIExpression(), metadata !41, metadata ptr %local2, metadata !DIExpression()), !dbg !38
+  call void @llvm.memset.p0i8.i64(ptr noundef nonnull align 16 dereferenceable(16) %local2, i8 8, i64 16, i1 false), !dbg !42, !DIAssignID !43
+  call void @_Z3escPi(ptr noundef nonnull %arraydecay), !dbg !44
+  ret void, !dbg !45
+}
+
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !1000}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"uwtable", i32 1}
+!6 = !{!"clang version 14.0.0"}
+!7 = distinct !DISubprogram(name: "shortenEnd", linkageName: "_Z10shortenEndv", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!10 = !{!11}
+!11 = !DILocalVariable(name: "local", scope: !7, file: !1, line: 3, type: !12)
+!12 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 160, elements: !14)
+!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!14 = !{!15}
+!15 = !DISubrange(count: 5)
+!16 = distinct !DIAssignID()
+!17 = !DILocation(line: 0, scope: !7)
+!18 = !DILocation(line: 3, column: 3, scope: !7)
+!19 = !DILocation(line: 4, column: 3, scope: !7)
+!20 = distinct !DIAssignID()
+!21 = !DILocation(line: 5, column: 26, scope: !7)
+!22 = !DILocation(line: 5, column: 3, scope: !7)
+!23 = distinct !DIAssignID()
+!24 = !DILocation(line: 6, column: 3, scope: !7)
+!25 = !DILocation(line: 7, column: 1, scope: !7)
+!26 = !DISubprogram(name: "esc", linkageName: "_Z3escPi", scope: !1, file: !1, line: 1, type: !27, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !30)
+!27 = !DISubroutineType(types: !28)
+!28 = !{null, !29}
+!29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64)
+!30 = !{}
+!31 = distinct !DISubprogram(name: "shortenStart", linkageName: "_Z12shortenStartv", scope: !1, file: !1, line: 8, type: !8, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !32)
+!32 = !{!33}
+!33 = !DILocalVariable(name: "local2", scope: !31, file: !1, line: 9, type: !34)
+!34 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 160, elements: !35)
+!35 = !{!36}
+!36 = !DISubrange(count: 5)
+!37 = distinct !DIAssignID()
+!38 = !DILocation(line: 0, scope: !31)
+!39 = !DILocation(line: 9, column: 3, scope: !31)
+!40 = !DILocation(line: 10, column: 3, scope: !31)
+!41 = distinct !DIAssignID()
+!42 = !DILocation(line: 11, column: 3, scope: !31)
+!43 = distinct !DIAssignID()
+!44 = !DILocation(line: 12, column: 3, scope: !31)
+!45 = !DILocation(line: 13, column: 1, scope: !31)
+!1000 = !{i32 7, !"debug-info-assignment-tracking", i1 true}


        


More information about the llvm-commits mailing list