[llvm] [SROA] Fix debug locations for variables with non-zero offsets (PR #97750)
Jeremy Morse via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 15 10:42:46 PDT 2024
================
@@ -4967,46 +4967,224 @@ AllocaInst *SROA::rewritePartition(AllocaInst &AI, AllocaSlices &AS,
return NewAI;
}
-static void insertNewDbgInst(DIBuilder &DIB, DbgDeclareInst *Orig,
- AllocaInst *NewAddr, DIExpression *NewFragmentExpr,
- Instruction *BeforeInst) {
- DIB.insertDeclare(NewAddr, Orig->getVariable(), NewFragmentExpr,
+// There isn't a shared interface to get the "address" parts out of a
+// dbg.declare and dbg.assign, so provide some wrappers now for
+// both debug intrinsics and records.
+const Value *getAddress(const DbgVariableIntrinsic *DVI) {
+ if (const auto *DAI = dyn_cast<DbgAssignIntrinsic>(DVI))
+ return DAI->getAddress();
+ return cast<DbgDeclareInst>(DVI)->getAddress();
+}
+const Value *getAddress(const DbgVariableRecord *DVR) {
+ assert(DVR->getType() == DbgVariableRecord::LocationType::Declare ||
+ DVR->getType() == DbgVariableRecord::LocationType::Assign);
+ return DVR->getAddress();
+}
+bool isKillAddress(const DbgVariableIntrinsic *DVI) {
+ if (const auto *DAI = dyn_cast<DbgAssignIntrinsic>(DVI))
+ return DAI->isKillAddress();
+ return cast<DbgDeclareInst>(DVI)->isKillLocation();
+}
+bool isKillAddress(const DbgVariableRecord *DVR) {
+ assert(DVR->getType() == DbgVariableRecord::LocationType::Declare ||
+ DVR->getType() == DbgVariableRecord::LocationType::Assign);
+ if (DVR->getType() == DbgVariableRecord::LocationType::Assign)
+ return DVR->isKillAddress();
+ return DVR->isKillLocation();
+}
+const DIExpression *getAddressExpression(const DbgVariableIntrinsic *DVI) {
+ if (const auto *DAI = dyn_cast<DbgAssignIntrinsic>(DVI))
+ return DAI->getAddressExpression();
+ return cast<DbgDeclareInst>(DVI)->getExpression();
+}
+const DIExpression *getAddressExpression(const DbgVariableRecord *DVR) {
+ assert(DVR->getType() == DbgVariableRecord::LocationType::Declare ||
+ DVR->getType() == DbgVariableRecord::LocationType::Assign);
+ if (DVR->getType() == DbgVariableRecord::LocationType::Assign)
+ return DVR->getAddressExpression();
+ return DVR->getExpression();
+}
+
+/// 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 change that introduces this function NFC it bails
+/// in some situations unecessarily, e.g. when fragment and bit extract
+/// sizes differ.
+static DIExpression *createOrReplaceFragment(const DIExpression *Expr,
+ DIExpression::FragmentInfo Frag,
+ int64_t BitExtractOffset) {
+ SmallVector<uint64_t, 8> Ops;
+ bool HasFragment = false;
+ bool HasBitExtract = false;
+
+ for (auto &Op : Expr->expr_ops()) {
+ if (Op.getOp() == dwarf::DW_OP_LLVM_fragment) {
+ HasFragment = true;
+ continue;
+ }
+ if (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_zext ||
+ Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext) {
+ HasBitExtract = true;
+ int64_t ExtractOffsetInBits = Op.getArg(0);
+ int64_t ExtractSizeInBits = Op.getArg(1);
+
+ // DIExpression::createFragmentExpression doesn't know how to handle
+ // a fragment that is smaller than the extract. Copy the behaviour
+ // (bail) to avoid non-NFC changes.
+ // FIXME: Don't do this.
+ if (Frag.SizeInBits < uint64_t(ExtractSizeInBits))
+ return nullptr;
+
+ assert(BitExtractOffset <= 0);
+ int64_t AdjustedOffset = ExtractOffsetInBits + BitExtractOffset;
+
+ // DIExpression::createFragmentExpression doesn't know what to do
+ // if the new extract starts "outside" the existing one. Copy the
+ // behaviour (bail) to avoid non-NFC changes.
+ // FIXME: Don't do this.
+ if (AdjustedOffset < 0)
+ return nullptr;
+
+ Ops.push_back(Op.getOp());
+ Ops.push_back(std::max<int64_t>(0, AdjustedOffset));
+ Ops.push_back(ExtractSizeInBits);
+ continue;
+ }
+ Op.appendToVector(Ops);
+ }
+
+ // Unsupported by createFragmentExpression, so don't support it here yet to
+ // preserve NFC-ness.
+ if (HasFragment && HasBitExtract)
+ return nullptr;
+
+ if (!HasBitExtract) {
+ Ops.push_back(dwarf::DW_OP_LLVM_fragment);
+ Ops.push_back(Frag.OffsetInBits);
+ Ops.push_back(Frag.SizeInBits);
+ }
+ return DIExpression::get(Expr->getContext(), Ops);
+}
+
+/// Insert a new dbg.declare.
+/// \p Orig Original to copy debug loc and variable from.
+/// \p NewAddr Location's new base address.
+/// \p NewAddrExpr New expression to apply to address.
+/// \p BeforeInst Insert position.
+/// \p NewFragment New fragment (absolute, non-relative).
+/// \p BitExtractAdjustment Offset to apply to any extract_bits op.
+static void
+insertNewDbgInst(DIBuilder &DIB, DbgDeclareInst *Orig, AllocaInst *NewAddr,
+ DIExpression *NewAddrExpr, Instruction *BeforeInst,
+ std::optional<DIExpression::FragmentInfo> NewFragment,
+ int64_t BitExtractAdjustment) {
+ if (NewFragment)
+ NewAddrExpr = createOrReplaceFragment(NewAddrExpr, *NewFragment,
+ BitExtractAdjustment);
+ if (!NewAddrExpr)
+ return;
+
+ DIB.insertDeclare(NewAddr, Orig->getVariable(), NewAddrExpr,
Orig->getDebugLoc(), BeforeInst);
}
-static void insertNewDbgInst(DIBuilder &DIB, DbgAssignIntrinsic *Orig,
- AllocaInst *NewAddr, DIExpression *NewFragmentExpr,
- Instruction *BeforeInst) {
+
+/// Insert a new dbg.assign.
+/// \p Orig Original to copy debug loc, variable, value and value expression
+/// from.
+/// \p NewAddr Location's new base address.
+/// \p NewAddrExpr New expression to apply to address.
+/// \p BeforeInst Insert position.
+/// \p NewFragment New fragment (absolute, non-relative).
+/// \p BitExtractAdjustment Offset to apply to any extract_bits op.
+static void
+insertNewDbgInst(DIBuilder &DIB, DbgAssignIntrinsic *Orig, AllocaInst *NewAddr,
+ DIExpression *NewAddrExpr, Instruction *BeforeInst,
+ std::optional<DIExpression::FragmentInfo> NewFragment,
+ int64_t BitExtractAdjustment) {
(void)BeforeInst;
----------------
jmorse wrote:
Comment explaining this will educate readers (it's because the dbg.assign dictates its own position?)
https://github.com/llvm/llvm-project/pull/97750
More information about the llvm-commits
mailing list