Skip to content

Commit 6eb61bb

Browse files
committed
[NFC] Add DIExpression::calculateFragmentIntersect
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.
1 parent deb6b60 commit 6eb61bb

File tree

3 files changed

+134
-153
lines changed

3 files changed

+134
-153
lines changed

llvm/include/llvm/IR/DebugInfoMetadata.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3074,6 +3074,43 @@ class DIExpression : public MDNode {
30743074
return 0;
30753075
}
30763076

3077+
/// Computes a fragment, bit-extract operation if needed, and new constant
3078+
/// offset to describe a part of a variable covered by some memory.
3079+
///
3080+
/// The memory region starts at:
3081+
/// \p SliceStart + \p SliceOffsetInBits
3082+
/// And is size:
3083+
/// \p SliceSizeInBits
3084+
///
3085+
/// The location of the existing variable fragment \p VarFrag is:
3086+
/// \p DbgPtr + \p DbgPtrOffsetInBits + \p DbgExtractOffsetInBits.
3087+
///
3088+
/// It is intended that these arguments are derived from a debug record:
3089+
/// - \p DbgPtr is the (single) DIExpression operand.
3090+
/// - \p DbgPtrOffsetInBits is the constant offset applied to \p DbgPtr.
3091+
/// - \p DbgExtractOffsetInBits is the offset from a
3092+
/// DW_OP_LLVM_bit_extract_[sz]ext operation.
3093+
///
3094+
/// Results and return value:
3095+
/// - Return false if the result can't be calculated for any reason.
3096+
/// - \p Result is set to nullopt if the intersect equals \p VarFarg.
3097+
/// - \p Result contains a zero-sized fragment if there's no intersect.
3098+
/// - \p OffsetFromLocationInBits is set to the difference between the first
3099+
/// bit of the variable location and the first bit of the slice. The
3100+
/// magnitude of a negative value therefore indicates the number of bits
3101+
/// into the variable fragment that the memory region begins.
3102+
///
3103+
/// We don't pass in a debug record directly to get the constituent parts
3104+
/// and offsets because different debug records store the information in
3105+
/// different places (dbg_assign has two DIExpressions - one contains the
3106+
/// fragment info for the entire intrinsic).
3107+
static bool calculateFragmentIntersect(
3108+
const DataLayout &DL, const Value *SliceStart, uint64_t SliceOffsetInBits,
3109+
uint64_t SliceSizeInBits, const Value *DbgPtr, int64_t DbgPtrOffsetInBits,
3110+
int64_t DbgExtractOffsetInBits, DIExpression::FragmentInfo VarFrag,
3111+
std::optional<DIExpression::FragmentInfo> &Result,
3112+
int64_t &OffsetFromLocationInBits);
3113+
30773114
using ExtOps = std::array<uint64_t, 6>;
30783115

30793116
/// Returns the ops for a zero- or sign-extension in a DIExpression.

llvm/lib/IR/DebugInfo.cpp

Lines changed: 28 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,181 +1864,56 @@ void at::deleteAll(Function *F) {
18641864
DVR->eraseFromParent();
18651865
}
18661866

1867-
/// Get the FragmentInfo for the variable if it exists, otherwise return a
1868-
/// FragmentInfo that covers the entire variable if the variable size is
1869-
/// known, otherwise return a zero-sized fragment.
1870-
static DIExpression::FragmentInfo
1871-
getFragmentOrEntireVariable(const DbgVariableRecord *DVR) {
1872-
DIExpression::FragmentInfo VariableSlice(0, 0);
1873-
// Get the fragment or variable size, or zero.
1874-
if (auto Sz = DVR->getFragmentSizeInBits())
1875-
VariableSlice.SizeInBits = *Sz;
1876-
if (auto Frag = DVR->getExpression()->getFragmentInfo())
1877-
VariableSlice.OffsetInBits = Frag->OffsetInBits;
1878-
return VariableSlice;
1879-
}
1880-
1881-
static DIExpression::FragmentInfo
1882-
getFragmentOrEntireVariable(const DbgVariableIntrinsic *DVI) {
1883-
DIExpression::FragmentInfo VariableSlice(0, 0);
1884-
// Get the fragment or variable size, or zero.
1885-
if (auto Sz = DVI->getFragmentSizeInBits())
1886-
VariableSlice.SizeInBits = *Sz;
1887-
if (auto Frag = DVI->getExpression()->getFragmentInfo())
1888-
VariableSlice.OffsetInBits = Frag->OffsetInBits;
1889-
return VariableSlice;
1890-
}
1867+
/// FIXME: Remove this wrapper function and call
1868+
/// DIExpression::calculateFragmentIntersect directly.
18911869
template <typename T>
18921870
bool calculateFragmentIntersectImpl(
18931871
const DataLayout &DL, const Value *Dest, uint64_t SliceOffsetInBits,
18941872
uint64_t SliceSizeInBits, const T *AssignRecord,
18951873
std::optional<DIExpression::FragmentInfo> &Result) {
1896-
// There are multiple offsets at play in this function, so let's break it
1897-
// down. Starting with how variables may be stored in allocas:
1898-
//
1899-
// 1 Simplest case: variable is alloca sized and starts at offset 0.
1900-
// 2 Variable is larger than the alloca: the alloca holds just a part of it.
1901-
// 3 Variable is smaller than the alloca: the alloca may hold multiple
1902-
// variables.
1903-
//
1904-
// Imagine we have a store to the entire alloca. In case (3) the store
1905-
// affects bits outside of the bounds of each variable. In case (2), where
1906-
// the alloca holds the Xth bit to the Yth bit of a variable, the
1907-
// zero-offset store doesn't represent an assignment at offset zero to the
1908-
// variable. It is an assignment to offset X.
1909-
//
1910-
// # Example 1
1911-
// Obviously, not all stores are alloca-sized and have zero offset. Imagine
1912-
// the lower 32 bits of this store are dead and are going to be DSEd:
1913-
//
1914-
// store i64 %v, ptr %dest, !DIAssignID !1
1915-
// dbg.assign(..., !DIExpression(fragment, 128, 32), !1, %dest,
1916-
// !DIExpression(DW_OP_plus_uconst, 4))
1917-
//
1918-
// Goal: Given our dead bits at offset:0 size:32 for the store, determine the
1919-
// part of the variable, which fits in the fragment expressed by the
1920-
// dbg.assign, that has been killed, if any.
1921-
//
1922-
// calculateFragmentIntersect(..., SliceOffsetInBits=0,
1923-
// SliceSizeInBits=32, Dest=%dest, Assign=dbg.assign)
1924-
//
1925-
// Drawing the store (s) in memory followed by the shortened version ($),
1926-
// then the dbg.assign (d), with the fragment information on a separate scale
1927-
// underneath:
1928-
//
1929-
// Memory
1930-
// offset
1931-
// from
1932-
// dest 0 63
1933-
// | |
1934-
// s[######] - Original stores 64 bits to Dest.
1935-
// $----[##] - DSE says the lower 32 bits are dead, to be removed.
1936-
// d [##] - Assign's address-modifying expression adds 4 bytes to
1937-
// dest.
1938-
// Variable | |
1939-
// Fragment 128|
1940-
// Offsets 159
1941-
//
1942-
// The answer is achieved in a few steps:
1943-
// 1. Add the fragment offset to the store offset:
1944-
// SliceOffsetInBits:0 + VarFrag.OffsetInBits:128 = 128
1945-
//
1946-
// 2. Subtract the address-modifying expression offset plus difference
1947-
// between d.address and dest:
1948-
// 128 - (expression_offset:32 + (d.address - dest):0) = 96
1949-
//
1950-
// 3. That offset along with the store size (32) represents the bits of the
1951-
// variable that'd be affected by the store. Call it SliceOfVariable.
1952-
// Intersect that with Assign's fragment info:
1953-
// SliceOfVariable ∩ Assign_fragment = none
1954-
//
1955-
// In this case: none of the dead bits of the store affect Assign.
1956-
//
1957-
// # Example 2
1958-
// Similar example with the same goal. This time the upper 16 bits
1959-
// of the store are going to be DSE'd.
1960-
//
1961-
// store i64 %v, ptr %dest, !DIAssignID !1
1962-
// dbg.assign(..., !DIExpression(fragment, 128, 32), !1, %dest,
1963-
// !DIExpression(DW_OP_plus_uconst, 4))
1964-
//
1965-
// calculateFragmentIntersect(..., SliceOffsetInBits=48,
1966-
// SliceSizeInBits=16, Dest=%dest, Assign=dbg.assign)
1967-
//
1968-
// Memory
1969-
// offset
1970-
// from
1971-
// dest 0 63
1972-
// | |
1973-
// s[######] - Original stores 64 bits to Dest.
1974-
// $[####]-- - DSE says the upper 16 bits are dead, to be removed.
1975-
// d [##] - Assign's address-modifying expression adds 4 bytes to
1976-
// dest.
1977-
// Variable | |
1978-
// Fragment 128|
1979-
// Offsets 159
1980-
//
1981-
// Using the same steps in the first example:
1982-
// 1. SliceOffsetInBits:48 + VarFrag.OffsetInBits:128 = 176
1983-
// 2. 176 - (expression_offset:32 + (d.address - dest):0) = 144
1984-
// 3. SliceOfVariable offset = 144, size = 16:
1985-
// SliceOfVariable ∩ Assign_fragment = (offset: 144, size: 16)
1986-
// SliceOfVariable tells us the bits of the variable described by Assign that
1987-
// are affected by the DSE.
1874+
// No overlap if this DbgRecord describes a killed location.
19881875
if (AssignRecord->isKillAddress())
19891876
return false;
19901877

1991-
DIExpression::FragmentInfo VarFrag =
1992-
getFragmentOrEntireVariable(AssignRecord);
1993-
if (VarFrag.SizeInBits == 0)
1994-
return false; // Variable size is unknown.
1995-
1996-
// Calculate the difference between Dest and the dbg.assign address +
1997-
// address-modifying expression.
1998-
int64_t PointerOffsetInBits;
1999-
{
2000-
auto DestOffsetInBytes =
2001-
AssignRecord->getAddress()->getPointerOffsetFrom(Dest, DL);
2002-
if (!DestOffsetInBytes)
2003-
return false; // Can't calculate difference in addresses.
2004-
2005-
int64_t ExprOffsetInBytes;
2006-
if (!AssignRecord->getAddressExpression()->extractIfOffset(
2007-
ExprOffsetInBytes))
2008-
return false;
1878+
int64_t AddrOffsetInBytes;
1879+
SmallVector<uint64_t> PostOffsetOps; //< Unused.
1880+
// Bail if we can't find a constant offset (or none) in the expression.
1881+
if (!AssignRecord->getAddressExpression()->extractLeadingOffset(
1882+
AddrOffsetInBytes, PostOffsetOps))
1883+
return false;
1884+
int64_t AddrOffsetInBits = AddrOffsetInBytes * 8;
20091885

2010-
int64_t PointerOffsetInBytes = *DestOffsetInBytes + ExprOffsetInBytes;
2011-
PointerOffsetInBits = PointerOffsetInBytes * 8;
2012-
}
1886+
Value *Addr = AssignRecord->getAddress();
1887+
// FIXME: It may not always be zero.
1888+
int64_t BitExtractOffsetInBits = 0;
1889+
DIExpression::FragmentInfo VarFrag =
1890+
AssignRecord->getFragmentOrEntireVariable();
20131891

2014-
// Adjust the slice offset so that we go from describing the a slice
2015-
// of memory to a slice of the variable.
2016-
int64_t NewOffsetInBits =
2017-
SliceOffsetInBits + VarFrag.OffsetInBits - PointerOffsetInBits;
2018-
if (NewOffsetInBits < 0)
2019-
return false; // Fragment offsets can only be positive.
2020-
DIExpression::FragmentInfo SliceOfVariable(SliceSizeInBits, NewOffsetInBits);
2021-
// Intersect the variable slice with AssignRecord's fragment to trim it down
2022-
// to size.
2023-
DIExpression::FragmentInfo TrimmedSliceOfVariable =
2024-
DIExpression::FragmentInfo::intersect(SliceOfVariable, VarFrag);
2025-
if (TrimmedSliceOfVariable == VarFrag)
2026-
Result = std::nullopt;
2027-
else
2028-
Result = TrimmedSliceOfVariable;
2029-
return true;
1892+
int64_t OffsetFromLocationInBits; //< Unused.
1893+
return DIExpression::calculateFragmentIntersect(
1894+
DL, Dest, SliceOffsetInBits, SliceSizeInBits, Addr, AddrOffsetInBits,
1895+
BitExtractOffsetInBits, VarFrag, Result, OffsetFromLocationInBits);
20301896
}
1897+
1898+
/// FIXME: Remove this wrapper function and call
1899+
/// DIExpression::calculateFragmentIntersect directly.
20311900
bool at::calculateFragmentIntersect(
20321901
const DataLayout &DL, const Value *Dest, uint64_t SliceOffsetInBits,
20331902
uint64_t SliceSizeInBits, const DbgAssignIntrinsic *DbgAssign,
20341903
std::optional<DIExpression::FragmentInfo> &Result) {
1904+
20351905
return calculateFragmentIntersectImpl(DL, Dest, SliceOffsetInBits,
20361906
SliceSizeInBits, DbgAssign, Result);
20371907
}
1908+
1909+
/// FIXME: Remove this wrapper function and call
1910+
/// DIExpression::calculateFragmentIntersect directly.
20381911
bool at::calculateFragmentIntersect(
20391912
const DataLayout &DL, const Value *Dest, uint64_t SliceOffsetInBits,
20401913
uint64_t SliceSizeInBits, const DbgVariableRecord *DVRAssign,
20411914
std::optional<DIExpression::FragmentInfo> &Result) {
1915+
// FIXME: Remove this wrapper function and call
1916+
// DIExpression::calculateFragmentIntersect directly.
20421917
return calculateFragmentIntersectImpl(DL, Dest, SliceOffsetInBits,
20431918
SliceSizeInBits, DVRAssign, Result);
20441919
}

llvm/lib/IR/DebugInfoMetadata.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2052,6 +2052,75 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
20522052
return DIExpression::get(Expr->getContext(), Ops);
20532053
}
20542054

2055+
/// See declaration for more info.
2056+
bool DIExpression::calculateFragmentIntersect(
2057+
const DataLayout &DL, const Value *SliceStart, uint64_t SliceOffsetInBits,
2058+
uint64_t SliceSizeInBits, const Value *DbgPtr, int64_t DbgPtrOffsetInBits,
2059+
int64_t DbgExtractOffsetInBits, DIExpression::FragmentInfo VarFrag,
2060+
std::optional<DIExpression::FragmentInfo> &Result,
2061+
int64_t &OffsetFromLocationInBits) {
2062+
2063+
if (VarFrag.SizeInBits == 0)
2064+
return false; // Variable size is unknown.
2065+
2066+
// Difference between mem slice start and the dbg location start.
2067+
// 0 4 8 12 16 ...
2068+
// | |
2069+
// dbg location start
2070+
// |
2071+
// mem slice start
2072+
// Here MemStartRelToDbgStartInBits is 8. Note this can be negative.
2073+
int64_t MemStartRelToDbgStartInBits;
2074+
{
2075+
auto MemOffsetFromDbgInBytes = SliceStart->getPointerOffsetFrom(DbgPtr, DL);
2076+
if (!MemOffsetFromDbgInBytes)
2077+
return false; // Can't calculate difference in addresses.
2078+
// Difference between the pointers.
2079+
MemStartRelToDbgStartInBits = *MemOffsetFromDbgInBytes * 8;
2080+
// Add the difference of the offsets.
2081+
MemStartRelToDbgStartInBits +=
2082+
SliceOffsetInBits - (DbgPtrOffsetInBits + DbgExtractOffsetInBits);
2083+
}
2084+
2085+
// Out-param. Invert offset to get offset from debug location.
2086+
OffsetFromLocationInBits = -MemStartRelToDbgStartInBits;
2087+
2088+
// Check if the variable fragment sits outside (before) this memory slice.
2089+
int64_t MemEndRelToDbgStart = MemStartRelToDbgStartInBits + SliceSizeInBits;
2090+
if (MemEndRelToDbgStart < 0) {
2091+
Result = {0, 0}; // Out-param.
2092+
return true;
2093+
}
2094+
2095+
// Work towards creating SliceOfVariable which is the bits of the variable
2096+
// that the memory region covers.
2097+
// 0 4 8 12 16 ...
2098+
// | |
2099+
// dbg location start with VarFrag offset=32
2100+
// |
2101+
// mem slice start: SliceOfVariable offset=40
2102+
int64_t MemStartRelToFragInBits =
2103+
MemStartRelToDbgStartInBits + VarFrag.OffsetInBits;
2104+
int64_t MemEndRelToFragInBits = MemStartRelToFragInBits + SliceSizeInBits;
2105+
// If the memory region starts before the debug location the fragment
2106+
// offset would be negative, which we can't encode. Limit those to 0. This
2107+
// is fine becausethose bits necessarily don't overlap with the variable
2108+
// fragment.
2109+
int64_t MemFragStart = std::max<int64_t>(0, MemStartRelToFragInBits);
2110+
int64_t MemFragSize =
2111+
std::max<int64_t>(0, MemEndRelToFragInBits - MemFragStart);
2112+
DIExpression::FragmentInfo SliceOfVariable(MemFragSize, MemFragStart);
2113+
2114+
// Intersect the memory region fragment with the variable location fragment.
2115+
DIExpression::FragmentInfo TrimmedSliceOfVariable =
2116+
DIExpression::FragmentInfo::intersect(SliceOfVariable, VarFrag);
2117+
if (TrimmedSliceOfVariable == VarFrag)
2118+
Result = std::nullopt; // Out-param.
2119+
else
2120+
Result = TrimmedSliceOfVariable; // Out-param.
2121+
return true;
2122+
}
2123+
20552124
std::pair<DIExpression *, const ConstantInt *>
20562125
DIExpression::constantFold(const ConstantInt *CI) {
20572126
// Copy the APInt so we can modify it.

0 commit comments

Comments
 (0)