Skip to content

Commit d9d9014

Browse files
OCHyamsyuxuanchen1997
authored andcommitted
[SROA] Fix debug locations for variables with non-zero offsets (#97750)
Summary: Fixes issue #61981 by adjusting variable location offsets (in the DIExpression) when splitting allocas. Patch [4/4] to fix 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 separate 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 the scope of this change focused on non-bitfield structured bindings the function bails in situations that DIExpression::createFragmentExpression fails. E.g. when fragment and bit extract sizes differ. These limitations can be removed in the future. Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60250875
1 parent af0044c commit d9d9014

File tree

3 files changed

+551
-65
lines changed

3 files changed

+551
-65
lines changed

llvm/lib/Transforms/Scalar/SROA.cpp

+275-61
Original file line numberDiff line numberDiff line change
@@ -4967,46 +4967,235 @@ 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+
4979+
const Value *getAddress(const DbgVariableRecord *DVR) {
4980+
assert(DVR->getType() == DbgVariableRecord::LocationType::Declare ||
4981+
DVR->getType() == DbgVariableRecord::LocationType::Assign);
4982+
return DVR->getAddress();
4983+
}
4984+
4985+
bool isKillAddress(const DbgVariableIntrinsic *DVI) {
4986+
if (const auto *DAI = dyn_cast<DbgAssignIntrinsic>(DVI))
4987+
return DAI->isKillAddress();
4988+
return cast<DbgDeclareInst>(DVI)->isKillLocation();
4989+
}
4990+
4991+
bool isKillAddress(const DbgVariableRecord *DVR) {
4992+
assert(DVR->getType() == DbgVariableRecord::LocationType::Declare ||
4993+
DVR->getType() == DbgVariableRecord::LocationType::Assign);
4994+
if (DVR->getType() == DbgVariableRecord::LocationType::Assign)
4995+
return DVR->isKillAddress();
4996+
return DVR->isKillLocation();
4997+
}
4998+
4999+
const DIExpression *getAddressExpression(const DbgVariableIntrinsic *DVI) {
5000+
if (const auto *DAI = dyn_cast<DbgAssignIntrinsic>(DVI))
5001+
return DAI->getAddressExpression();
5002+
return cast<DbgDeclareInst>(DVI)->getExpression();
5003+
}
5004+
5005+
const DIExpression *getAddressExpression(const DbgVariableRecord *DVR) {
5006+
assert(DVR->getType() == DbgVariableRecord::LocationType::Declare ||
5007+
DVR->getType() == DbgVariableRecord::LocationType::Assign);
5008+
if (DVR->getType() == DbgVariableRecord::LocationType::Assign)
5009+
return DVR->getAddressExpression();
5010+
return DVR->getExpression();
5011+
}
5012+
5013+
/// Create or replace an existing fragment in a DIExpression with \p Frag.
5014+
/// If the expression already contains a DW_OP_LLVM_extract_bits_[sz]ext
5015+
/// operation, add \p BitExtractOffset to the offset part.
5016+
///
5017+
/// Returns the new expression, or nullptr if this fails (see details below).
5018+
///
5019+
/// This function is similar to DIExpression::createFragmentExpression except
5020+
/// for 3 important distinctions:
5021+
/// 1. The new fragment isn't relative to an existing fragment.
5022+
/// 2. It assumes the computed location is a memory location. This means we
5023+
/// don't need to perform checks that creating the fragment preserves the
5024+
/// expression semantics.
5025+
/// 3. Existing extract_bits are modified independently of fragment changes
5026+
/// using \p BitExtractOffset. A change to the fragment offset or size
5027+
/// may affect a bit extract. But a bit extract offset can change
5028+
/// independently of the fragment dimensions.
5029+
///
5030+
/// Returns the new expression, or nullptr if one couldn't be created.
5031+
/// Ideally this is only used to signal that a bit-extract has become
5032+
/// zero-sized (and thus the new debug record has no size and can be
5033+
/// dropped), however, it fails for other reasons too - see the FIXME below.
5034+
///
5035+
/// FIXME: To keep the change that introduces this function NFC it bails
5036+
/// in some situations unecessarily, e.g. when fragment and bit extract
5037+
/// sizes differ.
5038+
static DIExpression *createOrReplaceFragment(const DIExpression *Expr,
5039+
DIExpression::FragmentInfo Frag,
5040+
int64_t BitExtractOffset) {
5041+
SmallVector<uint64_t, 8> Ops;
5042+
bool HasFragment = false;
5043+
bool HasBitExtract = false;
5044+
5045+
for (auto &Op : Expr->expr_ops()) {
5046+
if (Op.getOp() == dwarf::DW_OP_LLVM_fragment) {
5047+
HasFragment = true;
5048+
continue;
5049+
}
5050+
if (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_zext ||
5051+
Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext) {
5052+
HasBitExtract = true;
5053+
int64_t ExtractOffsetInBits = Op.getArg(0);
5054+
int64_t ExtractSizeInBits = Op.getArg(1);
5055+
5056+
// DIExpression::createFragmentExpression doesn't know how to handle
5057+
// a fragment that is smaller than the extract. Copy the behaviour
5058+
// (bail) to avoid non-NFC changes.
5059+
// FIXME: Don't do this.
5060+
if (Frag.SizeInBits < uint64_t(ExtractSizeInBits))
5061+
return nullptr;
5062+
5063+
assert(BitExtractOffset <= 0);
5064+
int64_t AdjustedOffset = ExtractOffsetInBits + BitExtractOffset;
5065+
5066+
// DIExpression::createFragmentExpression doesn't know what to do
5067+
// if the new extract starts "outside" the existing one. Copy the
5068+
// behaviour (bail) to avoid non-NFC changes.
5069+
// FIXME: Don't do this.
5070+
if (AdjustedOffset < 0)
5071+
return nullptr;
5072+
5073+
Ops.push_back(Op.getOp());
5074+
Ops.push_back(std::max<int64_t>(0, AdjustedOffset));
5075+
Ops.push_back(ExtractSizeInBits);
5076+
continue;
5077+
}
5078+
Op.appendToVector(Ops);
5079+
}
5080+
5081+
// Unsupported by createFragmentExpression, so don't support it here yet to
5082+
// preserve NFC-ness.
5083+
if (HasFragment && HasBitExtract)
5084+
return nullptr;
5085+
5086+
if (!HasBitExtract) {
5087+
Ops.push_back(dwarf::DW_OP_LLVM_fragment);
5088+
Ops.push_back(Frag.OffsetInBits);
5089+
Ops.push_back(Frag.SizeInBits);
5090+
}
5091+
return DIExpression::get(Expr->getContext(), Ops);
5092+
}
5093+
5094+
/// Insert a new dbg.declare.
5095+
/// \p Orig Original to copy debug loc and variable from.
5096+
/// \p NewAddr Location's new base address.
5097+
/// \p NewAddrExpr New expression to apply to address.
5098+
/// \p BeforeInst Insert position.
5099+
/// \p NewFragment New fragment (absolute, non-relative).
5100+
/// \p BitExtractAdjustment Offset to apply to any extract_bits op.
5101+
static void
5102+
insertNewDbgInst(DIBuilder &DIB, DbgDeclareInst *Orig, AllocaInst *NewAddr,
5103+
DIExpression *NewAddrExpr, Instruction *BeforeInst,
5104+
std::optional<DIExpression::FragmentInfo> NewFragment,
5105+
int64_t BitExtractAdjustment) {
5106+
if (NewFragment)
5107+
NewAddrExpr = createOrReplaceFragment(NewAddrExpr, *NewFragment,
5108+
BitExtractAdjustment);
5109+
if (!NewAddrExpr)
5110+
return;
5111+
5112+
DIB.insertDeclare(NewAddr, Orig->getVariable(), NewAddrExpr,
49745113
Orig->getDebugLoc(), BeforeInst);
49755114
}
4976-
static void insertNewDbgInst(DIBuilder &DIB, DbgAssignIntrinsic *Orig,
4977-
AllocaInst *NewAddr, DIExpression *NewFragmentExpr,
4978-
Instruction *BeforeInst) {
5115+
5116+
/// Insert a new dbg.assign.
5117+
/// \p Orig Original to copy debug loc, variable, value and value expression
5118+
/// from.
5119+
/// \p NewAddr Location's new base address.
5120+
/// \p NewAddrExpr New expression to apply to address.
5121+
/// \p BeforeInst Insert position.
5122+
/// \p NewFragment New fragment (absolute, non-relative).
5123+
/// \p BitExtractAdjustment Offset to apply to any extract_bits op.
5124+
static void
5125+
insertNewDbgInst(DIBuilder &DIB, DbgAssignIntrinsic *Orig, AllocaInst *NewAddr,
5126+
DIExpression *NewAddrExpr, Instruction *BeforeInst,
5127+
std::optional<DIExpression::FragmentInfo> NewFragment,
5128+
int64_t BitExtractAdjustment) {
5129+
// DIBuilder::insertDbgAssign will insert the #dbg_assign after NewAddr.
49795130
(void)BeforeInst;
5131+
5132+
// A dbg.assign puts fragment info in the value expression only. The address
5133+
// expression has already been built: NewAddrExpr.
5134+
DIExpression *NewFragmentExpr = Orig->getExpression();
5135+
if (NewFragment)
5136+
NewFragmentExpr = createOrReplaceFragment(NewFragmentExpr, *NewFragment,
5137+
BitExtractAdjustment);
5138+
if (!NewFragmentExpr)
5139+
return;
5140+
5141+
// Apply a DIAssignID to the store if it doesn't already have it.
49805142
if (!NewAddr->hasMetadata(LLVMContext::MD_DIAssignID)) {
49815143
NewAddr->setMetadata(LLVMContext::MD_DIAssignID,
49825144
DIAssignID::getDistinct(NewAddr->getContext()));
49835145
}
5146+
49845147
Instruction *NewAssign =
49855148
DIB.insertDbgAssign(NewAddr, Orig->getValue(), Orig->getVariable(),
4986-
NewFragmentExpr, NewAddr,
4987-
Orig->getAddressExpression(), Orig->getDebugLoc())
5149+
NewFragmentExpr, NewAddr, NewAddrExpr,
5150+
Orig->getDebugLoc())
49885151
.get<Instruction *>();
49895152
LLVM_DEBUG(dbgs() << "Created new assign intrinsic: " << *NewAssign << "\n");
49905153
(void)NewAssign;
49915154
}
4992-
static void insertNewDbgInst(DIBuilder &DIB, DbgVariableRecord *Orig,
4993-
AllocaInst *NewAddr, DIExpression *NewFragmentExpr,
4994-
Instruction *BeforeInst) {
5155+
5156+
/// Insert a new DbgRecord.
5157+
/// \p Orig Original to copy record type, debug loc and variable from, and
5158+
/// additionally value and value expression for dbg_assign records.
5159+
/// \p NewAddr Location's new base address.
5160+
/// \p NewAddrExpr New expression to apply to address.
5161+
/// \p BeforeInst Insert position.
5162+
/// \p NewFragment New fragment (absolute, non-relative).
5163+
/// \p BitExtractAdjustment Offset to apply to any extract_bits op.
5164+
static void
5165+
insertNewDbgInst(DIBuilder &DIB, DbgVariableRecord *Orig, AllocaInst *NewAddr,
5166+
DIExpression *NewAddrExpr, Instruction *BeforeInst,
5167+
std::optional<DIExpression::FragmentInfo> NewFragment,
5168+
int64_t BitExtractAdjustment) {
49955169
(void)DIB;
5170+
5171+
// A dbg_assign puts fragment info in the value expression only. The address
5172+
// expression has already been built: NewAddrExpr. A dbg_declare puts the
5173+
// new fragment info into NewAddrExpr (as it only has one expression).
5174+
DIExpression *NewFragmentExpr =
5175+
Orig->isDbgAssign() ? Orig->getExpression() : NewAddrExpr;
5176+
if (NewFragment)
5177+
NewFragmentExpr = createOrReplaceFragment(NewFragmentExpr, *NewFragment,
5178+
BitExtractAdjustment);
5179+
if (!NewFragmentExpr)
5180+
return;
5181+
49965182
if (Orig->isDbgDeclare()) {
49975183
DbgVariableRecord *DVR = DbgVariableRecord::createDVRDeclare(
49985184
NewAddr, Orig->getVariable(), NewFragmentExpr, Orig->getDebugLoc());
49995185
BeforeInst->getParent()->insertDbgRecordBefore(DVR,
50005186
BeforeInst->getIterator());
50015187
return;
50025188
}
5189+
5190+
// Apply a DIAssignID to the store if it doesn't already have it.
50035191
if (!NewAddr->hasMetadata(LLVMContext::MD_DIAssignID)) {
50045192
NewAddr->setMetadata(LLVMContext::MD_DIAssignID,
50055193
DIAssignID::getDistinct(NewAddr->getContext()));
50065194
}
5195+
50075196
DbgVariableRecord *NewAssign = DbgVariableRecord::createLinkedDVRAssign(
50085197
NewAddr, Orig->getValue(), Orig->getVariable(), NewFragmentExpr, NewAddr,
5009-
Orig->getAddressExpression(), Orig->getDebugLoc());
5198+
NewAddrExpr, Orig->getDebugLoc());
50105199
LLVM_DEBUG(dbgs() << "Created new DVRAssign: " << *NewAssign << "\n");
50115200
(void)NewAssign;
50125201
}
@@ -5019,7 +5208,7 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
50195208

50205209
unsigned NumPartitions = 0;
50215210
bool Changed = false;
5022-
const DataLayout &DL = AI.getDataLayout();
5211+
const DataLayout &DL = AI.getModule()->getDataLayout();
50235212

50245213
// First try to pre-split loads and stores.
50255214
Changed |= presplitLoadsAndStores(AI, AS);
@@ -5113,54 +5302,78 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
51135302
// Migrate debug information from the old alloca to the new alloca(s)
51145303
// and the individual partitions.
51155304
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-
}
5305+
// Can't overlap with undef memory.
5306+
if (isKillAddress(DbgVariable))
5307+
return;
51465308

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-
}
5309+
const Value *DbgPtr = getAddress(DbgVariable);
5310+
DIExpression::FragmentInfo VarFrag =
5311+
DbgVariable->getFragmentOrEntireVariable();
5312+
// Get the address expression constant offset if one exists and the ops
5313+
// that come after it.
5314+
int64_t CurrentExprOffsetInBytes = 0;
5315+
SmallVector<uint64_t> PostOffsetOps;
5316+
if (!getAddressExpression(DbgVariable)
5317+
->extractLeadingOffset(CurrentExprOffsetInBytes, PostOffsetOps))
5318+
return; // Couldn't interpret this DIExpression - drop the var.
5319+
5320+
// Offset defined by a DW_OP_LLVM_extract_bits_[sz]ext.
5321+
int64_t ExtractOffsetInBits = 0;
5322+
for (auto Op : getAddressExpression(DbgVariable)->expr_ops()) {
5323+
if (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_zext ||
5324+
Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext) {
5325+
ExtractOffsetInBits = Op.getArg(0);
5326+
break;
5327+
}
5328+
}
51555329

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-
}
5330+
DIBuilder DIB(*AI.getModule(), /*AllowUnresolved*/ false);
5331+
for (auto Fragment : Fragments) {
5332+
int64_t OffsetFromLocationInBits;
5333+
std::optional<DIExpression::FragmentInfo> NewDbgFragment;
5334+
// Find the variable fragment that the new alloca slice covers.
5335+
// Drop debug info for this variable fragment if we can't compute an
5336+
// intersect between it and the alloca slice.
5337+
if (!DIExpression::calculateFragmentIntersect(
5338+
DL, &AI, Fragment.Offset, Fragment.Size, DbgPtr,
5339+
CurrentExprOffsetInBytes * 8, ExtractOffsetInBits, VarFrag,
5340+
NewDbgFragment, OffsetFromLocationInBits))
5341+
continue; // Do not migrate this fragment to this slice.
5342+
5343+
// Zero sized fragment indicates there's no intersect between the variable
5344+
// fragment and the alloca slice. Skip this slice for this variable
5345+
// fragment.
5346+
if (NewDbgFragment && !NewDbgFragment->SizeInBits)
5347+
continue; // Do not migrate this fragment to this slice.
5348+
5349+
// No fragment indicates DbgVariable's variable or fragment exactly
5350+
// overlaps the slice; copy its fragment (or nullopt if there isn't one).
5351+
if (!NewDbgFragment)
5352+
NewDbgFragment = DbgVariable->getFragment();
5353+
5354+
// Reduce the new expression offset by the bit-extract offset since
5355+
// we'll be keeping that.
5356+
int64_t OffestFromNewAllocaInBits =
5357+
OffsetFromLocationInBits - ExtractOffsetInBits;
5358+
// We need to adjust an existing bit extract if the offset expression
5359+
// can't eat the slack (i.e., if the new offset would be negative).
5360+
int64_t BitExtractOffset =
5361+
std::min<int64_t>(0, OffestFromNewAllocaInBits);
5362+
// The magnitude of a negative value indicates the number of bits into
5363+
// the existing variable fragment that the memory region begins. The new
5364+
// variable fragment already excludes those bits - the new DbgPtr offset
5365+
// only needs to be applied if it's positive.
5366+
OffestFromNewAllocaInBits =
5367+
std::max(int64_t(0), OffestFromNewAllocaInBits);
5368+
5369+
// Rebuild the expression:
5370+
// {Offset(OffestFromNewAllocaInBits), PostOffsetOps, NewDbgFragment}
5371+
// Add NewDbgFragment later, because dbg.assigns don't want it in the
5372+
// address expression but the value expression instead.
5373+
DIExpression *NewExpr = DIExpression::get(AI.getContext(), PostOffsetOps);
5374+
if (OffestFromNewAllocaInBits > 0) {
5375+
int64_t OffsetInBytes = (OffestFromNewAllocaInBits + 7) / 8;
5376+
NewExpr = DIExpression::prepend(NewExpr, /*flags=*/0, OffsetInBytes);
51645377
}
51655378

51665379
// Remove any existing intrinsics on the new alloca describing
@@ -5177,7 +5390,8 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
51775390
for_each(findDbgDeclares(Fragment.Alloca), RemoveOne);
51785391
for_each(findDVRDeclares(Fragment.Alloca), RemoveOne);
51795392

5180-
insertNewDbgInst(DIB, DbgVariable, Fragment.Alloca, FragmentExpr, &AI);
5393+
insertNewDbgInst(DIB, DbgVariable, Fragment.Alloca, NewExpr, &AI,
5394+
NewDbgFragment, BitExtractOffset);
51815395
}
51825396
};
51835397

0 commit comments

Comments
 (0)