diff --git a/llvm/include/llvm/IR/BasicBlock.h b/llvm/include/llvm/IR/BasicBlock.h index ab291c24e5b6c..a6d66d1a02b83 100644 --- a/llvm/include/llvm/IR/BasicBlock.h +++ b/llvm/include/llvm/IR/BasicBlock.h @@ -36,6 +36,8 @@ class LLVMContext; class Module; class PHINode; class ValueSymbolTable; +class DPValue; +class DPMarker; /// LLVM Basic Block Representation /// @@ -56,6 +58,9 @@ class BasicBlock final : public Value, // Basic blocks are data objects also public ilist_node_with_parent { public: using InstListType = SymbolTableList>; + /// Flag recording whether or not this block stores debug-info in the form + /// of intrinsic instructions (false) or non-instruction records (true). + bool IsNewDbgInfoFormat; private: friend class BlockAddress; @@ -64,6 +69,55 @@ class BasicBlock final : public Value, // Basic blocks are data objects also InstListType InstList; Function *Parent; +public: + /// Attach a DPMarker to the given instruction. Enables the storage of any + /// debug-info at this position in the program. + DPMarker *createMarker(Instruction *I); + DPMarker *createMarker(InstListType::iterator It); + + /// Convert variable location debugging information stored in dbg.value + /// intrinsics into DPMarker / DPValue records. Deletes all dbg.values in + /// the process and sets IsNewDbgInfoFormat = true. Only takes effect if + /// the UseNewDbgInfoFormat LLVM command line option is given. + void convertToNewDbgValues(); + + /// Convert variable location debugging information stored in DPMarkers and + /// DPValues into the dbg.value intrinsic representation. Sets + /// IsNewDbgInfoFormat = false. + void convertFromNewDbgValues(); + + /// Ensure the block is in "old" dbg.value format (\p NewFlag == false) or + /// in the new format (\p NewFlag == true), converting to the desired format + /// if necessary. + void setIsNewDbgInfoFormat(bool NewFlag); + + /// Validate any DPMarkers / DPValues attached to instructions in this block, + /// and block-level stored data too (TrailingDPValues). + /// \p Assert Should this method fire an assertion if a problem is found? + /// \p Msg Should this method print a message to errs() if a problem is found? + /// \p OS Output stream to write errors to. + /// \returns True if a problem is found. + bool validateDbgValues(bool Assert = true, bool Msg = false, + raw_ostream *OS = nullptr); + + /// Record that the collection of DPValues in \p M "trails" after the last + /// instruction of this block. These are equivalent to dbg.value intrinsics + /// that exist at the end of a basic block with no terminator (a transient + /// state that occurs regularly). + void setTrailingDPValues(DPMarker *M); + + /// Fetch the collection of DPValues that "trail" after the last instruction + /// of this block, see \ref setTrailingDPValues. If there are none, returns + /// nullptr. + DPMarker *getTrailingDPValues(); + + /// Delete any trailing DPValues at the end of this block, see + /// \ref setTrailingDPValues. + void deleteTrailingDPValues(); + + void dumpDbgValues() const; + +private: void setParent(Function *parent); /// Constructor. diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h index b347664883fd9..1fe054316b75c 100644 --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -3765,6 +3765,10 @@ class DIArgList : public MDNode { 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; } diff --git a/llvm/include/llvm/IR/DebugProgramInstruction.h b/llvm/include/llvm/IR/DebugProgramInstruction.h new file mode 100644 index 0000000000000..fef4bb642bf36 --- /dev/null +++ b/llvm/include/llvm/IR/DebugProgramInstruction.h @@ -0,0 +1,378 @@ +//===-- 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/ilist_node.h" +#include "llvm/ADT/ilist.h" +#include "llvm/ADT/iterator.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DebugLoc.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 = nullptr; + + /// 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.end(), 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 diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h index 692ef7535f0e6..1628aafe320da 100644 --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -97,15 +97,28 @@ class LLVM_EXTERNAL_VISIBILITY Function : public GlobalObject, friend class SymbolTableListTraits; +public: + /// Is this function using intrinsics to record the position of debugging + /// information, or non-intrinsic records? See IsNewDbgInfoFormat in + /// \ref BasicBlock. + bool IsNewDbgInfoFormat; + /// hasLazyArguments/CheckLazyArguments - The argument list of a function is /// built on demand, so that the list isn't allocated until the first client /// needs it. The hasLazyArguments predicate returns true if the arg list /// hasn't been set up yet. -public: bool hasLazyArguments() const { return getSubclassDataFromValue() & (1<<0); } + /// \see BasicBlock::convertToNewDbgValues. + void convertToNewDbgValues(); + + /// \see BasicBlock::convertFromNewDbgValues. + void convertFromNewDbgValues(); + + void setIsNewDbgInfoFormat(bool NewVal); + private: void CheckLazyArguments() const { if (hasLazyArguments()) @@ -692,6 +705,7 @@ class LLVM_EXTERNAL_VISIBILITY Function : public GlobalObject, /// Insert \p BB in the basic block list at \p Position. \Returns an iterator /// to the newly inserted BB. Function::iterator insert(Function::iterator Position, BasicBlock *BB) { + BB->setIsNewDbgInfoFormat(IsNewDbgInfoFormat); return BasicBlocks.insert(Position, BB); } diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h index b5ccdf020a4c0..7f694219b15a7 100644 --- a/llvm/include/llvm/IR/Instruction.h +++ b/llvm/include/llvm/IR/Instruction.h @@ -29,10 +29,12 @@ namespace llvm { class BasicBlock; +class DPMarker; class FastMathFlags; class MDNode; class Module; struct AAMDNodes; +class DPMarker; template <> struct ilist_alloc_traits { static inline void deleteNode(Instruction *V); @@ -51,6 +53,12 @@ class Instruction : public User, /// 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. diff --git a/llvm/include/llvm/IR/Metadata.h b/llvm/include/llvm/IR/Metadata.h index 9659dbe4f281e..a245dabe086f0 100644 --- a/llvm/include/llvm/IR/Metadata.h +++ b/llvm/include/llvm/IR/Metadata.h @@ -43,6 +43,7 @@ namespace llvm { class Module; class ModuleSlotTracker; class raw_ostream; +class DPValue; template class StringMapEntry; template class StringMapEntryStorage; class Type; @@ -201,6 +202,78 @@ class MetadataAsValue : public Value { 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 @@ class MetadataTracking { 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 @@ class MetadataTracking { /// 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 @@ class MetadataTracking { /// 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 @@ class ReplaceableMetadataImpl { 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 @@ class ValueAsMetadata : public Metadata, ReplaceableMetadataImpl { 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 @@ class MDNode : public Metadata { 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); } diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h index 70beddddc1c16..68a89dc45c283 100644 --- a/llvm/include/llvm/IR/Module.h +++ b/llvm/include/llvm/IR/Module.h @@ -213,6 +213,27 @@ class LLVM_EXTERNAL_VISIBILITY Module { /// @name Constructors /// @{ public: + /// Is this Module using intrinsics to record the position of debugging + /// information, or non-intrinsic records? See IsNewDbgInfoFormat in + /// \ref BasicBlock. + bool IsNewDbgInfoFormat; + + /// \see BasicBlock::convertToNewDbgValues. + void convertToNewDbgValues() { + for (auto &F : *this) { + F.convertToNewDbgValues(); + } + IsNewDbgInfoFormat = true; + } + + /// \see BasicBlock::convertFromNewDbgValues. + void convertFromNewDbgValues() { + for (auto &F : *this) { + F.convertFromNewDbgValues(); + } + IsNewDbgInfoFormat = false; + } + /// The Module constructor. Note that there is no default constructor. You /// must provide a name for the module upon construction. explicit Module(StringRef ModuleID, LLVMContext& C); diff --git a/llvm/lib/IR/BasicBlock.cpp b/llvm/lib/IR/BasicBlock.cpp index 46b1a3b37132b..d238b651a69a0 100644 --- a/llvm/lib/IR/BasicBlock.cpp +++ b/llvm/lib/IR/BasicBlock.cpp @@ -16,16 +16,176 @@ #include "llvm/ADT/Statistic.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugProgramInstruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Type.h" +#include "llvm/Support/CommandLine.h" + +#include "LLVMContextImpl.h" using namespace llvm; #define DEBUG_TYPE "ir" STATISTIC(NumInstrRenumberings, "Number of renumberings across all blocks"); +cl::opt + UseNewDbgInfoFormat("experimental-debuginfo-iterators", + cl::desc("Enable communicating debuginfo positions " + "through iterators, eliminating intrinsics"), + cl::init(false)); + +DPMarker *BasicBlock::createMarker(Instruction *I) { + assert(IsNewDbgInfoFormat && + "Tried to create a marker in a non new debug-info block!"); + assert(I->DbgMarker == nullptr && + "Tried to create marker for instuction that already has one!"); + DPMarker *Marker = new DPMarker(); + Marker->MarkedInstr = I; + I->DbgMarker = Marker; + return Marker; +} + +DPMarker *BasicBlock::createMarker(InstListType::iterator It) { + assert(IsNewDbgInfoFormat && + "Tried to create a marker in a non new debug-info block!"); + if (It != end()) + return createMarker(&*It); + DPMarker *DPM = getTrailingDPValues(); + if (DPM) + return DPM; + DPM = new DPMarker(); + setTrailingDPValues(DPM); + return DPM; +} + +void BasicBlock::convertToNewDbgValues() { + // Is the command line option set? + if (!UseNewDbgInfoFormat) + return; + + IsNewDbgInfoFormat = true; + + // Iterate over all instructions in the instruction list, collecting dbg.value + // instructions and converting them to DPValues. Once we find a "real" + // instruction, attach all those DPValues to a DPMarker in that instruction. + SmallVector DPVals; + for (Instruction &I : make_early_inc_range(InstList)) { + assert(!I.DbgMarker && "DbgMarker already set on old-format instrs?"); + if (DbgValueInst *DVI = dyn_cast(&I)) { + // Convert this dbg.value to a DPValue. + DPValue *Value = new DPValue(DVI); + DPVals.push_back(Value); + DVI->eraseFromParent(); + continue; + } + + // Create a marker to store DPValues in. Technically we don't need to store + // one marker per instruction, but that's a future optimisation. + createMarker(&I); + DPMarker *Marker = I.DbgMarker; + + for (DPValue *DPV : DPVals) + Marker->insertDPValue(DPV, false); + + DPVals.clear(); + } +} + +void BasicBlock::convertFromNewDbgValues() { + invalidateOrders(); + IsNewDbgInfoFormat = false; + + // Iterate over the block, finding instructions annotated with DPMarkers. + // Convert any attached DPValues to dbg.values and insert ahead of the + // instruction. + for (auto &Inst : *this) { + if (!Inst.DbgMarker) + continue; + + DPMarker &Marker = *Inst.DbgMarker; + for (DPValue &DPV : Marker.getDbgValueRange()) + InstList.insert(Inst.getIterator(), + DPV.createDebugIntrinsic(getModule(), nullptr)); + + Marker.eraseFromParent(); + }; + + // Assume no trailing DPValues: we could technically create them at the end + // of the block, after a terminator, but this would be non-cannonical and + // indicates that something else is broken somewhere. + assert(!getTrailingDPValues()); +} + +bool BasicBlock::validateDbgValues(bool Assert, bool Msg, raw_ostream *OS) { + bool RetVal = false; + if (!OS) + OS = &errs(); + + // Helper lambda for reporting failures: via assertion, printing, and return + // value. + auto TestFailure = [Assert, Msg, &RetVal, OS](bool Val, const char *Text) { + // Did the test fail? + if (Val) + return; + + // If we're asserting, then fire off an assertion. + if (Assert) + llvm_unreachable(Text); + + if (Msg) + *OS << Text << "\n"; + RetVal = true; + }; + + // We should have the same debug-format as the parent function. + TestFailure(getParent()->IsNewDbgInfoFormat == IsNewDbgInfoFormat, + "Parent function doesn't have the same debug-info format"); + + // Only validate if we are using the new format. + if (!IsNewDbgInfoFormat) + return RetVal; + + // Match every DPMarker to every Instruction and vice versa, and + // verify that there are no invalid DPValues. + for (auto It = begin(); It != end(); ++It) { + if (!It->DbgMarker) + continue; + + // Validate DebugProgramMarkers. + DPMarker *CurrentDebugMarker = It->DbgMarker; + + // If this is a marker, it should match the instruction and vice versa. + TestFailure(CurrentDebugMarker->MarkedInstr == &*It, + "Debug Marker points to incorrect instruction?"); + + // Now validate any DPValues in the marker. + for (DPValue &DPV : CurrentDebugMarker->getDbgValueRange()) { + // Validate DebugProgramValues. + TestFailure(DPV.getMarker() == CurrentDebugMarker, + "Not pointing at correct next marker!"); + + // Verify that no DbgValues appear prior to PHIs. + TestFailure( + !isa(It), + "DebugProgramValues must not appear before PHI nodes in a block!"); + } + } + + // Except transiently when removing + re-inserting the block terminator, there + // should be no trailing DPValues. + TestFailure(!getTrailingDPValues(), "Trailing DPValues in block"); + return RetVal; +} + +void BasicBlock::setIsNewDbgInfoFormat(bool NewFlag) { + if (NewFlag && !IsNewDbgInfoFormat) + convertToNewDbgValues(); + else if (!NewFlag && IsNewDbgInfoFormat) + convertFromNewDbgValues(); +} + ValueSymbolTable *BasicBlock::getValueSymbolTable() { if (Function *F = getParent()) return F->getValueSymbolTable(); @@ -47,7 +207,8 @@ template class llvm::SymbolTableListTraitsIsNewDbgInfoFormat); } void BasicBlock::insertInto(Function *NewParent, BasicBlock *InsertBefore) { assert(NewParent && "Expected a parent"); assert(!Parent && "Already has a parent"); + setIsNewDbgInfoFormat(NewParent->IsNewDbgInfoFormat); + if (InsertBefore) NewParent->insert(InsertBefore->getIterator(), this); else @@ -91,6 +256,11 @@ BasicBlock::~BasicBlock() { assert(getParent() == nullptr && "BasicBlock still linked into the program!"); dropAllReferences(); + for (auto &Inst : *this) { + if (!Inst.DbgMarker) + continue; + Inst.DbgMarker->eraseFromParent(); + } InstList.clear(); } @@ -588,3 +758,16 @@ void BasicBlock::validateInstrOrdering() const { } } #endif + +void BasicBlock::setTrailingDPValues(DPMarker *foo) { + getContext().pImpl->setTrailingDPValues(this, foo); +} + +DPMarker *BasicBlock::getTrailingDPValues() { + return getContext().pImpl->getTrailingDPValues(this); +} + +void BasicBlock::deleteTrailingDPValues() { + getContext().pImpl->deleteTrailingDPValues(this); +} + diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt index d9656a24d0ed3..5fe3d8029e7d3 100644 --- a/llvm/lib/IR/CMakeLists.txt +++ b/llvm/lib/IR/CMakeLists.txt @@ -17,6 +17,7 @@ add_llvm_component_library(LLVMCore DataLayout.cpp DebugInfo.cpp DebugInfoMetadata.cpp + DebugProgramInstruction.cpp DebugLoc.cpp DiagnosticHandler.cpp DiagnosticInfo.cpp diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp index f7f36129ec855..48c4b62b467d1 100644 --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -2135,8 +2135,26 @@ void DIArgList::handleChangedOperand(void *Ref, Metadata *New) { } } if (Uniq) { + // In the RemoveDIs project (eliminating debug-info-intrinsics), DIArgLists + // can be referred to by DebugValueUser objects, which necessitates them + // being unique and replaceable metadata. This causes a slight + // performance regression that's to be avoided during the early stages of + // the RemoveDIs prototype, see D154080. +#ifdef EXPERIMENTAL_DEBUGINFO_ITERATORS + 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; + } +#else + // Otherwise, don't fully unique, become distinct instead. See D108968, + // there's a latent bug that presents here as nondeterminism otherwise. if (uniquify() != this) storeDistinctInContext(); +#endif } track(); } diff --git a/llvm/lib/IR/DebugProgramInstruction.cpp b/llvm/lib/IR/DebugProgramInstruction.cpp new file mode 100644 index 0000000000000..3f39bae983253 --- /dev/null +++ b/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()), + Variable(DPV.getVariable()), Expression(DPV.getExpression()), + DbgLoc(DPV.getDebugLoc()), Type(DPV.getType()) {} + +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 { + [[maybe_unused]] 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). + BasicBlock::iterator NextInst = std::next(Owner->getIterator()); + DPMarker *NextMarker; + if (NextInst == Owner->getParent()->end()) { + NextMarker = new DPMarker(); + Owner->getParent()->setTrailingDPValues(NextMarker); + } 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 + diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index 658aa67a38c93..49e0c1c5883c2 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -81,6 +81,27 @@ static cl::opt NonGlobalValueMaxNameSize( "non-global-value-max-name-size", cl::Hidden, cl::init(1024), cl::desc("Maximum size for the name of non-global values.")); +void Function::convertToNewDbgValues() { + IsNewDbgInfoFormat = true; + for (auto &BB : *this) { + BB.convertToNewDbgValues(); + } +} + +void Function::convertFromNewDbgValues() { + IsNewDbgInfoFormat = false; + for (auto &BB : *this) { + BB.convertFromNewDbgValues(); + } +} + +void Function::setIsNewDbgInfoFormat(bool NewFlag) { + if (NewFlag && !IsNewDbgInfoFormat) + convertToNewDbgValues(); + else if (!NewFlag && IsNewDbgInfoFormat) + convertFromNewDbgValues(); +} + //===----------------------------------------------------------------------===// // Argument Implementation //===----------------------------------------------------------------------===// @@ -402,7 +423,7 @@ Function::Function(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, : GlobalObject(Ty, Value::FunctionVal, OperandTraits::op_begin(this), 0, Linkage, name, computeAddrSpace(AddrSpace, ParentModule)), - NumArgs(Ty->getNumParams()) { + NumArgs(Ty->getNumParams()), IsNewDbgInfoFormat(false) { assert(FunctionType::isValidReturnType(getReturnType()) && "invalid return type"); setGlobalObjectSubClassData(0); diff --git a/llvm/lib/IR/LLVMContextImpl.cpp b/llvm/lib/IR/LLVMContextImpl.cpp index 2076eeed94176..406850b7de248 100644 --- a/llvm/lib/IR/LLVMContextImpl.cpp +++ b/llvm/lib/IR/LLVMContextImpl.cpp @@ -45,6 +45,14 @@ LLVMContextImpl::LLVMContextImpl(LLVMContext &C) 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: diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h index 4cc3f8da6b75b..ebc444fcb6896 100644 --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -57,6 +57,7 @@ class AttributeListImpl; class AttributeSetNode; class BasicBlock; struct DiagnosticHandler; +class DPMarker; class ElementCount; class Function; class GlobalObject; @@ -1633,6 +1634,36 @@ class LLVMContextImpl { /// 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) { + return TrailingDPValues.lookup(B); + } + + void deleteTrailingDPValues(BasicBlock *B) { + TrailingDPValues.erase(B); + } }; } // end namespace llvm diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp index 7860280619bb9..61504e0f321c6 100644 --- a/llvm/lib/IR/Metadata.cpp +++ b/llvm/lib/IR/Metadata.cpp @@ -32,6 +32,7 @@ #include "llvm/IR/Constants.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DebugLoc.h" +#include "llvm/IR/DebugProgramInstruction.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalObject.h" #include "llvm/IR/GlobalVariable.h" @@ -147,6 +148,32 @@ void MetadataAsValue::untrack() { 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 +222,8 @@ SmallVector ReplaceableMetadataImpl::getAllArgListUsers() { 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 +239,25 @@ SmallVector ReplaceableMetadataImpl::getAllArgListUsers() { 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 +356,11 @@ void ReplaceableMetadataImpl::replaceAllUsesWith(Metadata *MD) { 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 +396,7 @@ void ReplaceableMetadataImpl::resolveAllUses(bool ResolveUsers) { auto Owner = Pair.second.first; if (!Owner) continue; - if (isa(Owner)) + if (!Owner.is()) continue; // Resolve MDNodes that point at this. @@ -356,19 +409,34 @@ void ReplaceableMetadataImpl::resolveAllUses(bool ResolveUsers) { } } +// Special handing of DIArgList is required in the RemoveDIs project, see +// commentry in DIArgList::handleChangedOperand for details. Hidden behind +// conditional compilation to avoid a compile time regression. ReplaceableMetadataImpl *ReplaceableMetadataImpl::getOrCreate(Metadata &MD) { +#ifdef EXPERIMENTAL_DEBUGINFO_ITERATORS + if (auto *ArgList = dyn_cast(&MD)) + return ArgList->Context.getOrCreateReplaceableUses(); +#endif if (auto *N = dyn_cast(&MD)) return N->isResolved() ? nullptr : N->Context.getOrCreateReplaceableUses(); return dyn_cast(&MD); } ReplaceableMetadataImpl *ReplaceableMetadataImpl::getIfExists(Metadata &MD) { +#ifdef EXPERIMENTAL_DEBUGINFO_ITERATORS + if (auto *ArgList = dyn_cast(&MD)) + return ArgList->Context.getReplaceableUses(); +#endif if (auto *N = dyn_cast(&MD)) return N->isResolved() ? nullptr : N->Context.getReplaceableUses(); return dyn_cast(&MD); } bool ReplaceableMetadataImpl::isReplaceable(const Metadata &MD) { +#ifdef EXPERIMENTAL_DEBUGINFO_ITERATORS + if (isa(&MD)) + return true; +#endif if (auto *N = dyn_cast(&MD)) return !N->isResolved(); return isa(&MD); diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp index dba660bbe5baf..17efe7956a21c 100644 --- a/llvm/lib/IR/Module.cpp +++ b/llvm/lib/IR/Module.cpp @@ -71,7 +71,8 @@ template class llvm::SymbolTableListTraits; Module::Module(StringRef MID, LLVMContext &C) : Context(C), ValSymTab(std::make_unique(-1)), - ModuleID(std::string(MID)), SourceFileName(std::string(MID)), DL("") { + ModuleID(std::string(MID)), SourceFileName(std::string(MID)), DL(""), + IsNewDbgInfoFormat(false) { Context.addModule(this); } diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 1fa6bc15113ff..b1d1075285c22 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2945,6 +2945,14 @@ void Verifier::visitBasicBlock(BasicBlock &BB) { { Check(I.getParent() == &BB, "Instruction has bogus parent pointer!"); } + + // Confirm that no issues arise from the debug program. + if (BB.IsNewDbgInfoFormat) { + // Configure the validate function to not fire assertions, instead print + // errors and return true if there's a problem. + bool RetVal = BB.validateDbgValues(false, true, OS); + Check(!RetVal, "Invalid configuration of new-debug-info data found"); + } } void Verifier::visitTerminator(Instruction &I) { diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp index 2d45d8557f2ef..e335def36647a 100644 --- a/llvm/lib/Linker/IRMover.cpp +++ b/llvm/lib/Linker/IRMover.cpp @@ -1135,6 +1135,7 @@ Error IRLinker::linkFunctionBody(Function &Dst, Function &Src) { Dst.setPrologueData(Src.getPrologueData()); if (Src.hasPersonalityFn()) Dst.setPersonalityFn(Src.getPersonalityFn()); + assert(Src.IsNewDbgInfoFormat == Dst.IsNewDbgInfoFormat); // Copy over the metadata attachments without remapping. Dst.copyMetadata(&Src, 0); @@ -1545,6 +1546,8 @@ Error IRLinker::run() { if (Error Err = SrcM->getMaterializer()->materializeMetadata()) return Err; + DstM.IsNewDbgInfoFormat = SrcM->IsNewDbgInfoFormat; + // Inherit the target data from the source module if the destination module // doesn't have one already. if (DstM.getDataLayout().isDefault()) diff --git a/llvm/lib/Target/AMDGPU/AMDGPURewriteOutArguments.cpp b/llvm/lib/Target/AMDGPU/AMDGPURewriteOutArguments.cpp index 2fde7afc0c14f..e2055db496d75 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPURewriteOutArguments.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPURewriteOutArguments.cpp @@ -331,6 +331,8 @@ bool AMDGPURewriteOutArguments::runOnFunction(Function &F) { NewFunc->removeRetAttrs(RetAttrs); // TODO: How to preserve metadata? + NewFunc->setIsNewDbgInfoFormat(F.IsNewDbgInfoFormat); + // Move the body of the function into the new rewritten function, and replace // this function with a stub. NewFunc->splice(NewFunc->begin(), &F); diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp index db9d5bc5a2cd2..fb3fa8d23daa0 100644 --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -161,6 +161,7 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM, F->getName()); NF->copyAttributesFrom(F); NF->copyMetadata(F, 0); + NF->setIsNewDbgInfoFormat(F->IsNewDbgInfoFormat); // The new function will have the !dbg metadata copied from the original // function. The original function may not be deleted, and dbg metadata need diff --git a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp index 24c0923505c24..4f65748c19e60 100644 --- a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp +++ b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp @@ -174,6 +174,7 @@ bool DeadArgumentEliminationPass::deleteDeadVarargs(Function &F) { NF->setComdat(F.getComdat()); F.getParent()->getFunctionList().insert(F.getIterator(), NF); NF->takeName(&F); + NF->IsNewDbgInfoFormat = F.IsNewDbgInfoFormat; // Loop over all the callers of the function, transforming the call sites // to pass in a smaller number of arguments into the new function. @@ -877,6 +878,7 @@ bool DeadArgumentEliminationPass::removeDeadStuffFromFunction(Function *F) { // it again. F->getParent()->getFunctionList().insert(F->getIterator(), NF); NF->takeName(F); + NF->IsNewDbgInfoFormat = F->IsNewDbgInfoFormat; // Loop over all the callers of the function, transforming the call sites to // pass in a smaller number of arguments into the new function. diff --git a/llvm/lib/Transforms/Utils/CloneFunction.cpp b/llvm/lib/Transforms/Utils/CloneFunction.cpp index d55208602b715..94c6abcf7b4e1 100644 --- a/llvm/lib/Transforms/Utils/CloneFunction.cpp +++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp @@ -44,6 +44,7 @@ BasicBlock *llvm::CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap, ClonedCodeInfo *CodeInfo, DebugInfoFinder *DIFinder) { BasicBlock *NewBB = BasicBlock::Create(BB->getContext(), "", F); + NewBB->IsNewDbgInfoFormat = BB->IsNewDbgInfoFormat; if (BB->hasName()) NewBB->setName(BB->getName() + NameSuffix); @@ -472,6 +473,7 @@ void PruningFunctionCloner::CloneBlock( BasicBlock *NewBB; Twine NewName(BB->hasName() ? Twine(BB->getName()) + NameSuffix : ""); BBEntry = NewBB = BasicBlock::Create(BB->getContext(), NewName, NewFunc); + NewBB->IsNewDbgInfoFormat = BB->IsNewDbgInfoFormat; // It is only legal to clone a function if a block address within that // function is never referenced outside of the function. Given that, we diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp index 84ed73333416c..034a9230bbadb 100644 --- a/llvm/unittests/IR/DebugInfoTest.cpp +++ b/llvm/unittests/IR/DebugInfoTest.cpp @@ -11,6 +11,7 @@ #include "llvm/AsmParser/Parser.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DebugProgramInstruction.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" @@ -23,6 +24,8 @@ using namespace llvm; +extern cl::opt UseNewDbgInfoFormat; + static std::unique_ptr parseIR(LLVMContext &C, const char *IR) { SMDiagnostic Err; std::unique_ptr Mod = parseAssemblyString(IR, Err, C); @@ -743,4 +746,310 @@ TEST(AssignmentTrackingTest, InstrMethods) { } } +// 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. + ExitBlock->IsNewDbgInfoFormat = true; + ExitBlock->createMarker(FirstInst); + ExitBlock->createMarker(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(), 2u); + 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(), 0u); + + // 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(), 1u); + // 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(), 2u); + // 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(); + + DPMarker *EndMarker = ExitBlock->getTrailingDPValues(); + ASSERT_NE(EndMarker, nullptr); + EXPECT_EQ(EndMarker->StoredDPValues.size(), 2u); + // Test again that it's those two DPValues, DPV1 and DPV2. + ItCount = 0; + for (DPValue &Item : EndMarker->getDbgValueRange()) { + EXPECT_TRUE((&Item == DPV2 && ItCount == 0) || + (&Item == DPV1 && ItCount == 1)); + EXPECT_EQ(Item.getMarker(), EndMarker); + ++ItCount; + } + + // Cleanup the trailing DPValue records and marker. + EndMarker->eraseFromParent(); + + // The record of those trailing DPValues would dangle and cause an assertion + // failure if it lived until the end of the LLVMContext. + ExitBlock->deleteTrailingDPValues(); +} + +TEST(MetadataTest, DPValueConversionRoutines) { + LLVMContext C; + + // For the purpose of this test, set and un-set the command line option + // corresponding to UseNewDbgInfoFormat. + UseNewDbgInfoFormat = true; + + 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) +)"); + + // Check that the conversion routines and utilities between dbg.value + // debug-info format and DPValues works. + Function *F = M->getFunction("f"); + BasicBlock *BB1 = &F->getEntryBlock(); + // First instruction should be a dbg.value. + EXPECT_TRUE(isa(BB1->front())); + EXPECT_FALSE(BB1->IsNewDbgInfoFormat); + // Validating the block for DPValues / DPMarkers shouldn't fail -- there's + // no data stored right now. + EXPECT_FALSE(BB1->validateDbgValues(false, false)); + + // Function and module should be marked as not having the new format too. + EXPECT_FALSE(F->IsNewDbgInfoFormat); + EXPECT_FALSE(M->IsNewDbgInfoFormat); + + // Now convert. + M->convertToNewDbgValues(); + EXPECT_TRUE(M->IsNewDbgInfoFormat); + EXPECT_TRUE(F->IsNewDbgInfoFormat); + EXPECT_TRUE(BB1->IsNewDbgInfoFormat); + + // There should now be no dbg.value instructions! + // Ensure the first instruction exists, the test all of them. + EXPECT_FALSE(isa(BB1->front())); + for (auto &BB : *F) + for (auto &I : BB) + EXPECT_FALSE(isa(I)); + + // There should be a DPMarker on each of the two instructions in the entry + // block, each containing one DPValue. + EXPECT_EQ(BB1->size(), 2u); + Instruction *FirstInst = &BB1->front(); + Instruction *SecondInst = FirstInst->getNextNode(); + ASSERT_TRUE(FirstInst->DbgMarker); + ASSERT_TRUE(SecondInst->DbgMarker); + EXPECT_NE(FirstInst->DbgMarker, SecondInst->DbgMarker); + EXPECT_EQ(FirstInst, FirstInst->DbgMarker->MarkedInstr); + EXPECT_EQ(SecondInst, SecondInst->DbgMarker->MarkedInstr); + + EXPECT_EQ(FirstInst->DbgMarker->StoredDPValues.size(), 1u); + DPValue *DPV1 = &*FirstInst->DbgMarker->getDbgValueRange().begin(); + EXPECT_EQ(DPV1->getMarker(), FirstInst->DbgMarker); + // Should point at %a, an argument. + EXPECT_TRUE(isa(DPV1->getVariableLocationOp(0))); + + EXPECT_EQ(SecondInst->DbgMarker->StoredDPValues.size(), 1u); + DPValue *DPV2 = &*SecondInst->DbgMarker->getDbgValueRange().begin(); + EXPECT_EQ(DPV2->getMarker(), SecondInst->DbgMarker); + // Should point at FirstInst. + EXPECT_EQ(DPV2->getVariableLocationOp(0), FirstInst); + + // There should be no DPValues / DPMarkers in the second block, but it should + // be marked as being in the new format. + BasicBlock *BB2 = BB1->getNextNode(); + EXPECT_TRUE(BB2->IsNewDbgInfoFormat); + for (auto &Inst : *BB2) + // Either there should be no marker, or it should be empty. + EXPECT_TRUE(!Inst.DbgMarker || Inst.DbgMarker->StoredDPValues.empty()); + + // Validating the first block should continue to not be a problem, + EXPECT_FALSE(BB1->validateDbgValues(false, false)); + // But if we were to break something, it should be able to fire. Don't attempt + // to comprehensively test the validator, it's a smoke-test rather than a + // "proper" verification pass. + DPV1->setMarker(nullptr); + // A marker pointing the wrong way should be an error. + EXPECT_TRUE(BB1->validateDbgValues(false, false)); + DPV1->setMarker(FirstInst->DbgMarker); + + DILocalVariable *DLV1 = DPV1->getVariable(); + DIExpression *Expr1 = DPV1->getExpression(); + DILocalVariable *DLV2 = DPV2->getVariable(); + DIExpression *Expr2 = DPV2->getExpression(); + + // Convert everything back to the "old" format and ensure it's right. + M->convertFromNewDbgValues(); + EXPECT_FALSE(M->IsNewDbgInfoFormat); + EXPECT_FALSE(F->IsNewDbgInfoFormat); + EXPECT_FALSE(BB1->IsNewDbgInfoFormat); + + EXPECT_EQ(BB1->size(), 4u); + ASSERT_TRUE(isa(BB1->front())); + DbgValueInst *DVI1 = cast(&BB1->front()); + // These dbg.values should still point at the same places. + EXPECT_TRUE(isa(DVI1->getVariableLocationOp(0))); + DbgValueInst *DVI2 = cast(DVI1->getNextNode()->getNextNode()); + EXPECT_EQ(DVI2->getVariableLocationOp(0), FirstInst); + + // Check a few fields too, + EXPECT_EQ(DVI1->getVariable(), DLV1); + EXPECT_EQ(DVI1->getExpression(), Expr1); + EXPECT_EQ(DVI2->getVariable(), DLV2); + EXPECT_EQ(DVI2->getExpression(), Expr2); + + UseNewDbgInfoFormat = false; +} + } // end namespace diff --git a/llvm/unittests/Transforms/Utils/LocalTest.cpp b/llvm/unittests/Transforms/Utils/LocalTest.cpp index a27af6efb2c81..8225774104575 100644 --- a/llvm/unittests/Transforms/Utils/LocalTest.cpp +++ b/llvm/unittests/Transforms/Utils/LocalTest.cpp @@ -15,6 +15,7 @@ #include "llvm/IR/BasicBlock.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugInfo.h" +#include "llvm/IR/DebugProgramInstruction.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" @@ -1277,3 +1278,69 @@ TEST(Local, ExpressionForConstant) { Expr = createExpression(ConstantFP::get(PPC_FP128Ty, 32), PPC_FP128Ty); EXPECT_EQ(Expr, nullptr); } + +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(); +} + diff --git a/llvm/utils/gn/secondary/llvm/lib/IR/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/IR/BUILD.gn index 298f449e4d2ca..8c2e8abad4a54 100644 --- a/llvm/utils/gn/secondary/llvm/lib/IR/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/IR/BUILD.gn @@ -31,6 +31,7 @@ static_library("IR") { "DataLayout.cpp", "DebugInfo.cpp", "DebugInfoMetadata.cpp", + "DebugProgramInstruction.cpp", "DebugLoc.cpp", "DiagnosticHandler.cpp", "DiagnosticInfo.cpp",