Skip to content

Commit 9fc4786

Browse files
committed
[SROA] Fix debug locations for variables with non-zero offsets
Fixes #61981 by adjusting variable location offsets (in the DIExpression) when splittign allocas. AKA Fix debug locations for C++ structured bindings in SROA. NOTE: There's still a bug in mem2reg which generates incorrect locations in some situations: if the variable fragment has an offset into the new (split) alloca, mem2reg will fail to convert that into a bit shift (the location contains a garbage offset). That's not addressed here. insertNewDbgInst - Now takes the address-expression and FragmentInfo as serperate parameters because unlike dbg_declares dbg_assigns want those to go to different places. dbg_assign records put the variable fragment info in the value expression only (whereas dbg_declare has only one expression so puts it there - ideally this information wouldn't live in DIExpression, but that's another issue). MigrateOne - Modified to correctly compute the necessary offsets and fragment adjustments. The previous implementation produced bogus locations for variables with non-zero offsets. The changes replace most of the body of this lambda, so it might be easier to review in a split-diff view and focus on the change as a whole than to compare it to the old implementation. This uses calculateFragmentIntersect and extractLeadingOffset added in previous patches in this series, and createOrReplaceFragment described below. createOrReplaceFragment - Similar to DIExpression::createFragmentExpression except for 3 important distinctions: 1. The new fragment isn't relative to an existing fragment. 2. There are no checks on the the operation types because it is assumed the location this expression is computing is not implicit (i.e., it's always safe to create a fragment because arithmetic operations apply to the address computation, not to an implicit value computation). 3. Existing extract_bits are modified independetly of fragment changes using \p BitExtractOffset. A change to the fragment offset or size may affect a bit extract. But a bit extract offset can change independently of the fragment dimensions. Returns the new expression, or nullptr if one couldn't be created. Ideally this is only used to signal that a bit-extract has become zero-sized (and thus the new debug record has no size and can be dropped), however, it fails for other reasons too - see the FIXME below. FIXME: To keep this patch NFC the function bails in situations that DIExpression::createFragmentExpression fails in.E.g. when fragment and bit extract sizes differ. These limitations can be removed in the future.
1 parent e1c71d8 commit 9fc4786

File tree

3 files changed

+521
-67
lines changed

3 files changed

+521
-67
lines changed

llvm/lib/Transforms/Scalar/SROA.cpp

Lines changed: 264 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4967,46 +4967,224 @@ AllocaInst *SROA::rewritePartition(AllocaInst &AI, AllocaSlices &AS,
49674967
return NewAI;
49684968
}
49694969

4970-
static void insertNewDbgInst(DIBuilder &DIB, DbgDeclareInst *Orig,
4971-
AllocaInst *NewAddr, DIExpression *NewFragmentExpr,
4972-
Instruction *BeforeInst) {
4973-
DIB.insertDeclare(NewAddr, Orig->getVariable(), NewFragmentExpr,
4970+
// There isn't a shared interface to get the "address" parts out of a
4971+
// dbg.declare and dbg.assign, so provide some wrappers now for
4972+
// both debug intrinsics and records.
4973+
const Value *getAddress(const DbgVariableIntrinsic *DVI) {
4974+
if (const auto *DAI = dyn_cast<DbgAssignIntrinsic>(DVI))
4975+
return DAI->getAddress();
4976+
return cast<DbgDeclareInst>(DVI)->getAddress();
4977+
}
4978+
const Value *getAddress(const DbgVariableRecord *DVR) {
4979+
assert(DVR->getType() == DbgVariableRecord::LocationType::Declare ||
4980+
DVR->getType() == DbgVariableRecord::LocationType::Assign);
4981+
return DVR->getAddress();
4982+
}
4983+
bool isKillAddress(const DbgVariableIntrinsic *DVI) {
4984+
if (const auto *DAI = dyn_cast<DbgAssignIntrinsic>(DVI))
4985+
return DAI->isKillAddress();
4986+
return cast<DbgDeclareInst>(DVI)->isKillLocation();
4987+
}
4988+
bool isKillAddress(const DbgVariableRecord *DVR) {
4989+
assert(DVR->getType() == DbgVariableRecord::LocationType::Declare ||
4990+
DVR->getType() == DbgVariableRecord::LocationType::Assign);
4991+
if (DVR->getType() == DbgVariableRecord::LocationType::Assign)
4992+
return DVR->isKillAddress();
4993+
return DVR->isKillLocation();
4994+
}
4995+
const DIExpression *getAddressExpression(const DbgVariableIntrinsic *DVI) {
4996+
if (const auto *DAI = dyn_cast<DbgAssignIntrinsic>(DVI))
4997+
return DAI->getAddressExpression();
4998+
return cast<DbgDeclareInst>(DVI)->getExpression();
4999+
}
5000+
const DIExpression *getAddressExpression(const DbgVariableRecord *DVR) {
5001+
assert(DVR->getType() == DbgVariableRecord::LocationType::Declare ||
5002+
DVR->getType() == DbgVariableRecord::LocationType::Assign);
5003+
if (DVR->getType() == DbgVariableRecord::LocationType::Assign)
5004+
return DVR->getAddressExpression();
5005+
return DVR->getExpression();
5006+
}
5007+
5008+
/// Similar to DIExpression::createFragmentExpression except for 3 important
5009+
/// distinctions:
5010+
/// 1. The new fragment isn't relative to an existing fragment.
5011+
/// 2. There are no checks on the the operation types because it is assumed
5012+
/// the location this expression is computing is not implicit (i.e., it's
5013+
/// always safe to create a fragment because arithmetic operations apply
5014+
/// to the address computation, not to an implicit value computation).
5015+
/// 3. Existing extract_bits are modified independetly of fragment changes
5016+
/// using \p BitExtractOffset. A change to the fragment offset or size
5017+
/// may affect a bit extract. But a bit extract offset can change
5018+
/// independently of the fragment dimensions.
5019+
///
5020+
/// Returns the new expression, or nullptr if one couldn't be created.
5021+
/// Ideally this is only used to signal that a bit-extract has become
5022+
/// zero-sized (and thus the new debug record has no size and can be
5023+
/// dropped), however, it fails for other reasons too - see the FIXME below.
5024+
///
5025+
/// FIXME: To keep the change that introduces this function NFC it bails
5026+
/// in some situations unecessarily, e.g. when fragment and bit extract
5027+
/// sizes differ.
5028+
static DIExpression *createOrReplaceFragment(const DIExpression *Expr,
5029+
DIExpression::FragmentInfo Frag,
5030+
int64_t BitExtractOffset) {
5031+
SmallVector<uint64_t, 8> Ops;
5032+
bool HasFragment = false;
5033+
bool HasBitExtract = false;
5034+
5035+
for (auto &Op : Expr->expr_ops()) {
5036+
if (Op.getOp() == dwarf::DW_OP_LLVM_fragment) {
5037+
HasFragment = true;
5038+
continue;
5039+
}
5040+
if (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_zext ||
5041+
Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext) {
5042+
HasBitExtract = true;
5043+
int64_t ExtractOffsetInBits = Op.getArg(0);
5044+
int64_t ExtractSizeInBits = Op.getArg(1);
5045+
5046+
// DIExpression::createFragmentExpression doesn't know how to handle
5047+
// a fragment that is smaller than the extract. Copy the behaviour
5048+
// (bail) to avoid non-NFC changes.
5049+
// FIXME: Don't do this.
5050+
if (Frag.SizeInBits < uint64_t(ExtractSizeInBits))
5051+
return nullptr;
5052+
5053+
assert(BitExtractOffset <= 0);
5054+
int64_t AdjustedOffset = ExtractOffsetInBits + BitExtractOffset;
5055+
5056+
// DIExpression::createFragmentExpression doesn't know what to do
5057+
// if the new extract starts "outside" the existing one. Copy the
5058+
// behaviour (bail) to avoid non-NFC changes.
5059+
// FIXME: Don't do this.
5060+
if (AdjustedOffset < 0)
5061+
return nullptr;
5062+
5063+
Ops.push_back(Op.getOp());
5064+
Ops.push_back(std::max<int64_t>(0, AdjustedOffset));
5065+
Ops.push_back(ExtractSizeInBits);
5066+
continue;
5067+
}
5068+
Op.appendToVector(Ops);
5069+
}
5070+
5071+
// Unsupported by createFragmentExpression, so don't support it here yet to
5072+
// preserve NFC-ness.
5073+
if (HasFragment && HasBitExtract)
5074+
return nullptr;
5075+
5076+
if (!HasBitExtract) {
5077+
Ops.push_back(dwarf::DW_OP_LLVM_fragment);
5078+
Ops.push_back(Frag.OffsetInBits);
5079+
Ops.push_back(Frag.SizeInBits);
5080+
}
5081+
return DIExpression::get(Expr->getContext(), Ops);
5082+
}
5083+
5084+
/// Insert a new dbg.declare.
5085+
/// \p Orig Original to copy debug loc and variable from.
5086+
/// \p NewAddr Location's new base address.
5087+
/// \p NewAddrExpr New expression to apply to address.
5088+
/// \p BeforeInst Insert position.
5089+
/// \p NewFragment New fragment (absolute, non-relative).
5090+
/// \p BitExtractAdjustment Offset to apply to any extract_bits op.
5091+
static void
5092+
insertNewDbgInst(DIBuilder &DIB, DbgDeclareInst *Orig, AllocaInst *NewAddr,
5093+
DIExpression *NewAddrExpr, Instruction *BeforeInst,
5094+
std::optional<DIExpression::FragmentInfo> NewFragment,
5095+
int64_t BitExtractAdjustment) {
5096+
if (NewFragment)
5097+
NewAddrExpr = createOrReplaceFragment(NewAddrExpr, *NewFragment,
5098+
BitExtractAdjustment);
5099+
if (!NewAddrExpr)
5100+
return;
5101+
5102+
DIB.insertDeclare(NewAddr, Orig->getVariable(), NewAddrExpr,
49745103
Orig->getDebugLoc(), BeforeInst);
49755104
}
4976-
static void insertNewDbgInst(DIBuilder &DIB, DbgAssignIntrinsic *Orig,
4977-
AllocaInst *NewAddr, DIExpression *NewFragmentExpr,
4978-
Instruction *BeforeInst) {
5105+
5106+
/// Insert a new dbg.assign.
5107+
/// \p Orig Original to copy debug loc, variable, value and value expression
5108+
/// from.
5109+
/// \p NewAddr Location's new base address.
5110+
/// \p NewAddrExpr New expression to apply to address.
5111+
/// \p BeforeInst Insert position.
5112+
/// \p NewFragment New fragment (absolute, non-relative).
5113+
/// \p BitExtractAdjustment Offset to apply to any extract_bits op.
5114+
static void
5115+
insertNewDbgInst(DIBuilder &DIB, DbgAssignIntrinsic *Orig, AllocaInst *NewAddr,
5116+
DIExpression *NewAddrExpr, Instruction *BeforeInst,
5117+
std::optional<DIExpression::FragmentInfo> NewFragment,
5118+
int64_t BitExtractAdjustment) {
49795119
(void)BeforeInst;
5120+
5121+
// A dbg.assign puts fragment info in the value expression only. The address
5122+
// expression has already been built: NewAddrExpr.
5123+
DIExpression *NewFragmentExpr = Orig->getExpression();
5124+
if (NewFragment)
5125+
NewFragmentExpr = createOrReplaceFragment(NewFragmentExpr, *NewFragment,
5126+
BitExtractAdjustment);
5127+
if (!NewFragmentExpr)
5128+
return;
5129+
5130+
// Apply a DIAssignID to the store if it doesn't already have it.
49805131
if (!NewAddr->hasMetadata(LLVMContext::MD_DIAssignID)) {
49815132
NewAddr->setMetadata(LLVMContext::MD_DIAssignID,
49825133
DIAssignID::getDistinct(NewAddr->getContext()));
49835134
}
5135+
49845136
Instruction *NewAssign =
49855137
DIB.insertDbgAssign(NewAddr, Orig->getValue(), Orig->getVariable(),
4986-
NewFragmentExpr, NewAddr,
4987-
Orig->getAddressExpression(), Orig->getDebugLoc())
5138+
NewFragmentExpr, NewAddr, NewAddrExpr,
5139+
Orig->getDebugLoc())
49885140
.get<Instruction *>();
49895141
LLVM_DEBUG(dbgs() << "Created new assign intrinsic: " << *NewAssign << "\n");
49905142
(void)NewAssign;
49915143
}
4992-
static void insertNewDbgInst(DIBuilder &DIB, DbgVariableRecord *Orig,
4993-
AllocaInst *NewAddr, DIExpression *NewFragmentExpr,
4994-
Instruction *BeforeInst) {
5144+
5145+
/// Insert a new DbgRecord.
5146+
/// \p Orig Original to copy record type, debug loc and variable from, and
5147+
/// additionally value and value expression for dbg_assign records.
5148+
/// \p NewAddr Location's new base address.
5149+
/// \p NewAddrExpr New expression to apply to address.
5150+
/// \p BeforeInst Insert position.
5151+
/// \p NewFragment New fragment (absolute, non-relative).
5152+
/// \p BitExtractAdjustment Offset to apply to any extract_bits op.
5153+
static void
5154+
insertNewDbgInst(DIBuilder &DIB, DbgVariableRecord *Orig, AllocaInst *NewAddr,
5155+
DIExpression *NewAddrExpr, Instruction *BeforeInst,
5156+
std::optional<DIExpression::FragmentInfo> NewFragment,
5157+
int64_t BitExtractAdjustment) {
49955158
(void)DIB;
5159+
5160+
// A dbg_assign puts fragment info in the value expression only. The address
5161+
// expression has already been built: NewAddrExpr. A dbg_declare puts the
5162+
// new fragment info into NewAddrExpr (as it only has one expression).
5163+
DIExpression *NewFragmentExpr =
5164+
Orig->isDbgAssign() ? Orig->getExpression() : NewAddrExpr;
5165+
if (NewFragment)
5166+
NewFragmentExpr = createOrReplaceFragment(NewFragmentExpr, *NewFragment,
5167+
BitExtractAdjustment);
5168+
if (!NewFragmentExpr)
5169+
return;
5170+
49965171
if (Orig->isDbgDeclare()) {
49975172
DbgVariableRecord *DVR = DbgVariableRecord::createDVRDeclare(
49985173
NewAddr, Orig->getVariable(), NewFragmentExpr, Orig->getDebugLoc());
49995174
BeforeInst->getParent()->insertDbgRecordBefore(DVR,
50005175
BeforeInst->getIterator());
50015176
return;
50025177
}
5178+
5179+
// Apply a DIAssignID to the store if it doesn't already have it.
50035180
if (!NewAddr->hasMetadata(LLVMContext::MD_DIAssignID)) {
50045181
NewAddr->setMetadata(LLVMContext::MD_DIAssignID,
50055182
DIAssignID::getDistinct(NewAddr->getContext()));
50065183
}
5184+
50075185
DbgVariableRecord *NewAssign = DbgVariableRecord::createLinkedDVRAssign(
50085186
NewAddr, Orig->getValue(), Orig->getVariable(), NewFragmentExpr, NewAddr,
5009-
Orig->getAddressExpression(), Orig->getDebugLoc());
5187+
NewAddrExpr, Orig->getDebugLoc());
50105188
LLVM_DEBUG(dbgs() << "Created new DVRAssign: " << *NewAssign << "\n");
50115189
(void)NewAssign;
50125190
}
@@ -5019,7 +5197,7 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
50195197

50205198
unsigned NumPartitions = 0;
50215199
bool Changed = false;
5022-
const DataLayout &DL = AI.getDataLayout();
5200+
const DataLayout &DL = AI.getModule()->getDataLayout();
50235201

50245202
// First try to pre-split loads and stores.
50255203
Changed |= presplitLoadsAndStores(AI, AS);
@@ -5110,57 +5288,79 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
51105288
NumAllocaPartitions += NumPartitions;
51115289
MaxPartitionsPerAlloca.updateMax(NumPartitions);
51125290

5113-
// Migrate debug information from the old alloca to the new alloca(s)
5114-
// and the individual partitions.
51155291
auto MigrateOne = [&](auto *DbgVariable) {
5116-
auto *Expr = DbgVariable->getExpression();
5117-
DIBuilder DIB(*AI.getModule(), /*AllowUnresolved*/ false);
5118-
uint64_t AllocaSize =
5119-
DL.getTypeSizeInBits(AI.getAllocatedType()).getFixedValue();
5120-
for (auto Fragment : Fragments) {
5121-
// Create a fragment expression describing the new partition or reuse AI's
5122-
// expression if there is only one partition.
5123-
auto *FragmentExpr = Expr;
5124-
if (Fragment.Size < AllocaSize || Expr->isFragment()) {
5125-
// If this alloca is already a scalar replacement of a larger aggregate,
5126-
// Fragment.Offset describes the offset inside the scalar.
5127-
auto ExprFragment = Expr->getFragmentInfo();
5128-
uint64_t Offset = ExprFragment ? ExprFragment->OffsetInBits : 0;
5129-
uint64_t Start = Offset + Fragment.Offset;
5130-
uint64_t Size = Fragment.Size;
5131-
if (ExprFragment) {
5132-
uint64_t AbsEnd =
5133-
ExprFragment->OffsetInBits + ExprFragment->SizeInBits;
5134-
if (Start >= AbsEnd) {
5135-
// No need to describe a SROAed padding.
5136-
continue;
5137-
}
5138-
Size = std::min(Size, AbsEnd - Start);
5139-
}
5140-
// The new, smaller fragment is stenciled out from the old fragment.
5141-
if (auto OrigFragment = FragmentExpr->getFragmentInfo()) {
5142-
assert(Start >= OrigFragment->OffsetInBits &&
5143-
"new fragment is outside of original fragment");
5144-
Start -= OrigFragment->OffsetInBits;
5145-
}
5292+
// Can't overlap with undef memory.
5293+
if (isKillAddress(DbgVariable))
5294+
return;
51465295

5147-
// The alloca may be larger than the variable.
5148-
auto VarSize = DbgVariable->getVariable()->getSizeInBits();
5149-
if (VarSize) {
5150-
if (Size > *VarSize)
5151-
Size = *VarSize;
5152-
if (Size == 0 || Start + Size > *VarSize)
5153-
continue;
5154-
}
5296+
const Value *DbgPtr = getAddress(DbgVariable);
5297+
DIExpression::FragmentInfo VarFrag =
5298+
DbgVariable->getFragmentOrEntireVariable();
5299+
// Get the address expression constant offset if one exists and the ops
5300+
// that come after it.
5301+
int64_t CurrentExprOffsetInBytes = 0;
5302+
SmallVector<uint64_t> PostOffsetOps;
5303+
if (!getAddressExpression(DbgVariable)
5304+
->extractLeadingOffset(CurrentExprOffsetInBytes, PostOffsetOps))
5305+
return; // Couldn't interpret this DIExpression - drop the var.
5306+
5307+
// Offset defined by a DW_OP_LLVM_extract_bits_[sz]ext.
5308+
int64_t ExtractOffsetInBits = 0;
5309+
for (auto Op : getAddressExpression(DbgVariable)->expr_ops()) {
5310+
if (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_zext ||
5311+
Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext) {
5312+
ExtractOffsetInBits = Op.getArg(0);
5313+
break;
5314+
}
5315+
}
51555316

5156-
// Avoid creating a fragment expression that covers the entire variable.
5157-
if (!VarSize || *VarSize != Size) {
5158-
if (auto E =
5159-
DIExpression::createFragmentExpression(Expr, Start, Size))
5160-
FragmentExpr = *E;
5161-
else
5162-
continue;
5163-
}
5317+
DIBuilder DIB(*AI.getModule(), /*AllowUnresolved*/ false);
5318+
for (auto Fragment : Fragments) {
5319+
int64_t OffsetFromLocationInBits;
5320+
std::optional<DIExpression::FragmentInfo> NewDbgFragment;
5321+
// Find the variable fragment that the new alloca slice covers.
5322+
// Drop debug info for this variable fragment if we can't compute an
5323+
// intersect between it and the alloca slice.
5324+
if (!DIExpression::calculateFragmentIntersect(
5325+
DL, &AI, Fragment.Offset, Fragment.Size, DbgPtr,
5326+
CurrentExprOffsetInBytes * 8, ExtractOffsetInBits, VarFrag,
5327+
NewDbgFragment, OffsetFromLocationInBits))
5328+
continue; // Do not migrate this fragment to this slice.
5329+
5330+
// Zero sized fragment indicates there's no intersect between the variable
5331+
// fragment and the alloca slice. Skip this slice for this variable
5332+
// fragment.
5333+
if (NewDbgFragment && !NewDbgFragment->SizeInBits)
5334+
continue; // Do not migrate this fragment to this slice.
5335+
5336+
// No fragment indicates DbgVariable's variable or fragment exactly
5337+
// overlaps the slice; copy its fragment (or nullopt if there isn't one).
5338+
if (!NewDbgFragment)
5339+
NewDbgFragment = DbgVariable->getFragment();
5340+
5341+
// Reduce the new expression offset by the bit-extract offset since
5342+
// we'll be keeping that.
5343+
int64_t OffestFromNewAllocaInBits =
5344+
OffsetFromLocationInBits - ExtractOffsetInBits;
5345+
// We need to adjust an existing bit extract if the offset expression
5346+
// can't eat the slack (i.e., if the new offset would be negative).
5347+
int64_t BitExtractOffset =
5348+
std::min<int64_t>(0, OffestFromNewAllocaInBits);
5349+
// The magnitude of a negative value indicates the number of bits into
5350+
// the existing variable fragment that the memory region begins. The new
5351+
// variable fragment already excludes those bits - the new DbgPtr offset
5352+
// only needs to be applied if it's positive.
5353+
OffestFromNewAllocaInBits =
5354+
std::max(int64_t(0), OffestFromNewAllocaInBits);
5355+
5356+
// Rebuild the expression:
5357+
// {Offset(OffestFromNewAllocaInBits), PostOffsetOps, NewDbgFragment}
5358+
// Add NewDbgFragment later, because dbg.assigns don't want it in the
5359+
// address expression but the value expression instead.
5360+
DIExpression *NewExpr = DIExpression::get(AI.getContext(), PostOffsetOps);
5361+
if (OffestFromNewAllocaInBits > 0) {
5362+
int64_t OffsetInBytes = (OffestFromNewAllocaInBits + 7) / 8;
5363+
NewExpr = DIExpression::prepend(NewExpr, /*flags=*/0, OffsetInBytes);
51645364
}
51655365

51665366
// Remove any existing intrinsics on the new alloca describing
@@ -5177,7 +5377,8 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
51775377
for_each(findDbgDeclares(Fragment.Alloca), RemoveOne);
51785378
for_each(findDVRDeclares(Fragment.Alloca), RemoveOne);
51795379

5180-
insertNewDbgInst(DIB, DbgVariable, Fragment.Alloca, FragmentExpr, &AI);
5380+
insertNewDbgInst(DIB, DbgVariable, Fragment.Alloca, NewExpr, &AI,
5381+
NewDbgFragment, BitExtractOffset);
51815382
}
51825383
};
51835384

llvm/test/DebugInfo/Generic/assignment-tracking/sroa/var-sized-fragment.ll

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@
1919
;; return a;
2020
;; }
2121

22-
;; FIXME: Variable 'b' gets an incorrect location (value and expression) - see
23-
;; llvm.org/PR61981. This check just ensures that no fragment info is added to
24-
;; the dbg.value.
25-
; CHECK: #dbg_value(i32 %.sroa.0.0.extract.trunc, ![[B:[0-9]+]], !DIExpression(DW_OP_plus_uconst, 4),
22+
; CHECK: #dbg_value(i32 %.sroa.2.0.extract.trunc, ![[B:[0-9]+]], !DIExpression(),
2623
; CHECK: #dbg_value(i32 %.sroa.0.0.extract.trunc, ![[A:[0-9]+]], !DIExpression(),
2724
; CHECK: ![[A]] = !DILocalVariable(name: "a",
2825
; CHECK: ![[B]] = !DILocalVariable(name: "b",

0 commit comments

Comments
 (0)