Index: llvm/include/llvm/IR/DebugInfoMetadata.h =================================================================== --- llvm/include/llvm/IR/DebugInfoMetadata.h +++ llvm/include/llvm/IR/DebugInfoMetadata.h @@ -3755,6 +3755,10 @@ iterator args_begin() { return Args.begin(); } iterator args_end() { return Args.end(); } + ReplaceableMetadataImpl *getReplaceableUses() { + return Context.getReplaceableUses(); + } + static bool classof(const Metadata *MD) { return MD->getMetadataID() == DIArgListKind; } Index: llvm/include/llvm/IR/DebugProgramInstruction.h =================================================================== --- /dev/null +++ llvm/include/llvm/IR/DebugProgramInstruction.h @@ -0,0 +1,384 @@ +//===-- llvm/DebugProgramInstruction.h - Stream of debug info -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Data structures for storing variable assignment information in LLVM. In the +// dbg.value design, a dbg.value intrinsic specifies the position in a block +// a source variable take on an LLVM Value: +// +// %foo = add i32 1, %0 +// dbg.value(metadata i32 %foo, ...) +// %bar = void call @ext(%foo); +// +// and all information is stored in the Value / Metadata hierachy defined +// elsewhere in LLVM. In the "DPValue" design, each instruction /may/ have a +// connection with a DPMarker, which identifies a position immediately before the +// instruction, and each DPMarker /may/ then have connections to DPValues which +// record the variable assignment information. To illustrate: +// +// %foo = add i32 1, %0 +// ; foo->DbgMarker == nullptr +// ;; There are no variable assignments / debug records "in front" of +// ;; the instruction for %foo, therefore it has no DbgMarker. +// %bar = void call @ext(%foo) +// ; bar->DbgMarker = { +// ; StoredDPValues = { +// ; DPValue(metadata i32 %foo, ...) +// ; } +// ; } +// ;; There is a debug-info record in front of the %bar instruction, +// ;; thus it points at a DPMarker object. That DPMarker contains a +// ;; DPValue in it's ilist, storing the equivalent information to the +// ;; dbg.value above: the Value, DILocalVariable, etc. +// +// This structure separates the two concerns of the position of the debug-info +// in the function, and the Value that it refers to. It also creates a new +// "place" in-between the Value / Metadata hierachy where we can customise +// storage and allocation techniques to better suite debug-info workloads. +// NB: as of the initial prototype, none of that has actually been attempted +// yet. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_DEBUGPROGRAMINSTRUCTION_H +#define LLVM_IR_DEBUGPROGRAMINSTRUCTION_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Bitfields.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/ilist_node.h" +#include "llvm/ADT/iterator.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/SymbolTableListTraits.h" +#include "llvm/IR/TrackingMDRef.h" +#include "llvm/Support/AtomicOrdering.h" +#include +#include + +namespace llvm { + +class Instruction; +class BasicBlock; +class MDNode; +class Module; +class DbgVariableIntrinsic; +class DPMarker; +class DPValue; +class raw_ostream; + +/// Record of a variable value-assignment, aka a non instruction representation +/// of the dbg.value intrinsic. Features various methods copied across from the +/// Instruction class to aid ease-of-use. DPValue objects should always be +/// linked into a DPMarker's StoredDPValues list. The marker connects a DPValue +/// back to it's position in the BasicBlock. +/// +/// This class inherits from DebugValueUser to allow LLVM's metadata facilities +/// to update our references to metadata beneath our feet. +class DPValue : public ilist_node, private DebugValueUser { + friend class DebugValueUser; + + // NB: there is no explicit "Value" field in this class, it's effectively the + // DebugValueUser superclass instead. The referred to Value can either be a + // ValueAsMetadata or a DIArgList. + + DILocalVariable *Variable; + DIExpression *Expression; + DebugLoc DbgLoc; + +public: + void deleteInstr(); + + const BasicBlock *getParent() const; + BasicBlock *getParent(); + void dump() const; + void removeFromParent(); + void eraseFromParent(); + + using self_iterator = simple_ilist::iterator; + using const_self_iterator = simple_ilist::const_iterator; + + enum class LocationType { + Declare, + Value, + }; + /// Classification of the debug-info record that this DPValue represents. + /// Essentially, "is this a dbg.value or dbg.declare?". dbg.declares are not + /// currently supported, but it would be trivial to do so. + LocationType Type; + + /// Marker that this DPValue is linked into. + DPMarker *Marker = nullptr; + + /// Create a new DPValue representing the intrinsic \p DVI, for example the + /// assignment represented by a dbg.value. + DPValue(const DbgVariableIntrinsic *DVI); + DPValue(const DPValue &DPV); + /// Directly construct a new DPValue representing a dbg.value intrinsic + /// assigning \p Location to the DV / Expr / DI variable. + DPValue(Metadata *Location, DILocalVariable *DV, DIExpression *Expr, + const DILocation *DI); + + /// Iterator for ValueAsMetadata that internally uses direct pointer iteration + /// over either a ValueAsMetadata* or a ValueAsMetadata**, dereferencing to the + /// ValueAsMetadata . + class location_op_iterator + : public iterator_facade_base { + PointerUnion I; + + public: + location_op_iterator(ValueAsMetadata *SingleIter) : I(SingleIter) {} + location_op_iterator(ValueAsMetadata **MultiIter) : I(MultiIter) {} + + location_op_iterator(const location_op_iterator &R) : I(R.I) {} + location_op_iterator &operator=(const location_op_iterator &R) { + I = R.I; + return *this; + } + bool operator==(const location_op_iterator &RHS) const { + return I == RHS.I; + } + const Value *operator*() const { + ValueAsMetadata *VAM = I.is() + ? I.get() + : *I.get(); + return VAM->getValue(); + }; + Value *operator*() { + ValueAsMetadata *VAM = I.is() + ? I.get() + : *I.get(); + return VAM->getValue(); + } + location_op_iterator &operator++() { + if (I.is()) + I = I.get() + 1; + else + I = I.get() + 1; + return *this; + } + location_op_iterator &operator--() { + if (I.is()) + I = I.get() - 1; + else + I = I.get() - 1; + return *this; + } + }; + + /// Get the locations corresponding to the variable referenced by the debug + /// info intrinsic. Depending on the intrinsic, this could be the + /// variable's value or its address. + iterator_range location_ops() const; + + Value *getVariableLocationOp(unsigned OpIdx) const; + + void replaceVariableLocationOp(Value *OldValue, Value *NewValue, + bool AllowEmpty = false); + void replaceVariableLocationOp(unsigned OpIdx, Value *NewValue); + /// Adding a new location operand will always result in this intrinsic using + /// an ArgList, and must always be accompanied by a new expression that uses + /// the new operand. + void addVariableLocationOps(ArrayRef NewValues, + DIExpression *NewExpr); + + void setVariable(DILocalVariable *NewVar) { Variable = NewVar; } + + void setExpression(DIExpression *NewExpr) { Expression = NewExpr; } + + unsigned getNumVariableLocationOps() const { + if (hasArgList()) + return cast(getRawLocation())->getArgs().size(); + return 1; + } + + bool hasArgList() const { return isa(getRawLocation()); } + /// Returns true if this DPValue has no empty MDNodes in its location list. + bool hasValidLocation() const { return getVariableLocationOp(0) != nullptr; } + + /// Does this describe the address of a local variable. True for dbg.addr + /// and dbg.declare, but not dbg.value, which describes its value. + bool isAddressOfVariable() const { return Type != LocationType::Value; } + LocationType getType() const { return Type; } + + DebugLoc getDebugLoc() const { return DbgLoc; } + void setDebugLoc(DebugLoc Loc) { DbgLoc = std::move(Loc); } + + void setKillLocation() { + // TODO: When/if we remove duplicate values from DIArgLists, we don't need + // this set anymore. + SmallPtrSet RemovedValues; + for (Value *OldValue : location_ops()) { + if (!RemovedValues.insert(OldValue).second) + continue; + Value *Poison = PoisonValue::get(OldValue->getType()); + replaceVariableLocationOp(OldValue, Poison); + } + } + + bool isKillLocation() const { + return (getNumVariableLocationOps() == 0 && + !getExpression()->isComplex()) || + any_of(location_ops(), [](Value *V) { return isa(V); }); + } + + DILocalVariable *getVariable() const { return Variable; } + + DIExpression *getExpression() const { return Expression; } + + Metadata *getRawLocation() const { return DebugValue; } + + /// Use of this should generally be avoided; instead, + /// replaceVariableLocationOp and addVariableLocationOps should be used where + /// possible to avoid creating invalid state. + void setRawLocation(Metadata *NewLocation) { + assert( + (isa(NewLocation) || isa(NewLocation) || + isa(NewLocation)) && + "Location for a DPValue must be either ValueAsMetadata or DIArgList"); + resetDebugValue(NewLocation); + } + + /// Get the size (in bits) of the variable, or fragment of the variable that + /// is described. + std::optional getFragmentSizeInBits() const; + + DPValue *clone() const; + /// Convert this DPValue back into a dbg.value intrinsic. + /// \p InsertBefore Optional position to insert this intrinsic. + /// \returns A new dbg.value intrinsic representiung this DPValue. + DbgVariableIntrinsic *createDebugIntrinsic(Module *M, + Instruction *InsertBefore) const; + /// Handle changes to the location of the Value(s) that we refer to happening + /// "under our feet". + void handleChangedLocation(Metadata *NewLocation); + + void setMarker(DPMarker *M) { Marker = M; } + + DPMarker *getMarker() { return Marker; } + const DPMarker *getMarker() const { return Marker; } + + BasicBlock *getBlock(); + const BasicBlock *getBlock() const; + + Function *getFunction(); + const Function *getFunction() const; + + Module *getModule(); + const Module *getModule() const; + + LLVMContext &getContext(); + const LLVMContext &getContext() const; + + void print(raw_ostream &O, bool IsForDebug = false) const; + void print(raw_ostream &ROS, ModuleSlotTracker &MST, bool IsForDebug) const; +}; + +/// Per-instruction record of debug-info. If an Instruction is the position of +/// some debugging information, it points at a DPMarker storing that info. Each +/// marker points back at the instruction that owns it. Various utilities are +/// provided for manipulating the DPValues contained within this marker. +/// +/// This class has a rough surface area, because it's needed to preserve the one +/// arefact that we can't yet eliminate from the intrinsic / dbg.value +/// debug-info design: the order of DPValues/records is significant, and +/// duplicates can exist. Thus, if one has a run of debug-info records such as: +/// dbg.value(... +/// %foo = barinst +/// dbg.value(... +/// and remove barinst, then the dbg.values must be preserved in the correct +/// order. Hence, the use of iterators to select positions to insert things +/// into, or the occasional InsertAtHead parameter indicating that new records +/// should go at the start of the list. +/// +/// There are only five or six places in LLVM that truly rely on this ordering, +/// which we can improve in the future. Additionally, many improvements in the +/// way that debug-info is stored can be achieved in this class, at a future +/// date. +class DPMarker { +public: + DPMarker() {} + /// Link back to the Instruction that owns this marker. Can be null during + /// operations that move a marker from one instruction to another. + Instruction *MarkedInstr; + + /// List of DPValues, each recording a single variable assignment, the + /// equivalent of a dbg.value intrinsic. There is a one-to-one relationship + /// between each dbg.value in a block and each DPValue once the + /// representation has been converted, and the ordering of DPValues is + /// meaningful in the same was a dbg.values. + simple_ilist StoredDPValues; + + const BasicBlock *getParent() const; + BasicBlock *getParent(); + + /// Handle the removal of a marker: the position of debug-info has gone away, + /// but the stored debug records should not. Drop them onto the next + /// instruction, or otherwise work out what to do with them. + void removeMarker(); + void dump() const; + + void removeFromParent(); + void eraseFromParent(); + + /// Implement operator<< on DPMarker. + void print(raw_ostream &O, bool IsForDebug = false) const; + void print(raw_ostream &ROS, ModuleSlotTracker &MST, bool IsForDebug) const; + + /// Produce a range over all the DPValues in this Marker. + iterator_range::iterator> getDbgValueRange(); + /// Transfer any DPValues from \p Src into this DPMarker. If \p InsertAtHead + /// is true, place them before existing DPValues, otherwise afterwards. + void absorbDebugValues(DPMarker &Src, bool InsertAtHead); + /// Insert a DPValue into this DPMarker, at the end of the list. If + /// \p InsertAtHead is true, at the start. + void insertDPValue(DPValue *New, bool InsertAtHead); + /// Clone all DPMarkers from \p From into this marker. There are numerous + /// options to customise the source/destination, due to gnarliness, see class + /// comment. + /// \p FromHere If non-null, copy from FromHere to the end of From's DPValues + /// \p InsertAtHead Place the cloned DPValues at the start of StoredDPValues + /// \returns Range over all the newly cloned DPValues + iterator_range::iterator> + cloneDebugInfoFrom(DPMarker *From, + std::optional::iterator> FromHere, + bool InsertAtHead = false); + /// Erase all DPValues in this DPMarker. + void dropDPValues(); + /// Erase a single DPValue from this marker. In an ideal future, we would + /// never erase an assignment in this way, but it's the equivalent to + /// erasing a dbg.value from a block. + void dropOneDPValue(DPValue *DPV); + + /// We generally act like all llvm Instructions have a range of DPValues + /// attached to them, but in reality sometimes we don't allocate the DPMarker + /// to save time and memory, but still have to return ranges of DPValues. When + /// we need to describe such an unallocated DPValue range, use this static + /// markers range instead. This will bite us if someone tries to insert a + /// DPValue in that range, but they should be using the Official (TM) API for + /// that. + static DPMarker EmptyDPMarker; + static iterator_range::iterator> getEmptyDPValueRange(){ + return make_range(EmptyDPMarker.StoredDPValues.begin(), EmptyDPMarker.StoredDPValues.end()); + } +}; + +inline raw_ostream &operator<<(raw_ostream &OS, const DPMarker &Marker) { + Marker.print(OS); + return OS; +} + +inline raw_ostream &operator<<(raw_ostream &OS, const DPValue &Value) { + Value.print(OS); + return OS; +} + +} // namespace llvm + +#endif // LLVM_IR_DEBUGPROGRAMINSTRUCTION_H Index: llvm/include/llvm/IR/Instruction.h =================================================================== --- llvm/include/llvm/IR/Instruction.h +++ llvm/include/llvm/IR/Instruction.h @@ -29,6 +29,7 @@ namespace llvm { class BasicBlock; +class DPMarker; class FastMathFlags; class MDNode; class Module; @@ -51,6 +52,12 @@ /// O(1) local dominance checks between instructions. mutable unsigned Order = 0; +public: + /// Optional marker recording the position for debugging information that + /// takes effect immediately before this instruction. Null unless there is + /// debugging information present. + DPMarker *DbgMarker = nullptr; + protected: // The 15 first bits of `Value::SubclassData` are available for subclasses of // `Instruction` to use. Index: llvm/include/llvm/IR/Metadata.h =================================================================== --- llvm/include/llvm/IR/Metadata.h +++ llvm/include/llvm/IR/Metadata.h @@ -43,6 +43,7 @@ class Module; class ModuleSlotTracker; class raw_ostream; +class DPValue; template class StringMapEntry; template class StringMapEntryStorage; class Type; @@ -201,6 +202,78 @@ void untrack(); }; +/// Base class for tracking ValueAsMetadata/DIArgLists with user lookups and +/// Owner callbacks outside of ValueAsMetadata. +/// +/// Currently only inherited by DPValue; if other classes need to use it, then +/// a SubclassID will need to be added (either as a new field or by making +/// DebugValue into a PointerIntUnion) to discriminate between the subclasses in +/// lookup and callback handling. +class DebugValueUser { +protected: + Metadata *DebugValue; + +public: + DPValue *getUser(); + const DPValue *getUser() const; + void handleChangedValue(Metadata *NewDebugValue); + DebugValueUser() = default; + explicit DebugValueUser(Metadata *DebugValue) : DebugValue(DebugValue) { + trackDebugValue(); + } + + DebugValueUser(DebugValueUser &&X) : DebugValue(X.DebugValue) { + retrackDebugValue(X); + } + DebugValueUser(const DebugValueUser &X) : DebugValue(X.DebugValue) { + trackDebugValue(); + } + + DebugValueUser &operator=(DebugValueUser &&X) { + if (&X == this) + return *this; + + untrackDebugValue(); + DebugValue = X.DebugValue; + retrackDebugValue(X); + return *this; + } + + DebugValueUser &operator=(const DebugValueUser &X) { + if (&X == this) + return *this; + + untrackDebugValue(); + DebugValue = X.DebugValue; + trackDebugValue(); + return *this; + } + + ~DebugValueUser() { untrackDebugValue(); } + + void resetDebugValue() { + untrackDebugValue(); + DebugValue = nullptr; + } + void resetDebugValue(Metadata *DebugValue) { + untrackDebugValue(); + this->DebugValue = DebugValue; + trackDebugValue(); + } + + bool operator==(const DebugValueUser &X) const { + return DebugValue == X.DebugValue; + } + bool operator!=(const DebugValueUser &X) const { + return DebugValue != X.DebugValue; + } + +private: + void trackDebugValue(); + void untrackDebugValue(); + void retrackDebugValue(DebugValueUser &X); +}; + /// API for tracking metadata references through RAUW and deletion. /// /// Shared API for updating \a Metadata pointers in subclasses that support @@ -241,6 +314,15 @@ return track(Ref, MD, &Owner); } + /// Track the reference to metadata for \a DebugValueUser. + /// + /// As \a track(Metadata*&), but with support for calling back to \c Owner to + /// tell it that its operand changed. This could trigger \c Owner being + /// re-uniqued. + static bool track(void *Ref, Metadata &MD, DebugValueUser &Owner) { + return track(Ref, MD, &Owner); + } + /// Stop tracking a reference to metadata. /// /// Stops \c *MD from tracking \c MD. @@ -263,7 +345,7 @@ /// Check whether metadata is replaceable. static bool isReplaceable(const Metadata &MD); - using OwnerTy = PointerUnion; + using OwnerTy = PointerUnion; private: /// Track a reference to metadata for an owner. @@ -275,8 +357,8 @@ /// Shared implementation of use-lists for replaceable metadata. /// /// Most metadata cannot be RAUW'ed. This is a shared implementation of -/// use-lists and associated API for the two that support it (\a ValueAsMetadata -/// and \a TempMDNode). +/// use-lists and associated API for the three that support it ( +/// \a ValueAsMetadata, \a TempMDNode, and \a DIArgList). class ReplaceableMetadataImpl { friend class MetadataTracking; @@ -305,6 +387,8 @@ static void SalvageDebugInfo(const Constant &C); /// Returns the list of all DIArgList users of this. SmallVector getAllArgListUsers(); + /// Returns the list of all DPValue users of this. + SmallVector getAllDPValueUsers(); /// Resolve all uses of this. /// @@ -388,6 +472,9 @@ SmallVector getAllArgListUsers() { return ReplaceableMetadataImpl::getAllArgListUsers(); } + SmallVector getAllDPValueUsers() { + return ReplaceableMetadataImpl::getAllDPValueUsers(); + } static void handleDeletion(Value *V); static void handleRAUW(Value *From, Value *To); @@ -1133,11 +1220,15 @@ bool isDistinct() const { return Storage == Distinct; } bool isTemporary() const { return Storage == Temporary; } + bool isReplaceable() const { + return isTemporary() || getMetadataID() == DIArgListKind; + } + /// RAUW a temporary. /// /// \pre \a isTemporary() must be \c true. void replaceAllUsesWith(Metadata *MD) { - assert(isTemporary() && "Expected temporary node"); + assert(isReplaceable() && "Expected temporary/replaceable node"); if (Context.hasReplaceableUses()) Context.getReplaceableUses()->replaceAllUsesWith(MD); } Index: llvm/lib/IR/CMakeLists.txt =================================================================== --- llvm/lib/IR/CMakeLists.txt +++ llvm/lib/IR/CMakeLists.txt @@ -17,6 +17,7 @@ DataLayout.cpp DebugInfo.cpp DebugInfoMetadata.cpp + DebugProgramInstruction.cpp DebugLoc.cpp DiagnosticHandler.cpp DiagnosticInfo.cpp Index: llvm/lib/IR/DebugInfoMetadata.cpp =================================================================== --- llvm/lib/IR/DebugInfoMetadata.cpp +++ llvm/lib/IR/DebugInfoMetadata.cpp @@ -2104,8 +2104,14 @@ } } if (Uniq) { - if (uniquify() != this) - storeDistinctInContext(); + MDNode *UniqueArgList = uniquify(); + if (UniqueArgList != this) { + replaceAllUsesWith(UniqueArgList); + // Clear this here so we don't try to untrack in the destructor. + Args.clear(); + delete this; + return; + } } track(); } Index: llvm/lib/IR/DebugProgramInstruction.cpp =================================================================== --- /dev/null +++ llvm/lib/IR/DebugProgramInstruction.cpp @@ -0,0 +1,353 @@ +//======-- DebugProgramInstruction.cpp - Implement DPValues/DPMarkers --======// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/DebugProgramInstruction.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/IntrinsicInst.h" + +namespace llvm { + +DPValue::DPValue(const DbgVariableIntrinsic *DVI) + : DebugValueUser(DVI->getRawLocation()), Variable(DVI->getVariable()), + Expression(DVI->getExpression()), DbgLoc(DVI->getDebugLoc()) { + switch (DVI->getIntrinsicID()) { + case Intrinsic::dbg_value: + Type = LocationType::Value; + break; + case Intrinsic::dbg_declare: + Type = LocationType::Declare; + break; + default: + llvm_unreachable( + "Trying to create a DPValue with an invalid intrinsic type!"); + } +} + +DPValue::DPValue(const DPValue &DPV) + : DebugValueUser(DPV.getRawLocation()), Type(DPV.getType()), + Variable(DPV.getVariable()), Expression(DPV.getExpression()), + DbgLoc(DPV.getDebugLoc()) {} + +DPValue::DPValue(Metadata *Location, DILocalVariable *DV, DIExpression *Expr, + const DILocation *DI) + : DebugValueUser(Location), Variable(DV), Expression(Expr), DbgLoc(DI), + Type(LocationType::Value) { +} + +void DPValue::deleteInstr() { delete this; } + +iterator_range DPValue::location_ops() const { + auto *MD = getRawLocation(); + // If a Value has been deleted, the "location" for this DPValue will be + // replaced by nullptr. Return an empty range. + if (!MD) + return {location_op_iterator(static_cast(nullptr)), + location_op_iterator(static_cast(nullptr))}; + + // If operand is ValueAsMetadata, return a range over just that operand. + if (auto *VAM = dyn_cast(MD)) + return {location_op_iterator(VAM), location_op_iterator(VAM + 1)}; + + // If operand is DIArgList, return a range over its args. + if (auto *AL = dyn_cast(MD)) + return {location_op_iterator(AL->args_begin()), + location_op_iterator(AL->args_end())}; + + // Operand is an empty metadata tuple, so return empty iterator. + assert(cast(MD)->getNumOperands() == 0); + return {location_op_iterator(static_cast(nullptr)), + location_op_iterator(static_cast(nullptr))}; +} + +Value *DPValue::getVariableLocationOp(unsigned OpIdx) const { + auto *MD = getRawLocation(); + if (!MD) + return nullptr; + + if (auto *AL = dyn_cast(MD)) + return AL->getArgs()[OpIdx]->getValue(); + if (isa(MD)) + return nullptr; + assert(isa(MD) && + "Attempted to get location operand from DPValue with none."); + auto *V = cast(MD); + assert(OpIdx == 0 && "Operand Index must be 0 for a debug intrinsic with a " + "single location operand."); + return V->getValue(); +} + +static ValueAsMetadata *getAsMetadata(Value *V) { + return isa(V) ? dyn_cast( + cast(V)->getMetadata()) + : ValueAsMetadata::get(V); +} + +void DPValue::replaceVariableLocationOp(Value *OldValue, Value *NewValue, + bool AllowEmpty) { + assert(NewValue && "Values must be non-null"); + auto Locations = location_ops(); + auto OldIt = find(Locations, OldValue); + if (OldIt == Locations.end()) { + if (AllowEmpty) + return; + llvm_unreachable("OldValue must be a current location"); + } + + if (!hasArgList()) { + // Set our location to be the MAV wrapping the new Value. + setRawLocation(isa(NewValue) + ? cast(NewValue)->getMetadata() + : ValueAsMetadata::get(NewValue)); + return; + } + + // We must be referring to a DIArgList, produce a new operands vector with the + // old value replaced, generate a new DIArgList and set it as our location. + SmallVector MDs; + ValueAsMetadata *NewOperand = getAsMetadata(NewValue); + for (auto *VMD : Locations) + MDs.push_back(VMD == *OldIt ? NewOperand : getAsMetadata(VMD)); + setRawLocation(DIArgList::get(getVariableLocationOp(0)->getContext(), MDs)); +} + +void DPValue::replaceVariableLocationOp(unsigned OpIdx, Value *NewValue) { + assert(OpIdx < getNumVariableLocationOps() && "Invalid Operand Index"); + + if (!hasArgList()) { + setRawLocation(isa(NewValue) + ? cast(NewValue)->getMetadata() + : ValueAsMetadata::get(NewValue)); + return; + } + + SmallVector MDs; + ValueAsMetadata *NewOperand = getAsMetadata(NewValue); + for (unsigned Idx = 0; Idx < getNumVariableLocationOps(); ++Idx) + MDs.push_back(Idx == OpIdx ? NewOperand + : getAsMetadata(getVariableLocationOp(Idx))); + + setRawLocation(DIArgList::get(getVariableLocationOp(0)->getContext(), MDs)); +} + +void DPValue::addVariableLocationOps(ArrayRef NewValues, + DIExpression *NewExpr) { + assert(NewExpr->hasAllLocationOps(getNumVariableLocationOps() + + NewValues.size()) && + "NewExpr for debug variable intrinsic does not reference every " + "location operand."); + assert(!is_contained(NewValues, nullptr) && "New values must be non-null"); + setExpression(NewExpr); + SmallVector MDs; + for (auto *VMD : location_ops()) + MDs.push_back(getAsMetadata(VMD)); + for (auto *VMD : NewValues) + MDs.push_back(getAsMetadata(VMD)); + setRawLocation(DIArgList::get(getVariableLocationOp(0)->getContext(), MDs)); +} + +std::optional DPValue::getFragmentSizeInBits() const { + if (auto Fragment = getExpression()->getFragmentInfo()) + return Fragment->SizeInBits; + return getVariable()->getSizeInBits(); +} + +DPValue *DPValue::clone() const { return new DPValue(*this); } + +DbgVariableIntrinsic * +DPValue::createDebugIntrinsic(Module *M, Instruction *InsertBefore) const { + DICompileUnit *Unit = + getDebugLoc().get()->getScope()->getSubprogram()->getUnit(); + assert(M && Unit && + "Cannot clone from BasicBlock that is not part of a Module or " + "DICompileUnit!"); + LLVMContext &Context = getDebugLoc()->getContext(); + Value *Args[] = {MetadataAsValue::get(Context, getRawLocation()), + MetadataAsValue::get(Context, getVariable()), + MetadataAsValue::get(Context, getExpression())}; + Function *IntrinsicFn; + + // Work out what sort of intrinsic we're going to produce. + switch (getType()) { + case DPValue::LocationType::Declare: + IntrinsicFn = Intrinsic::getDeclaration(M, Intrinsic::dbg_declare); + break; + case DPValue::LocationType::Value: + IntrinsicFn = Intrinsic::getDeclaration(M, Intrinsic::dbg_value); + break; + } + + // Create the intrinsic from this DPValue's information, optionally insert + // into the target location. + DbgVariableIntrinsic *DVI = cast( + CallInst::Create(IntrinsicFn->getFunctionType(), IntrinsicFn, Args)); + DVI->setTailCall(); + DVI->setDebugLoc(getDebugLoc()); + if (InsertBefore) + DVI->insertBefore(InsertBefore); + + return DVI; +} + +void DPValue::handleChangedLocation(Metadata *NewLocation) { + resetDebugValue(NewLocation); +} + +const BasicBlock *DPValue::getParent() const { + return Marker->MarkedInstr->getParent(); +} + +BasicBlock *DPValue::getParent() { return Marker->MarkedInstr->getParent(); } + +BasicBlock *DPValue::getBlock() { return Marker->getParent(); } + +const BasicBlock *DPValue::getBlock() const { return Marker->getParent(); } + +Function *DPValue::getFunction() { return getBlock()->getParent(); } + +const Function *DPValue::getFunction() const { return getBlock()->getParent(); } + +Module *DPValue::getModule() { return getFunction()->getParent(); } + +const Module *DPValue::getModule() const { return getFunction()->getParent(); } + +LLVMContext &DPValue::getContext() { return getBlock()->getContext(); } + +const LLVMContext &DPValue::getContext() const { + return getBlock()->getContext(); +} + +/////////////////////////////////////////////////////////////////////////////// + +// An empty, global, DPMarker for the purpose of describing empty ranges of +// DPValues. +DPMarker DPMarker::EmptyDPMarker; + +void DPMarker::dropDPValues() { + while (!StoredDPValues.empty()) { + auto It = StoredDPValues.begin(); + DPValue *DPV = &*It; + StoredDPValues.erase(It); + DPV->deleteInstr(); + } +} + +void DPMarker::dropOneDPValue(DPValue *DPV) { + assert(DPV->getMarker() == this); + StoredDPValues.erase(DPV->getIterator()); + DPV->deleteInstr(); +} + +const BasicBlock *DPMarker::getParent() const { + return MarkedInstr->getParent(); +} + +BasicBlock *DPMarker::getParent() { return MarkedInstr->getParent(); } + +void DPMarker::removeMarker() { + // Are there any DPValues in this DPMarker? If not, nothing to preserve. + Instruction *Owner = MarkedInstr; + if (StoredDPValues.empty()) { + eraseFromParent(); + Owner->DbgMarker = nullptr; + return; + } + + // The attached DPValues need to be preserved; attach them to the next + // instruction. If there isn't a next instruction, put them on the + // "trailing" list. + // (This logic gets refactored in a future patch, needed to break some + // dependencies here, and the TrailingDPValues facility only turns + // up in the next patch). + BasicBlock::iterator NextInst = std::next(Owner->getIterator()); + DPMarker *NextMarker; + if (NextInst == Owner->getParent()->end()) { + llvm_unreachable("Unimplemented trailing DPValue bit of RemoveDIs"); + } else { + NextMarker = NextInst->DbgMarker; + } + NextMarker->absorbDebugValues(*this, true); + + eraseFromParent(); +} + +void DPMarker::removeFromParent() { + MarkedInstr->DbgMarker = nullptr; + MarkedInstr = nullptr; +} + +void DPMarker::eraseFromParent() { + if (MarkedInstr) + removeFromParent(); + dropDPValues(); + delete this; +} + +iterator_range DPMarker::getDbgValueRange() { + return make_range(StoredDPValues.begin(), StoredDPValues.end()); +} + +void DPValue::removeFromParent() { + getMarker()->StoredDPValues.erase(getIterator()); +} + +void DPValue::eraseFromParent() { + removeFromParent(); + deleteInstr(); +} + +void DPMarker::insertDPValue(DPValue *New, bool InsertAtHead) { + auto It = InsertAtHead ? StoredDPValues.begin() : StoredDPValues.end(); + StoredDPValues.insert(It, *New); + New->setMarker(this); +} + +void DPMarker::absorbDebugValues(DPMarker &Src, bool InsertAtHead) { + auto It = InsertAtHead ? StoredDPValues.begin() : StoredDPValues.end(); + for (DPValue &DPV : Src.StoredDPValues) + DPV.setMarker(this); + + StoredDPValues.splice(It, Src.StoredDPValues); +} + +iterator_range::iterator> DPMarker::cloneDebugInfoFrom( + DPMarker *From, std::optional::iterator> from_here, + bool InsertAtHead) { + DPValue *First = nullptr; + // Work out what range of DPValues to clone: normally all the contents of the + // "From" marker, optionally we can start from the from_here position down to + // end(). + auto Range = + make_range(From->StoredDPValues.begin(), From->StoredDPValues.end()); + if (from_here.has_value()) + Range = make_range(*from_here, From->StoredDPValues.end()); + + // Clone each DPValue and insert into StoreDPValues; optionally place them at + // the start or the end of the list. + auto Pos = (InsertAtHead) ? StoredDPValues.begin() : StoredDPValues.end(); + for (DPValue &DPV : Range) { + DPValue *New = DPV.clone(); + New->setMarker(this); + StoredDPValues.insert(Pos, *New); + if (!First) + First = New; + } + + if (!First) + return {StoredDPValues.end(), StoredDPValues.end()}; + + if (InsertAtHead) + // If InsertAtHead is set, we cloned a range onto the front of of the + // StoredDPValues collection, return that range. + return {StoredDPValues.begin(), Pos}; + else + // We inserted a block at the end, return that range. + return {First->getIterator(), StoredDPValues.end()}; +} + +} // end namespace llvm + Index: llvm/lib/IR/LLVMContextImpl.h =================================================================== --- llvm/lib/IR/LLVMContextImpl.h +++ llvm/lib/IR/LLVMContextImpl.h @@ -57,6 +57,7 @@ class AttributeSetNode; class BasicBlock; struct DiagnosticHandler; +class DPMarker; class ElementCount; class Function; class GlobalObject; @@ -1633,6 +1634,38 @@ /// The lifetime of the object must be guaranteed to extend as long as the /// LLVMContext is used by compilation. void setOptPassGate(OptPassGate &); + + /// Mapping of blocks to collections of "trailing" DPValues. As part of the + /// "RemoveDIs" project, debug-info variable location records are going to + /// cease being instructions... which raises the problem of where should they + /// be recorded when we remove the terminator of a blocks, such as: + /// + /// %foo = add i32 0, 0 + /// br label %bar + /// + /// If the branch is removed, a legitimate transient state while editing a + /// block, any debug-records between those two instructions will not have a + /// location. Each block thus records any DPValue records that "trail" in + /// such a way. These are stored in LLVMContext because typically LLVM only + /// edits a small number of blocks at a time, so there's no need to bloat + /// BasicBlock with such a data structure. + SmallDenseMap TrailingDPValues; + + // Set, get and delete operations for TrailingDPValues. + void setTrailingDPValues(BasicBlock *B, DPMarker *M) { + assert(!TrailingDPValues.count(B)); + TrailingDPValues[B] = M; + } + + DPMarker *getTrailingDPValues(BasicBlock *B) { + if (!TrailingDPValues.count(B)) + return nullptr; + return TrailingDPValues[B]; + } + + void deleteTrailingDPValues(BasicBlock *B) { + TrailingDPValues.erase(B); + } }; } // end namespace llvm Index: llvm/lib/IR/LLVMContextImpl.cpp =================================================================== --- llvm/lib/IR/LLVMContextImpl.cpp +++ llvm/lib/IR/LLVMContextImpl.cpp @@ -45,6 +45,14 @@ Int16Ty(C, 16), Int32Ty(C, 32), Int64Ty(C, 64), Int128Ty(C, 128) {} LLVMContextImpl::~LLVMContextImpl() { +#ifndef NDEBUG + // Check that any variable location records that fell off the end of a block + // when it's terminator was removed, were eventually replaced. This assertion + // firing indicates that DPValues went missing during the lifetime of the + // LLVMContext. + assert(TrailingDPValues.empty() && "DPValue records in blocks not cleaned"); +#endif + // NOTE: We need to delete the contents of OwnedModules, but Module's dtor // will call LLVMContextImpl::removeModule, thus invalidating iterators into // the container. Avoid iterators during this operation: Index: llvm/lib/IR/Metadata.cpp =================================================================== --- llvm/lib/IR/Metadata.cpp +++ llvm/lib/IR/Metadata.cpp @@ -147,6 +147,32 @@ MetadataTracking::untrack(MD); } +DPValue *DebugValueUser::getUser() { return static_cast(this); } +const DPValue *DebugValueUser::getUser() const { + return static_cast(this); +} +void DebugValueUser::handleChangedValue(Metadata *NewMD) { + getUser()->handleChangedLocation(NewMD); +} + +void DebugValueUser::trackDebugValue() { + if (DebugValue) + MetadataTracking::track(&DebugValue, *DebugValue, *this); +} + +void DebugValueUser::untrackDebugValue() { + if (DebugValue) + MetadataTracking::untrack(DebugValue); +} + +void DebugValueUser::retrackDebugValue(DebugValueUser &X) { + assert(DebugValue == X.DebugValue && "Expected values to match"); + if (X.DebugValue) { + MetadataTracking::retrack(X.DebugValue, DebugValue); + X.DebugValue = nullptr; + } +} + bool MetadataTracking::track(void *Ref, Metadata &MD, OwnerTy Owner) { assert(Ref && "Expected live reference"); assert((Owner || *static_cast(Ref) == &MD) && @@ -195,6 +221,8 @@ SmallVector *> MDUsersWithID; for (auto Pair : UseMap) { OwnerTy Owner = Pair.second.first; + if (Owner.isNull()) + continue; if (!isa(Owner)) continue; Metadata *OwnerMD = cast(Owner); @@ -210,6 +238,25 @@ return MDUsers; } +SmallVector ReplaceableMetadataImpl::getAllDPValueUsers() { + SmallVector *> DPVUsersWithID; + for (auto Pair : UseMap) { + OwnerTy Owner = Pair.second.first; + if (Owner.isNull()) + continue; + if (!Owner.is()) + continue; + DPVUsersWithID.push_back(&UseMap[Pair.first]); + } + llvm::sort(DPVUsersWithID, [](auto UserA, auto UserB) { + return UserA->second < UserB->second; + }); + SmallVector DPVUsers; + for (auto UserWithID : DPVUsersWithID) + DPVUsers.push_back(UserWithID->first.get()->getUser()); + return DPVUsers; +} + void ReplaceableMetadataImpl::addRef(void *Ref, OwnerTy Owner) { bool WasInserted = UseMap.insert(std::make_pair(Ref, std::make_pair(Owner, NextIndex))) @@ -308,6 +355,11 @@ continue; } + if (Owner.is()) { + Owner.get()->getUser()->handleChangedLocation(MD); + continue; + } + // There's a Metadata owner -- dispatch. Metadata *OwnerMD = cast(Owner); switch (OwnerMD->getMetadataID()) { @@ -343,7 +395,7 @@ auto Owner = Pair.second.first; if (!Owner) continue; - if (isa(Owner)) + if (!Owner.is()) continue; // Resolve MDNodes that point at this. @@ -357,18 +409,25 @@ } ReplaceableMetadataImpl *ReplaceableMetadataImpl::getOrCreate(Metadata &MD) { + if (auto ArgList = dyn_cast(&MD)) + return ArgList->Context.getOrCreateReplaceableUses(); if (auto *N = dyn_cast(&MD)) return N->isResolved() ? nullptr : N->Context.getOrCreateReplaceableUses(); return dyn_cast(&MD); } ReplaceableMetadataImpl *ReplaceableMetadataImpl::getIfExists(Metadata &MD) { + if (auto ArgList = dyn_cast(&MD)) { + return ArgList->Context.getOrCreateReplaceableUses(); + } if (auto *N = dyn_cast(&MD)) return N->isResolved() ? nullptr : N->Context.getReplaceableUses(); return dyn_cast(&MD); } bool ReplaceableMetadataImpl::isReplaceable(const Metadata &MD) { + if (isa(&MD)) + return true; if (auto *N = dyn_cast(&MD)) return !N->isResolved(); return isa(&MD); Index: llvm/unittests/IR/DebugInfoTest.cpp =================================================================== --- llvm/unittests/IR/DebugInfoTest.cpp +++ llvm/unittests/IR/DebugInfoTest.cpp @@ -730,4 +730,169 @@ } } +// Test some very straight-forward operations on DPValues -- these are +// dbg.values that have been converted to a non-instruction format. +TEST(MetadataTest, ConvertDbgToDPValue) { + LLVMContext C; + std::unique_ptr M = parseIR(C, R"( + define i16 @f(i16 %a) !dbg !6 { + call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11 + %b = add i16 %a, 1, !dbg !11 + call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11 + ret i16 0, !dbg !11 + + exit: + %c = add i16 %b, 1, !dbg !11 + ret i16 0, !dbg !11 + } + declare void @llvm.dbg.value(metadata, metadata, metadata) #0 + attributes #0 = { nounwind readnone speculatable willreturn } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!5} + + !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) + !1 = !DIFile(filename: "t.ll", directory: "/") + !2 = !{} + !5 = !{i32 2, !"Debug Info Version", i32 3} + !6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8) + !7 = !DISubroutineType(types: !2) + !8 = !{!9} + !9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10) + !10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned) + !11 = !DILocation(line: 1, column: 1, scope: !6) +)"); + + // Find the first dbg.value, + Instruction &I = *M->getFunction("f")->getEntryBlock().getFirstNonPHI(); + const DILocalVariable *Var = nullptr; + const DIExpression *Expr = nullptr; + const DILocation *Loc = nullptr; + const Metadata *MLoc = nullptr; + DPValue *DPV1 = nullptr; + { + DbgValueInst *DPI = dyn_cast(&I); + ASSERT_TRUE(DPI); + Var = DPI->getVariable(); + Expr = DPI->getExpression(); + Loc = DPI->getDebugLoc().get(); + MLoc = DPI->getRawLocation(); + + // Test the creation of a DPValue and it's conversion back to a dbg.value. + DPV1 = new DPValue(DPI); + EXPECT_EQ(DPV1->getVariable(), Var); + EXPECT_EQ(DPV1->getExpression(), Expr); + EXPECT_EQ(DPV1->getDebugLoc().get(), Loc); + EXPECT_EQ(DPV1->getRawLocation(), MLoc); + + // Erase dbg.value, + DPI->eraseFromParent(); + // Re-create from DPV1, inserting at front. + DPV1->createDebugIntrinsic(&*M, + &M->getFunction("f")->getEntryBlock().front()); + + Instruction *NewDPI = &M->getFunction("f")->getEntryBlock().front(); + DbgValueInst *DPI2 = dyn_cast(NewDPI); + ASSERT_TRUE(DPI2); + EXPECT_EQ(DPI2->getVariable(), Var); + EXPECT_EQ(DPI2->getExpression(), Expr); + EXPECT_EQ(DPI2->getDebugLoc().get(), Loc); + EXPECT_EQ(DPI2->getRawLocation(), MLoc); + } + + // Fetch the second dbg.value, convert it to a DPValue, + BasicBlock::iterator It = M->getFunction("f")->getEntryBlock().begin(); + It = std::next(std::next(It)); + DbgValueInst *DPI3 = dyn_cast(It); + ASSERT_TRUE(DPI3); + DPValue *DPV2 = new DPValue(DPI3); + + // These dbg.values are supposed to refer to different values. + EXPECT_NE(DPV1->getRawLocation(), DPV2->getRawLocation()); + + // Try manipulating DPValues and markers in the exit block. + BasicBlock *ExitBlock = &*std::next(M->getFunction("f")->getEntryBlock().getIterator()); + Instruction *FirstInst = &ExitBlock->front(); + Instruction *RetInst = &*std::next(FirstInst->getIterator()); + + // Set-up DPMarkers in this block. + FirstInst->DbgMarker = new DPMarker(); + FirstInst->DbgMarker->MarkedInstr = FirstInst; + RetInst->DbgMarker = new DPMarker(); + RetInst->DbgMarker->MarkedInstr = RetInst; + + // Insert DPValues into markers, order should come out DPV2, DPV1. + FirstInst->DbgMarker->insertDPValue(DPV1, false); + FirstInst->DbgMarker->insertDPValue(DPV2, true); + unsigned int ItCount = 0; + for (DPValue &Item : FirstInst->DbgMarker->getDbgValueRange()) { + EXPECT_TRUE((&Item == DPV2 && ItCount == 0) || + (&Item == DPV1 && ItCount == 1)); + EXPECT_EQ(Item.getMarker(), FirstInst->DbgMarker); + ++ItCount; + } + + // Clone them onto the second marker -- should allocate new DPVs. + RetInst->DbgMarker->cloneDebugInfoFrom(FirstInst->DbgMarker, std::nullopt, false); + EXPECT_EQ(RetInst->DbgMarker->StoredDPValues.size(), 2); + ItCount = 0; + // Check these things store the same information; but that they're not the same + // objects. + for (DPValue &Item : RetInst->DbgMarker->getDbgValueRange()) { + EXPECT_TRUE((Item.getRawLocation() == DPV2->getRawLocation() && ItCount == 0) || + (Item.getRawLocation() == DPV1->getRawLocation() && ItCount == 1)); + + EXPECT_EQ(Item.getMarker(), RetInst->DbgMarker); + EXPECT_NE(&Item, DPV1); + EXPECT_NE(&Item, DPV2); + ++ItCount; + } + + RetInst->DbgMarker->dropDPValues(); + EXPECT_EQ(RetInst->DbgMarker->StoredDPValues.size(), 0); + + // Try cloning one single DPValue. + auto DIIt = std::next(FirstInst->DbgMarker->getDbgValueRange().begin()); + RetInst->DbgMarker->cloneDebugInfoFrom(FirstInst->DbgMarker, DIIt, false); + EXPECT_EQ(RetInst->DbgMarker->StoredDPValues.size(), 1); + // The second DPValue should have been cloned; it should have the same values + // as DPV1. + EXPECT_EQ(RetInst->DbgMarker->StoredDPValues.begin()->getRawLocation(), + DPV1->getRawLocation()); + // We should be able to drop individual DPValues. + RetInst->DbgMarker->dropOneDPValue(&*RetInst->DbgMarker->StoredDPValues.begin()); + + // "Aborb" a DPMarker: this means pretend that the instruction it's attached + // to is disappearing so it needs to be transferred into "this" marker. + RetInst->DbgMarker->absorbDebugValues(*FirstInst->DbgMarker, true); + EXPECT_EQ(RetInst->DbgMarker->StoredDPValues.size(), 2); + // Should be the DPV1 and DPV2 objects. + ItCount = 0; + for (DPValue &Item : RetInst->DbgMarker->getDbgValueRange()) { + EXPECT_TRUE((&Item == DPV2 && ItCount == 0) || + (&Item == DPV1 && ItCount == 1)); + EXPECT_EQ(Item.getMarker(), RetInst->DbgMarker); + ++ItCount; + } + + // Finally -- there are two DPValues left over. If we remove evrything in the + // basic block, then they should sink down into the "TrailingDPValues" + // container for dangling debug-info. Future facilities will restore them + // back when a terminator is inserted. + FirstInst->DbgMarker->removeMarker(); + FirstInst->eraseFromParent(); + RetInst->DbgMarker->removeMarker(); + RetInst->eraseFromParent(); + + EXPECT_EQ(ExitBlock->TrailingDPValues.StoredDPValues.size(), 2); + // Test again that it's those two DPValues, DPV1 and DPV2. + ItCount = 0; + for (DPValue &Item : ExitBlock->TrailingDPValues.getDbgValueRange()) { + EXPECT_TRUE((&Item == DPV2 && ItCount == 0) || + (&Item == DPV1 && ItCount == 1)); + EXPECT_EQ(Item.getMarker(), &ExitBlock->TrailingDPValues); + ++ItCount; + } +} + } // end namespace Index: llvm/unittests/Transforms/Utils/LocalTest.cpp =================================================================== --- llvm/unittests/Transforms/Utils/LocalTest.cpp +++ llvm/unittests/Transforms/Utils/LocalTest.cpp @@ -1161,3 +1161,69 @@ BB0->dropAllReferences(); } + +TEST(Local, ReplaceDPValue) { + LLVMContext C; + + // Test that RAUW also replaces the operands of DPValue objects, i.e. + // non-instruction stored debugging information. + std::unique_ptr M = parseIR(C, + R"( + declare void @llvm.dbg.value(metadata, metadata, metadata) + define void @f(i32 %a) !dbg !8 { + entry: + %foo = add i32 %a, 1, !dbg !13 + %bar = add i32 %foo, 0, !dbg !13 + call void @llvm.dbg.value(metadata i32 %bar, metadata !11, metadata !DIExpression()), !dbg !13 + ret void, !dbg !14 + } + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4} + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) + !1 = !DIFile(filename: "t2.c", directory: "foo") + !2 = !{} + !3 = !{i32 2, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2) + !9 = !DISubroutineType(types: !10) + !10 = !{null} + !11 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !12) + !12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !13 = !DILocation(line: 2, column: 7, scope: !8) + !14 = !DILocation(line: 3, column: 1, scope: !8) + )"); + auto *GV = M->getNamedValue("f"); + ASSERT_TRUE(GV); + auto *F = dyn_cast(GV); + ASSERT_TRUE(F); + BasicBlock::iterator It = F->front().begin(); + Instruction *FooInst = &*It; + It = std::next(It); + Instruction *BarInst = &*It; + It = std::next(It); + DbgValueInst *DVI = dyn_cast(It); + ASSERT_TRUE(DVI); + It = std::next(It); + Instruction *RetInst = &*It; + + // Convert DVI into a DPValue. + RetInst->DbgMarker = new DPMarker(); + RetInst->DbgMarker->MarkedInstr = RetInst; + DPValue *DPV = new DPValue(DVI); + RetInst->DbgMarker->insertDPValue(DPV, false); + // ... and erase the dbg.value. + DVI->eraseFromParent(); + + // DPV should originally refer to %bar, + EXPECT_EQ(DPV->getVariableLocationOp(0), BarInst); + + // Now try to replace the computation of %bar with %foo -- this should cause + // the DPValue's to have it's operand updated beneath it. + BarInst->replaceAllUsesWith(FooInst); + // Check DPV now points at %foo. + EXPECT_EQ(DPV->getVariableLocationOp(0), FooInst); + + // Teardown. + RetInst->DbgMarker->eraseFromParent(); +} + Index: llvm/utils/gn/secondary/llvm/lib/IR/BUILD.gn =================================================================== --- llvm/utils/gn/secondary/llvm/lib/IR/BUILD.gn +++ llvm/utils/gn/secondary/llvm/lib/IR/BUILD.gn @@ -31,6 +31,7 @@ "DataLayout.cpp", "DebugInfo.cpp", "DebugInfoMetadata.cpp", + "DebugProgramInstruction.cpp", "DebugLoc.cpp", "DiagnosticHandler.cpp", "DiagnosticInfo.cpp",