Reapply 7d77bbef4a, adding new debug-info classes

This reverts commit 957efa4ce4.

Original commit message below -- in this follow up, I've shifted
un-necessary inclusions of DebugProgramInstruction.h into being forward
declarations (fixes clang-compile time I hope), and a memory leak in the
DebugInfoTest.cpp IR unittests.

I also tracked a compile-time regression in D154080, more explanation
there, but the result of which is hiding some of the changes behind the
EXPERIMENTAL_DEBUGINFO_ITERATORS compile-time flag. This is tested by the
"new-debug-iterators" buildbot.

[DebugInfo][RemoveDIs] Add prototype storage classes for "new" debug-info

This patch adds a variety of classes needed to record variable location
debug-info without using the existing intrinsic approach, see the rationale
at [0].

The two added files and corresponding unit tests are the majority of the
plumbing required for this, but at this point isn't accessible from the
rest of LLVM as we need to stage it into the repo gently. An overview is
that classes are added for recording variable information attached to Real
(TM) instructions, in the form of DPValues and DPMarker objects. The
metadata-uses of DPValues is plumbed into the metadata hierachy, and a
field added to class Instruction, which are all stimulated in the unit
tests. The next few patches in this series add utilities to convert to/from
this new debug-info format and add instruction/block utilities to have
debug-info automatically updated in the background when various operations
occur.

This patch was reviewed in Phab in D153990 and D154080, I've squashed them
together into this commit as there are dependencies between the two
patches, and there's little profit in landing them separately.

[0] https://discourse.llvm.org/t/rfc-instruction-api-changes-needed-to-eliminate-debug-intrinsics-from-ir/68939
This commit is contained in:
Jeremy Morse 2023-11-08 14:58:34 +00:00
parent 671d10ad39
commit f1b0a54451
25 changed files with 1658 additions and 9 deletions

View File

@ -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<BasicBlock, Function> {
public:
using InstListType = SymbolTableList<Instruction, ilist_iterator_bits<true>>;
/// 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 @@ private:
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.

View File

@ -3765,6 +3765,10 @@ public:
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;
}

View File

@ -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 <cstdint>
#include <utility>
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<DPValue>, 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<DPValue>::iterator;
using const_self_iterator = simple_ilist<DPValue>::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<location_op_iterator,
std::bidirectional_iterator_tag, Value *> {
PointerUnion<ValueAsMetadata *, ValueAsMetadata **> 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<ValueAsMetadata *>()
? I.get<ValueAsMetadata *>()
: *I.get<ValueAsMetadata **>();
return VAM->getValue();
};
Value *operator*() {
ValueAsMetadata *VAM = I.is<ValueAsMetadata *>()
? I.get<ValueAsMetadata *>()
: *I.get<ValueAsMetadata **>();
return VAM->getValue();
}
location_op_iterator &operator++() {
if (I.is<ValueAsMetadata *>())
I = I.get<ValueAsMetadata *>() + 1;
else
I = I.get<ValueAsMetadata **>() + 1;
return *this;
}
location_op_iterator &operator--() {
if (I.is<ValueAsMetadata *>())
I = I.get<ValueAsMetadata *>() - 1;
else
I = I.get<ValueAsMetadata **>() - 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_op_iterator> 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<Value *> NewValues,
DIExpression *NewExpr);
void setVariable(DILocalVariable *NewVar) { Variable = NewVar; }
void setExpression(DIExpression *NewExpr) { Expression = NewExpr; }
unsigned getNumVariableLocationOps() const {
if (hasArgList())
return cast<DIArgList>(getRawLocation())->getArgs().size();
return 1;
}
bool hasArgList() const { return isa<DIArgList>(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<Value *, 4> 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<UndefValue>(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<ValueAsMetadata>(NewLocation) || isa<DIArgList>(NewLocation) ||
isa<MDNode>(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<uint64_t> 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<DPValue> 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<simple_ilist<DPValue>::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<simple_ilist<DPValue>::iterator>
cloneDebugInfoFrom(DPMarker *From,
std::optional<simple_ilist<DPValue>::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<simple_ilist<DPValue>::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

View File

@ -97,15 +97,28 @@ private:
friend class SymbolTableListTraits<Function>;
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 @@ public:
/// 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);
}

View File

@ -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<Instruction> {
static inline void deleteNode(Instruction *V);
@ -51,6 +53,12 @@ private:
/// 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.

View File

@ -43,6 +43,7 @@ namespace llvm {
class Module;
class ModuleSlotTracker;
class raw_ostream;
class DPValue;
template <typename T> class StringMapEntry;
template <typename ValueTy> class StringMapEntryStorage;
class Type;
@ -201,6 +202,78 @@ private:
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 @@ public:
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 @@ public:
/// Check whether metadata is replaceable.
static bool isReplaceable(const Metadata &MD);
using OwnerTy = PointerUnion<MetadataAsValue *, Metadata *>;
using OwnerTy = PointerUnion<MetadataAsValue *, Metadata *, DebugValueUser *>;
private:
/// Track a reference to metadata for an owner.
@ -275,8 +357,8 @@ private:
/// 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 @@ public:
static void SalvageDebugInfo(const Constant &C);
/// Returns the list of all DIArgList users of this.
SmallVector<Metadata *> getAllArgListUsers();
/// Returns the list of all DPValue users of this.
SmallVector<DPValue *> getAllDPValueUsers();
/// Resolve all uses of this.
///
@ -388,6 +472,9 @@ public:
SmallVector<Metadata *> getAllArgListUsers() {
return ReplaceableMetadataImpl::getAllArgListUsers();
}
SmallVector<DPValue *> getAllDPValueUsers() {
return ReplaceableMetadataImpl::getAllDPValueUsers();
}
static void handleDeletion(Value *V);
static void handleRAUW(Value *From, Value *To);
@ -1133,11 +1220,15 @@ public:
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);
}

View File

@ -213,6 +213,27 @@ private:
/// @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);

View File

@ -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<bool>
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<DPValue *, 4> DPVals;
for (Instruction &I : make_early_inc_range(InstList)) {
assert(!I.DbgMarker && "DbgMarker already set on old-format instrs?");
if (DbgValueInst *DVI = dyn_cast<DbgValueInst>(&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<PHINode>(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::SymbolTableListTraits<Instruction,
BasicBlock::BasicBlock(LLVMContext &C, const Twine &Name, Function *NewParent,
BasicBlock *InsertBefore)
: Value(Type::getLabelTy(C), Value::BasicBlockVal), Parent(nullptr) {
: Value(Type::getLabelTy(C), Value::BasicBlockVal),
IsNewDbgInfoFormat(false), Parent(nullptr) {
if (NewParent)
insertInto(NewParent, InsertBefore);
@ -56,12 +217,16 @@ BasicBlock::BasicBlock(LLVMContext &C, const Twine &Name, Function *NewParent,
"Cannot insert block before another block with no function!");
setName(Name);
if (NewParent)
setIsNewDbgInfoFormat(NewParent->IsNewDbgInfoFormat);
}
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);
}

View File

@ -17,6 +17,7 @@ add_llvm_component_library(LLVMCore
DataLayout.cpp
DebugInfo.cpp
DebugInfoMetadata.cpp
DebugProgramInstruction.cpp
DebugLoc.cpp
DiagnosticHandler.cpp
DiagnosticInfo.cpp

View File

@ -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();
}

View File

@ -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_op_iterator> 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<ValueAsMetadata *>(nullptr)),
location_op_iterator(static_cast<ValueAsMetadata *>(nullptr))};
// If operand is ValueAsMetadata, return a range over just that operand.
if (auto *VAM = dyn_cast<ValueAsMetadata>(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<DIArgList>(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<MDNode>(MD)->getNumOperands() == 0);
return {location_op_iterator(static_cast<ValueAsMetadata *>(nullptr)),
location_op_iterator(static_cast<ValueAsMetadata *>(nullptr))};
}
Value *DPValue::getVariableLocationOp(unsigned OpIdx) const {
auto *MD = getRawLocation();
if (!MD)
return nullptr;
if (auto *AL = dyn_cast<DIArgList>(MD))
return AL->getArgs()[OpIdx]->getValue();
if (isa<MDNode>(MD))
return nullptr;
assert(isa<ValueAsMetadata>(MD) &&
"Attempted to get location operand from DPValue with none.");
auto *V = cast<ValueAsMetadata>(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<MetadataAsValue>(V) ? dyn_cast<ValueAsMetadata>(
cast<MetadataAsValue>(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<MetadataAsValue>(NewValue)
? cast<MetadataAsValue>(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<ValueAsMetadata *, 4> 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<MetadataAsValue>(NewValue)
? cast<MetadataAsValue>(NewValue)->getMetadata()
: ValueAsMetadata::get(NewValue));
return;
}
SmallVector<ValueAsMetadata *, 4> 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<Value *> 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<ValueAsMetadata *, 4> 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<uint64_t> 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<DbgVariableIntrinsic>(
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<DPValue::self_iterator> 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<simple_ilist<DPValue>::iterator> DPMarker::cloneDebugInfoFrom(
DPMarker *From, std::optional<simple_ilist<DPValue>::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

View File

@ -81,6 +81,27 @@ static cl::opt<unsigned> 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<Function>::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);

View File

@ -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:

View File

@ -57,6 +57,7 @@ class AttributeListImpl;
class AttributeSetNode;
class BasicBlock;
struct DiagnosticHandler;
class DPMarker;
class ElementCount;
class Function;
class GlobalObject;
@ -1633,6 +1634,36 @@ public:
/// 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<BasicBlock *, DPMarker *> 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

View File

@ -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<DPValue *>(this); }
const DPValue *DebugValueUser::getUser() const {
return static_cast<const DPValue *>(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<Metadata **>(Ref) == &MD) &&
@ -195,6 +222,8 @@ SmallVector<Metadata *> ReplaceableMetadataImpl::getAllArgListUsers() {
SmallVector<std::pair<OwnerTy, uint64_t> *> MDUsersWithID;
for (auto Pair : UseMap) {
OwnerTy Owner = Pair.second.first;
if (Owner.isNull())
continue;
if (!isa<Metadata *>(Owner))
continue;
Metadata *OwnerMD = cast<Metadata *>(Owner);
@ -210,6 +239,25 @@ SmallVector<Metadata *> ReplaceableMetadataImpl::getAllArgListUsers() {
return MDUsers;
}
SmallVector<DPValue *> ReplaceableMetadataImpl::getAllDPValueUsers() {
SmallVector<std::pair<OwnerTy, uint64_t> *> DPVUsersWithID;
for (auto Pair : UseMap) {
OwnerTy Owner = Pair.second.first;
if (Owner.isNull())
continue;
if (!Owner.is<DebugValueUser *>())
continue;
DPVUsersWithID.push_back(&UseMap[Pair.first]);
}
llvm::sort(DPVUsersWithID, [](auto UserA, auto UserB) {
return UserA->second < UserB->second;
});
SmallVector<DPValue *> DPVUsers;
for (auto UserWithID : DPVUsersWithID)
DPVUsers.push_back(UserWithID->first.get<DebugValueUser *>()->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<DebugValueUser *>()) {
Owner.get<DebugValueUser *>()->getUser()->handleChangedLocation(MD);
continue;
}
// There's a Metadata owner -- dispatch.
Metadata *OwnerMD = cast<Metadata *>(Owner);
switch (OwnerMD->getMetadataID()) {
@ -343,7 +396,7 @@ void ReplaceableMetadataImpl::resolveAllUses(bool ResolveUsers) {
auto Owner = Pair.second.first;
if (!Owner)
continue;
if (isa<MetadataAsValue *>(Owner))
if (!Owner.is<Metadata *>())
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<DIArgList>(&MD))
return ArgList->Context.getOrCreateReplaceableUses();
#endif
if (auto *N = dyn_cast<MDNode>(&MD))
return N->isResolved() ? nullptr : N->Context.getOrCreateReplaceableUses();
return dyn_cast<ValueAsMetadata>(&MD);
}
ReplaceableMetadataImpl *ReplaceableMetadataImpl::getIfExists(Metadata &MD) {
#ifdef EXPERIMENTAL_DEBUGINFO_ITERATORS
if (auto *ArgList = dyn_cast<DIArgList>(&MD))
return ArgList->Context.getReplaceableUses();
#endif
if (auto *N = dyn_cast<MDNode>(&MD))
return N->isResolved() ? nullptr : N->Context.getReplaceableUses();
return dyn_cast<ValueAsMetadata>(&MD);
}
bool ReplaceableMetadataImpl::isReplaceable(const Metadata &MD) {
#ifdef EXPERIMENTAL_DEBUGINFO_ITERATORS
if (isa<DIArgList>(&MD))
return true;
#endif
if (auto *N = dyn_cast<MDNode>(&MD))
return !N->isResolved();
return isa<ValueAsMetadata>(&MD);

View File

@ -71,7 +71,8 @@ template class llvm::SymbolTableListTraits<GlobalIFunc>;
Module::Module(StringRef MID, LLVMContext &C)
: Context(C), ValSymTab(std::make_unique<ValueSymbolTable>(-1)),
ModuleID(std::string(MID)), SourceFileName(std::string(MID)), DL("") {
ModuleID(std::string(MID)), SourceFileName(std::string(MID)), DL(""),
IsNewDbgInfoFormat(false) {
Context.addModule(this);
}

View File

@ -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) {

View File

@ -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())

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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<bool> UseNewDbgInfoFormat;
static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
SMDiagnostic Err;
std::unique_ptr<Module> 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<Module> 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<DbgValueInst>(&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<DbgValueInst>(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<DbgValueInst>(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<Module> 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<DbgValueInst>(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<DbgValueInst>(BB1->front()));
for (auto &BB : *F)
for (auto &I : BB)
EXPECT_FALSE(isa<DbgValueInst>(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<Argument>(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<DbgValueInst>(BB1->front()));
DbgValueInst *DVI1 = cast<DbgValueInst>(&BB1->front());
// These dbg.values should still point at the same places.
EXPECT_TRUE(isa<Argument>(DVI1->getVariableLocationOp(0)));
DbgValueInst *DVI2 = cast<DbgValueInst>(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

View File

@ -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<Module> 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<Function>(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<DbgValueInst>(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();
}

View File

@ -31,6 +31,7 @@ static_library("IR") {
"DataLayout.cpp",
"DebugInfo.cpp",
"DebugInfoMetadata.cpp",
"DebugProgramInstruction.cpp",
"DebugLoc.cpp",
"DiagnosticHandler.cpp",
"DiagnosticInfo.cpp",