[llvm] 7a7d370 - [NFC] Add DIExpression::calculateFragmentIntersect (#97738)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 12 00:28:39 PDT 2024
Author: Orlando Cazalet-Hyams
Date: 2024-07-12T08:28:36+01:00
New Revision: 7a7d370742dff82caa6a713bc2b1e2c0c51a9f30
URL: https://github.com/llvm/llvm-project/commit/7a7d370742dff82caa6a713bc2b1e2c0c51a9f30
DIFF: https://github.com/llvm/llvm-project/commit/7a7d370742dff82caa6a713bc2b1e2c0c51a9f30.diff
LOG: [NFC] Add DIExpression::calculateFragmentIntersect (#97738)
Patch [3/x] to fix structured bindings debug info in SROA.
This function computes a fragment, bit-extract operation if needed, and new
constant offset to describe a part of a variable covered by some memory.
This generalises, simplifies, and replaces at::calculateFragmentIntersect. That
version is still used as a wrapper for now though to keep this change NFC.
The new version takes doesn't have a DbgRecord parameter, instead using an
explicit address and address offset. The old version only operates on
dbg_assigns and this change means it can also operate on dbg_declare records
easily, which it will do in a subsequent patch.
The new version has a new out-param OffsetFromLocationInBits which is set to
the difference between the first bit of the variable location and the first
bit of the memory slice. This will be used in a subsequent patch in SROA to
determine the new offset to use in the address expression after splitting an
alloca.
Added:
Modified:
llvm/include/llvm/IR/DebugInfoMetadata.h
llvm/lib/IR/DebugInfo.cpp
llvm/lib/IR/DebugInfoMetadata.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index a6220035d25c2..c0b518b68b557 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -3084,6 +3084,43 @@ class DIExpression : public MDNode {
return 0;
}
+ /// Computes a fragment, bit-extract operation if needed, and new constant
+ /// offset to describe a part of a variable covered by some memory.
+ ///
+ /// The memory region starts at:
+ /// \p SliceStart + \p SliceOffsetInBits
+ /// And is size:
+ /// \p SliceSizeInBits
+ ///
+ /// The location of the existing variable fragment \p VarFrag is:
+ /// \p DbgPtr + \p DbgPtrOffsetInBits + \p DbgExtractOffsetInBits.
+ ///
+ /// It is intended that these arguments are derived from a debug record:
+ /// - \p DbgPtr is the (single) DIExpression operand.
+ /// - \p DbgPtrOffsetInBits is the constant offset applied to \p DbgPtr.
+ /// - \p DbgExtractOffsetInBits is the offset from a
+ /// DW_OP_LLVM_bit_extract_[sz]ext operation.
+ ///
+ /// Results and return value:
+ /// - Return false if the result can't be calculated for any reason.
+ /// - \p Result is set to nullopt if the intersect equals \p VarFarg.
+ /// - \p Result contains a zero-sized fragment if there's no intersect.
+ /// - \p OffsetFromLocationInBits is set to the
diff erence between the first
+ /// bit of the variable location and the first bit of the slice. The
+ /// magnitude of a negative value therefore indicates the number of bits
+ /// into the variable fragment that the memory region begins.
+ ///
+ /// We don't pass in a debug record directly to get the constituent parts
+ /// and offsets because
diff erent debug records store the information in
+ ///
diff erent places (dbg_assign has two DIExpressions - one contains the
+ /// fragment info for the entire intrinsic).
+ static bool calculateFragmentIntersect(
+ const DataLayout &DL, const Value *SliceStart, uint64_t SliceOffsetInBits,
+ uint64_t SliceSizeInBits, const Value *DbgPtr, int64_t DbgPtrOffsetInBits,
+ int64_t DbgExtractOffsetInBits, DIExpression::FragmentInfo VarFrag,
+ std::optional<DIExpression::FragmentInfo> &Result,
+ int64_t &OffsetFromLocationInBits);
+
using ExtOps = std::array<uint64_t, 6>;
/// Returns the ops for a zero- or sign-extension in a DIExpression.
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index 7f1489ebbd740..7fa1f9696d43b 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -1864,170 +1864,42 @@ void at::deleteAll(Function *F) {
DVR->eraseFromParent();
}
-/// 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.
-static DIExpression::FragmentInfo
-getFragmentOrEntireVariable(const DbgVariableRecord *DVR) {
- DIExpression::FragmentInfo VariableSlice(0, 0);
- // Get the fragment or variable size, or zero.
- if (auto Sz = DVR->getFragmentSizeInBits())
- VariableSlice.SizeInBits = *Sz;
- if (auto Frag = DVR->getExpression()->getFragmentInfo())
- VariableSlice.OffsetInBits = Frag->OffsetInBits;
- return VariableSlice;
-}
-
-static DIExpression::FragmentInfo
-getFragmentOrEntireVariable(const DbgVariableIntrinsic *DVI) {
- DIExpression::FragmentInfo VariableSlice(0, 0);
- // Get the fragment or variable size, or zero.
- if (auto Sz = DVI->getFragmentSizeInBits())
- VariableSlice.SizeInBits = *Sz;
- if (auto Frag = DVI->getExpression()->getFragmentInfo())
- VariableSlice.OffsetInBits = Frag->OffsetInBits;
- return VariableSlice;
-}
+/// FIXME: Remove this wrapper function and call
+/// DIExpression::calculateFragmentIntersect directly.
template <typename T>
bool calculateFragmentIntersectImpl(
const DataLayout &DL, const Value *Dest, uint64_t SliceOffsetInBits,
uint64_t SliceSizeInBits, const T *AssignRecord,
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, Assign=dbg.assign)
- //
- // Drawing the store (s) in memory followed by the shortened version ($),
- // then the dbg.assign (d), with the fragment information on a separate 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 [##] - Assign'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 Assign's fragment info:
- // SliceOfVariable ∩ Assign_fragment = none
- //
- // In this case: none of the dead bits of the store affect Assign.
- //
- // # 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, Assign=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 [##] - Assign'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 ∩ Assign_fragment = (offset: 144, size: 16)
- // SliceOfVariable tells us the bits of the variable described by Assign that
- // are affected by the DSE.
+ // No overlap if this DbgRecord describes a killed location.
if (AssignRecord->isKillAddress())
return false;
- DIExpression::FragmentInfo VarFrag =
- getFragmentOrEntireVariable(AssignRecord);
- 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;
+ int64_t AddrOffsetInBits;
{
- auto DestOffsetInBytes =
- AssignRecord->getAddress()->getPointerOffsetFrom(Dest, DL);
- if (!DestOffsetInBytes)
- return false; // Can't calculate
diff erence in addresses.
-
- int64_t ExprOffsetInBytes;
- if (!AssignRecord->getAddressExpression()->extractIfOffset(
- ExprOffsetInBytes))
+ int64_t AddrOffsetInBytes;
+ SmallVector<uint64_t> PostOffsetOps; //< Unused.
+ // Bail if we can't find a constant offset (or none) in the expression.
+ if (!AssignRecord->getAddressExpression()->extractLeadingOffset(
+ AddrOffsetInBytes, PostOffsetOps))
return false;
-
- int64_t PointerOffsetInBytes = *DestOffsetInBytes + ExprOffsetInBytes;
- PointerOffsetInBits = PointerOffsetInBytes * 8;
+ AddrOffsetInBits = AddrOffsetInBytes * 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 AssignRecord'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;
+ Value *Addr = AssignRecord->getAddress();
+ // FIXME: It may not always be zero.
+ int64_t BitExtractOffsetInBits = 0;
+ DIExpression::FragmentInfo VarFrag =
+ AssignRecord->getFragmentOrEntireVariable();
+
+ int64_t OffsetFromLocationInBits; //< Unused.
+ return DIExpression::calculateFragmentIntersect(
+ DL, Dest, SliceOffsetInBits, SliceSizeInBits, Addr, AddrOffsetInBits,
+ BitExtractOffsetInBits, VarFrag, Result, OffsetFromLocationInBits);
}
+
+/// FIXME: Remove this wrapper function and call
+/// DIExpression::calculateFragmentIntersect directly.
bool at::calculateFragmentIntersect(
const DataLayout &DL, const Value *Dest, uint64_t SliceOffsetInBits,
uint64_t SliceSizeInBits, const DbgAssignIntrinsic *DbgAssign,
@@ -2035,6 +1907,9 @@ bool at::calculateFragmentIntersect(
return calculateFragmentIntersectImpl(DL, Dest, SliceOffsetInBits,
SliceSizeInBits, DbgAssign, Result);
}
+
+/// FIXME: Remove this wrapper function and call
+/// DIExpression::calculateFragmentIntersect directly.
bool at::calculateFragmentIntersect(
const DataLayout &DL, const Value *Dest, uint64_t SliceOffsetInBits,
uint64_t SliceSizeInBits, const DbgVariableRecord *DVRAssign,
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index b4f7a9df41281..3440fcf711c78 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -2091,6 +2091,75 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
return DIExpression::get(Expr->getContext(), Ops);
}
+/// See declaration for more info.
+bool DIExpression::calculateFragmentIntersect(
+ const DataLayout &DL, const Value *SliceStart, uint64_t SliceOffsetInBits,
+ uint64_t SliceSizeInBits, const Value *DbgPtr, int64_t DbgPtrOffsetInBits,
+ int64_t DbgExtractOffsetInBits, DIExpression::FragmentInfo VarFrag,
+ std::optional<DIExpression::FragmentInfo> &Result,
+ int64_t &OffsetFromLocationInBits) {
+
+ if (VarFrag.SizeInBits == 0)
+ return false; // Variable size is unknown.
+
+ // Difference between mem slice start and the dbg location start.
+ // 0 4 8 12 16 ...
+ // | |
+ // dbg location start
+ // |
+ // mem slice start
+ // Here MemStartRelToDbgStartInBits is 8. Note this can be negative.
+ int64_t MemStartRelToDbgStartInBits;
+ {
+ auto MemOffsetFromDbgInBytes = SliceStart->getPointerOffsetFrom(DbgPtr, DL);
+ if (!MemOffsetFromDbgInBytes)
+ return false; // Can't calculate
diff erence in addresses.
+ // Difference between the pointers.
+ MemStartRelToDbgStartInBits = *MemOffsetFromDbgInBytes * 8;
+ // Add the
diff erence of the offsets.
+ MemStartRelToDbgStartInBits +=
+ SliceOffsetInBits - (DbgPtrOffsetInBits + DbgExtractOffsetInBits);
+ }
+
+ // Out-param. Invert offset to get offset from debug location.
+ OffsetFromLocationInBits = -MemStartRelToDbgStartInBits;
+
+ // Check if the variable fragment sits outside (before) this memory slice.
+ int64_t MemEndRelToDbgStart = MemStartRelToDbgStartInBits + SliceSizeInBits;
+ if (MemEndRelToDbgStart < 0) {
+ Result = {0, 0}; // Out-param.
+ return true;
+ }
+
+ // Work towards creating SliceOfVariable which is the bits of the variable
+ // that the memory region covers.
+ // 0 4 8 12 16 ...
+ // | |
+ // dbg location start with VarFrag offset=32
+ // |
+ // mem slice start: SliceOfVariable offset=40
+ int64_t MemStartRelToVarInBits =
+ MemStartRelToDbgStartInBits + VarFrag.OffsetInBits;
+ int64_t MemEndRelToVarInBits = MemStartRelToVarInBits + SliceSizeInBits;
+ // If the memory region starts before the debug location the fragment
+ // offset would be negative, which we can't encode. Limit those to 0. This
+ // is fine because those bits necessarily don't overlap with the existing
+ // variable fragment.
+ int64_t MemFragStart = std::max<int64_t>(0, MemStartRelToVarInBits);
+ int64_t MemFragSize =
+ std::max<int64_t>(0, MemEndRelToVarInBits - MemFragStart);
+ DIExpression::FragmentInfo SliceOfVariable(MemFragSize, MemFragStart);
+
+ // Intersect the memory region fragment with the variable location fragment.
+ DIExpression::FragmentInfo TrimmedSliceOfVariable =
+ DIExpression::FragmentInfo::intersect(SliceOfVariable, VarFrag);
+ if (TrimmedSliceOfVariable == VarFrag)
+ Result = std::nullopt; // Out-param.
+ else
+ Result = TrimmedSliceOfVariable; // Out-param.
+ return true;
+}
+
std::pair<DIExpression *, const ConstantInt *>
DIExpression::constantFold(const ConstantInt *CI) {
// Copy the APInt so we can modify it.
More information about the llvm-commits
mailing list