mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-28 06:00:30 +00:00
[DebugInfo] Honour variable fragments in LiveDebugValues
This patch makes the LiveDebugValues pass consider fragments when propagating DBG_VALUE insts between blocks, fixing PR41979. Fragment info for a variable location is added to the open-ranges key, which allows distinct fragments to be tracked separately. To handle overlapping fragments things become slightly funkier. To avoid excessive searching for overlaps in the data-flow part of LiveDebugValues, this patch: * Pre-computes pairings of fragments that overlap, for each DILocalVariable * During data-flow, whenever something happens that causes an open range to be terminated (via erase), any fragments pre-determined to overlap are also terminated. The effect of which is that when encountering a DBG_VALUE fragment that overlaps others, the overlapped fragments do not get propagated to other blocks. We still rely on later location-list building to correctly handle overlapping fragments within blocks. It's unclear whether a mixture of DBG_VALUEs with and without fragmented expressions are legitimate. To avoid suprises, this patch interprets a DBG_VALUE with no fragment as overlapping any DBG_VALUE _with_ a fragment. Differential Revision: https://reviews.llvm.org/D62904 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@363256 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
e4a2667a65
commit
d7e357f858
@ -2523,17 +2523,14 @@ public:
|
||||
createFragmentExpression(const DIExpression *Expr, unsigned OffsetInBits,
|
||||
unsigned SizeInBits);
|
||||
|
||||
/// Determine the relative position of the fragments described by this
|
||||
/// DIExpression and \p Other.
|
||||
/// Determine the relative position of the fragments passed in.
|
||||
/// Returns -1 if this is entirely before Other, 0 if this and Other overlap,
|
||||
/// 1 if this is entirely after Other.
|
||||
int fragmentCmp(const DIExpression *Other) const {
|
||||
auto Fragment1 = *getFragmentInfo();
|
||||
auto Fragment2 = *Other->getFragmentInfo();
|
||||
unsigned l1 = Fragment1.OffsetInBits;
|
||||
unsigned l2 = Fragment2.OffsetInBits;
|
||||
unsigned r1 = l1 + Fragment1.SizeInBits;
|
||||
unsigned r2 = l2 + Fragment2.SizeInBits;
|
||||
static int fragmentCmp(const FragmentInfo &A, const FragmentInfo &B) {
|
||||
uint64_t l1 = A.OffsetInBits;
|
||||
uint64_t l2 = B.OffsetInBits;
|
||||
uint64_t r1 = l1 + A.SizeInBits;
|
||||
uint64_t r2 = l2 + B.SizeInBits;
|
||||
if (r1 <= l2)
|
||||
return -1;
|
||||
else if (r2 <= l1)
|
||||
@ -2542,6 +2539,19 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Check if fragments overlap between a pair of FragmentInfos.
|
||||
static bool fragmentsOverlap(const FragmentInfo &A, const FragmentInfo &B) {
|
||||
return fragmentCmp(A, B) == 0;
|
||||
}
|
||||
|
||||
/// Determine the relative position of the fragments described by this
|
||||
/// DIExpression and \p Other. Calls static fragmentCmp implementation.
|
||||
int fragmentCmp(const DIExpression *Other) const {
|
||||
auto Fragment1 = *getFragmentInfo();
|
||||
auto Fragment2 = *Other->getFragmentInfo();
|
||||
return fragmentCmp(Fragment1, Fragment2);
|
||||
}
|
||||
|
||||
/// Check if fragments overlap between this DIExpression and \p Other.
|
||||
bool fragmentsOverlap(const DIExpression *Other) const {
|
||||
if (!isFragment() || !Other->isFragment())
|
||||
@ -2550,6 +2560,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const DIExpression::FragmentInfo &A,
|
||||
const struct DIExpression::FragmentInfo &B) {
|
||||
return std::tie(A.SizeInBits, A.OffsetInBits) ==
|
||||
std::tie(B.SizeInBits, B.OffsetInBits);
|
||||
}
|
||||
|
||||
inline bool operator<(const struct DIExpression::FragmentInfo &A,
|
||||
const struct DIExpression::FragmentInfo &B) {
|
||||
return std::tie(A.SizeInBits, A.OffsetInBits) <
|
||||
std::tie(B.SizeInBits, B.OffsetInBits);
|
||||
}
|
||||
|
||||
template <> struct DenseMapInfo<struct DIExpression::FragmentInfo> {
|
||||
using FragInfo = struct DIExpression::FragmentInfo;
|
||||
static const uint64_t MaxVal = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
static inline FragInfo getEmptyKey() { return {MaxVal, MaxVal}; }
|
||||
|
||||
static inline FragInfo getTombstoneKey() { return {MaxVal - 1, MaxVal - 1}; }
|
||||
|
||||
static unsigned getHashValue(const FragInfo &Frag) {
|
||||
return (Frag.SizeInBits & 0xffff) << 16 | (Frag.OffsetInBits & 0xffff);
|
||||
}
|
||||
|
||||
static bool isEqual(const FragInfo &A, const FragInfo &B) { return A == B; }
|
||||
};
|
||||
|
||||
/// Global variables.
|
||||
///
|
||||
/// TODO: Remove DisplayName. It's always equal to Name.
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/PostOrderIterator.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/SparseBitVector.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
@ -57,6 +58,7 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@ -107,24 +109,63 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
/// Based on std::pair so it can be used as an index into a DenseMap.
|
||||
using DebugVariableBase =
|
||||
std::pair<const DILocalVariable *, const DILocation *>;
|
||||
/// A potentially inlined instance of a variable.
|
||||
struct DebugVariable : public DebugVariableBase {
|
||||
DebugVariable(const DILocalVariable *Var, const DILocation *InlinedAt)
|
||||
: DebugVariableBase(Var, InlinedAt) {}
|
||||
using FragmentInfo = DIExpression::FragmentInfo;
|
||||
using OptFragmentInfo = Optional<DIExpression::FragmentInfo>;
|
||||
|
||||
const DILocalVariable *getVar() const { return this->first; }
|
||||
const DILocation *getInlinedAt() const { return this->second; }
|
||||
/// Storage for identifying a potentially inlined instance of a variable,
|
||||
/// or a fragment thereof.
|
||||
class DebugVariable {
|
||||
const DILocalVariable *Variable;
|
||||
OptFragmentInfo Fragment;
|
||||
const DILocation *InlinedAt;
|
||||
|
||||
bool operator<(const DebugVariable &DV) const {
|
||||
if (getVar() == DV.getVar())
|
||||
return getInlinedAt() < DV.getInlinedAt();
|
||||
return getVar() < DV.getVar();
|
||||
/// Fragment that will overlap all other fragments. Used as default when
|
||||
/// caller demands a fragment.
|
||||
static const FragmentInfo DefaultFragment;
|
||||
|
||||
public:
|
||||
DebugVariable(const DILocalVariable *Var, OptFragmentInfo &&FragmentInfo,
|
||||
const DILocation *InlinedAt)
|
||||
: Variable(Var), Fragment(FragmentInfo), InlinedAt(InlinedAt) {}
|
||||
|
||||
DebugVariable(const DILocalVariable *Var, OptFragmentInfo &FragmentInfo,
|
||||
const DILocation *InlinedAt)
|
||||
: Variable(Var), Fragment(FragmentInfo), InlinedAt(InlinedAt) {}
|
||||
|
||||
DebugVariable(const DILocalVariable *Var, const DIExpression *DIExpr,
|
||||
const DILocation *InlinedAt)
|
||||
: DebugVariable(Var, DIExpr->getFragmentInfo(), InlinedAt) {}
|
||||
|
||||
DebugVariable(const MachineInstr &MI)
|
||||
: DebugVariable(MI.getDebugVariable(),
|
||||
MI.getDebugExpression()->getFragmentInfo(),
|
||||
MI.getDebugLoc()->getInlinedAt()) {}
|
||||
|
||||
const DILocalVariable *getVar() const { return Variable; }
|
||||
const OptFragmentInfo &getFragment() const { return Fragment; }
|
||||
const DILocation *getInlinedAt() const { return InlinedAt; }
|
||||
|
||||
const FragmentInfo getFragmentDefault() const {
|
||||
return Fragment.getValueOr(DefaultFragment);
|
||||
}
|
||||
|
||||
static bool isFragmentDefault(FragmentInfo &F) {
|
||||
return F == DefaultFragment;
|
||||
}
|
||||
|
||||
bool operator==(const DebugVariable &Other) const {
|
||||
return std::tie(Variable, Fragment, InlinedAt) ==
|
||||
std::tie(Other.Variable, Other.Fragment, Other.InlinedAt);
|
||||
}
|
||||
|
||||
bool operator<(const DebugVariable &Other) const {
|
||||
return std::tie(Variable, Fragment, InlinedAt) <
|
||||
std::tie(Other.Variable, Other.Fragment, Other.InlinedAt);
|
||||
}
|
||||
};
|
||||
|
||||
friend struct llvm::DenseMapInfo<DebugVariable>;
|
||||
|
||||
/// A pair of debug variable and value location.
|
||||
struct VarLoc {
|
||||
// The location at which a spilled variable resides. It consists of a
|
||||
@ -159,8 +200,7 @@ private:
|
||||
} Loc;
|
||||
|
||||
VarLoc(const MachineInstr &MI, LexicalScopes &LS)
|
||||
: Var(MI.getDebugVariable(), MI.getDebugLoc()->getInlinedAt()), MI(MI),
|
||||
UVS(MI.getDebugLoc(), LS) {
|
||||
: Var(MI), MI(MI), UVS(MI.getDebugLoc(), LS) {
|
||||
static_assert((sizeof(Loc) == sizeof(uint64_t)),
|
||||
"hash does not cover all members of Loc");
|
||||
assert(MI.isDebugValue() && "not a DBG_VALUE");
|
||||
@ -183,8 +223,7 @@ private:
|
||||
/// The constructor for spill locations.
|
||||
VarLoc(const MachineInstr &MI, unsigned SpillBase, int SpillOffset,
|
||||
LexicalScopes &LS)
|
||||
: Var(MI.getDebugVariable(), MI.getDebugLoc()->getInlinedAt()), MI(MI),
|
||||
UVS(MI.getDebugLoc(), LS) {
|
||||
: Var(MI), MI(MI), UVS(MI.getDebugLoc(), LS) {
|
||||
assert(MI.isDebugValue() && "not a DBG_VALUE");
|
||||
assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE");
|
||||
Kind = SpillLocKind;
|
||||
@ -232,26 +271,35 @@ private:
|
||||
};
|
||||
using TransferMap = SmallVector<TransferDebugPair, 4>;
|
||||
|
||||
// Types for recording sets of variable fragments that overlap. For a given
|
||||
// local variable, we record all other fragments of that variable that could
|
||||
// overlap it, to reduce search time.
|
||||
using FragmentOfVar =
|
||||
std::pair<const DILocalVariable *, DIExpression::FragmentInfo>;
|
||||
using OverlapMap =
|
||||
DenseMap<FragmentOfVar, SmallVector<DIExpression::FragmentInfo, 1>>;
|
||||
|
||||
// Helper while building OverlapMap, a map of all fragments seen for a given
|
||||
// DILocalVariable.
|
||||
using VarToFragments =
|
||||
DenseMap<const DILocalVariable *, SmallSet<FragmentInfo, 4>>;
|
||||
|
||||
/// This holds the working set of currently open ranges. For fast
|
||||
/// access, this is done both as a set of VarLocIDs, and a map of
|
||||
/// DebugVariable to recent VarLocID. Note that a DBG_VALUE ends all
|
||||
/// previous open ranges for the same variable.
|
||||
class OpenRangesSet {
|
||||
VarLocSet VarLocs;
|
||||
SmallDenseMap<DebugVariableBase, unsigned, 8> Vars;
|
||||
SmallDenseMap<DebugVariable, unsigned, 8> Vars;
|
||||
OverlapMap &OverlappingFragments;
|
||||
|
||||
public:
|
||||
OpenRangesSet(OverlapMap &_OLapMap) : OverlappingFragments(_OLapMap) {}
|
||||
|
||||
const VarLocSet &getVarLocs() const { return VarLocs; }
|
||||
|
||||
/// Terminate all open ranges for Var by removing it from the set.
|
||||
void erase(DebugVariable Var) {
|
||||
auto It = Vars.find(Var);
|
||||
if (It != Vars.end()) {
|
||||
unsigned ID = It->second;
|
||||
VarLocs.reset(ID);
|
||||
Vars.erase(It);
|
||||
}
|
||||
}
|
||||
void erase(DebugVariable Var);
|
||||
|
||||
/// Terminate all open ranges listed in \c KillSet by removing
|
||||
/// them from the set.
|
||||
@ -262,7 +310,7 @@ private:
|
||||
}
|
||||
|
||||
/// Insert a new range into the set.
|
||||
void insert(unsigned VarLocID, DebugVariableBase Var) {
|
||||
void insert(unsigned VarLocID, DebugVariable Var) {
|
||||
VarLocs.set(VarLocID);
|
||||
Vars.insert({Var, VarLocID});
|
||||
}
|
||||
@ -310,6 +358,9 @@ private:
|
||||
VarLocInMBB &OutLocs, VarLocMap &VarLocIDs,
|
||||
TransferMap &Transfers, bool transferChanges);
|
||||
|
||||
void accumulateFragmentMap(MachineInstr &MI, VarToFragments &SeenFragments,
|
||||
OverlapMap &OLapMap);
|
||||
|
||||
bool join(MachineBasicBlock &MBB, VarLocInMBB &OutLocs, VarLocInMBB &InLocs,
|
||||
const VarLocMap &VarLocIDs,
|
||||
SmallPtrSet<const MachineBasicBlock *, 16> &Visited,
|
||||
@ -343,10 +394,46 @@ public:
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
namespace llvm {
|
||||
|
||||
template <> struct DenseMapInfo<LiveDebugValues::DebugVariable> {
|
||||
using DV = LiveDebugValues::DebugVariable;
|
||||
using OptFragmentInfo = LiveDebugValues::OptFragmentInfo;
|
||||
using FragmentInfo = LiveDebugValues::FragmentInfo;
|
||||
|
||||
// Empty key: no key should be generated that has no DILocalVariable.
|
||||
static inline DV getEmptyKey() {
|
||||
return DV(nullptr, OptFragmentInfo(), nullptr);
|
||||
}
|
||||
|
||||
// Difference in tombstone is that the Optional is meaningful
|
||||
static inline DV getTombstoneKey() {
|
||||
return DV(nullptr, OptFragmentInfo({0, 0}), nullptr);
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const DV &D) {
|
||||
unsigned HV = 0;
|
||||
const OptFragmentInfo &Fragment = D.getFragment();
|
||||
if (Fragment)
|
||||
HV = DenseMapInfo<FragmentInfo>::getHashValue(*Fragment);
|
||||
|
||||
return hash_combine(D.getVar(), HV, D.getInlinedAt());
|
||||
}
|
||||
|
||||
static bool isEqual(const DV &A, const DV &B) { return A == B; }
|
||||
};
|
||||
|
||||
}; // namespace llvm
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
const DIExpression::FragmentInfo
|
||||
LiveDebugValues::DebugVariable::DefaultFragment = {
|
||||
std::numeric_limits<uint64_t>::max(),
|
||||
std::numeric_limits<uint64_t>::min()};
|
||||
|
||||
char LiveDebugValues::ID = 0;
|
||||
|
||||
char &llvm::LiveDebugValuesID = LiveDebugValues::ID;
|
||||
@ -366,6 +453,39 @@ void LiveDebugValues::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
MachineFunctionPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
/// Erase a variable from the set of open ranges, and additionally erase any
|
||||
/// fragments that may overlap it.
|
||||
void LiveDebugValues::OpenRangesSet::erase(DebugVariable Var) {
|
||||
// Erasure helper.
|
||||
auto DoErase = [this](DebugVariable VarToErase) {
|
||||
auto It = Vars.find(VarToErase);
|
||||
if (It != Vars.end()) {
|
||||
unsigned ID = It->second;
|
||||
VarLocs.reset(ID);
|
||||
Vars.erase(It);
|
||||
}
|
||||
};
|
||||
|
||||
// Erase the variable/fragment that ends here.
|
||||
DoErase(Var);
|
||||
|
||||
// Extract the fragment. Interpret an empty fragment as one that covers all
|
||||
// possible bits.
|
||||
FragmentInfo ThisFragment = Var.getFragmentDefault();
|
||||
|
||||
// There may be fragments that overlap the designated fragment. Look them up
|
||||
// in the pre-computed overlap map, and erase them too.
|
||||
auto MapIt = OverlappingFragments.find({Var.getVar(), ThisFragment});
|
||||
if (MapIt != OverlappingFragments.end()) {
|
||||
for (auto Fragment : MapIt->second) {
|
||||
LiveDebugValues::OptFragmentInfo FragmentHolder;
|
||||
if (!DebugVariable::isFragmentDefault(Fragment))
|
||||
FragmentHolder = LiveDebugValues::OptFragmentInfo(Fragment);
|
||||
DoErase({Var.getVar(), FragmentHolder, Var.getInlinedAt()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Debug Range Extension Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -416,13 +536,14 @@ void LiveDebugValues::transferDebugValue(const MachineInstr &MI,
|
||||
if (!MI.isDebugValue())
|
||||
return;
|
||||
const DILocalVariable *Var = MI.getDebugVariable();
|
||||
const DIExpression *Expr = MI.getDebugExpression();
|
||||
const DILocation *DebugLoc = MI.getDebugLoc();
|
||||
const DILocation *InlinedAt = DebugLoc->getInlinedAt();
|
||||
assert(Var->isValidLocationForIntrinsic(DebugLoc) &&
|
||||
"Expected inlined-at fields to agree");
|
||||
|
||||
// End all previous ranges of Var.
|
||||
DebugVariable V(Var, InlinedAt);
|
||||
DebugVariable V(Var, Expr, InlinedAt);
|
||||
OpenRanges.erase(V);
|
||||
|
||||
// Add the VarLoc to OpenRanges from this DBG_VALUE.
|
||||
@ -464,8 +585,7 @@ void LiveDebugValues::insertTransferDebugPair(
|
||||
unsigned LocId = VarLocIDs.insert(VL);
|
||||
|
||||
// Close this variable's previous location range.
|
||||
DebugVariable V(DebugInstr->getDebugVariable(),
|
||||
DebugInstr->getDebugLoc()->getInlinedAt());
|
||||
DebugVariable V(*DebugInstr);
|
||||
OpenRanges.erase(V);
|
||||
|
||||
OpenRanges.insert(LocId, VL.Var);
|
||||
@ -757,6 +877,66 @@ bool LiveDebugValues::transferTerminatorInst(MachineInstr &MI,
|
||||
return Changed;
|
||||
}
|
||||
|
||||
/// Accumulate a mapping between each DILocalVariable fragment and other
|
||||
/// fragments of that DILocalVariable which overlap. This reduces work during
|
||||
/// the data-flow stage from "Find any overlapping fragments" to "Check if the
|
||||
/// known-to-overlap fragments are present".
|
||||
/// \param MI A previously unprocessed DEBUG_VALUE instruction to analyze for
|
||||
/// fragment usage.
|
||||
/// \param SeenFragments Map from DILocalVariable to all fragments of that
|
||||
/// Variable which are known to exist.
|
||||
/// \param OverlappingFragments The overlap map being constructed, from one
|
||||
/// Var/Fragment pair to a vector of fragments known to overlap.
|
||||
void LiveDebugValues::accumulateFragmentMap(MachineInstr &MI,
|
||||
VarToFragments &SeenFragments,
|
||||
OverlapMap &OverlappingFragments) {
|
||||
DebugVariable MIVar(MI);
|
||||
FragmentInfo ThisFragment = MIVar.getFragmentDefault();
|
||||
|
||||
// If this is the first sighting of this variable, then we are guaranteed
|
||||
// there are currently no overlapping fragments either. Initialize the set
|
||||
// of seen fragments, record no overlaps for the current one, and return.
|
||||
auto SeenIt = SeenFragments.find(MIVar.getVar());
|
||||
if (SeenIt == SeenFragments.end()) {
|
||||
SmallSet<FragmentInfo, 4> OneFragment;
|
||||
OneFragment.insert(ThisFragment);
|
||||
SeenFragments.insert({MIVar.getVar(), OneFragment});
|
||||
|
||||
OverlappingFragments.insert({{MIVar.getVar(), ThisFragment}, {}});
|
||||
return;
|
||||
}
|
||||
|
||||
// If this particular Variable/Fragment pair already exists in the overlap
|
||||
// map, it has already been accounted for.
|
||||
auto IsInOLapMap =
|
||||
OverlappingFragments.insert({{MIVar.getVar(), ThisFragment}, {}});
|
||||
if (!IsInOLapMap.second)
|
||||
return;
|
||||
|
||||
auto &ThisFragmentsOverlaps = IsInOLapMap.first->second;
|
||||
auto &AllSeenFragments = SeenIt->second;
|
||||
|
||||
// Otherwise, examine all other seen fragments for this variable, with "this"
|
||||
// fragment being a previously unseen fragment. Record any pair of
|
||||
// overlapping fragments.
|
||||
for (auto &ASeenFragment : AllSeenFragments) {
|
||||
// Does this previously seen fragment overlap?
|
||||
if (DIExpression::fragmentsOverlap(ThisFragment, ASeenFragment)) {
|
||||
// Yes: Mark the current fragment as being overlapped.
|
||||
ThisFragmentsOverlaps.push_back(ASeenFragment);
|
||||
// Mark the previously seen fragment as being overlapped by the current
|
||||
// one.
|
||||
auto ASeenFragmentsOverlaps =
|
||||
OverlappingFragments.find({MIVar.getVar(), ASeenFragment});
|
||||
assert(ASeenFragmentsOverlaps != OverlappingFragments.end() &&
|
||||
"Previously seen var fragment has no vector of overlaps");
|
||||
ASeenFragmentsOverlaps->second.push_back(ThisFragment);
|
||||
}
|
||||
}
|
||||
|
||||
AllSeenFragments.insert(ThisFragment);
|
||||
}
|
||||
|
||||
/// This routine creates OpenRanges and OutLocs.
|
||||
bool LiveDebugValues::process(MachineInstr &MI, OpenRangesSet &OpenRanges,
|
||||
VarLocInMBB &OutLocs, VarLocMap &VarLocIDs,
|
||||
@ -888,11 +1068,15 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {
|
||||
bool OLChanged = false;
|
||||
bool MBBJoined = false;
|
||||
|
||||
VarLocMap VarLocIDs; // Map VarLoc<>unique ID for use in bitvectors.
|
||||
OpenRangesSet OpenRanges; // Ranges that are open until end of bb.
|
||||
VarLocInMBB OutLocs; // Ranges that exist beyond bb.
|
||||
VarLocInMBB InLocs; // Ranges that are incoming after joining.
|
||||
TransferMap Transfers; // DBG_VALUEs associated with spills.
|
||||
VarLocMap VarLocIDs; // Map VarLoc<>unique ID for use in bitvectors.
|
||||
OverlapMap OverlapFragments; // Map of overlapping variable fragments
|
||||
OpenRangesSet OpenRanges(OverlapFragments);
|
||||
// Ranges that are open until end of bb.
|
||||
VarLocInMBB OutLocs; // Ranges that exist beyond bb.
|
||||
VarLocInMBB InLocs; // Ranges that are incoming after joining.
|
||||
TransferMap Transfers; // DBG_VALUEs associated with spills.
|
||||
|
||||
VarToFragments SeenFragments;
|
||||
|
||||
// Blocks which are artificial, i.e. blocks which exclusively contain
|
||||
// instructions without locations, or with line 0 locations.
|
||||
@ -914,10 +1098,14 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {
|
||||
// over the BBs. The LiveDebugVariables pass has already created DBG_VALUE
|
||||
// instructions for spills of registers that are known to be user variables
|
||||
// within the BB in which the spill occurs.
|
||||
for (auto &MBB : MF)
|
||||
for (auto &MI : MBB)
|
||||
for (auto &MBB : MF) {
|
||||
for (auto &MI : MBB) {
|
||||
process(MI, OpenRanges, OutLocs, VarLocIDs, Transfers,
|
||||
dontTransferChanges);
|
||||
if (MI.isDebugValue())
|
||||
accumulateFragmentMap(MI, SeenFragments, OverlapFragments);
|
||||
}
|
||||
}
|
||||
|
||||
auto hasNonArtificialLocation = [](const MachineInstr &MI) -> bool {
|
||||
if (const DebugLoc &DL = MI.getDebugLoc())
|
||||
|
@ -10,30 +10,6 @@
|
||||
; CHECK: DW_TAG_formal_parameter
|
||||
; CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] ({{.*}}
|
||||
; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_regx D16, DW_OP_piece 0x8, DW_OP_regx D17, DW_OP_piece 0x4
|
||||
; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_regx D16, DW_OP_piece 0x8, DW_OP_regx D17, DW_OP_piece 0x4
|
||||
|
||||
; FIXME: The second location list entry should not be emitted.
|
||||
;
|
||||
; The input to LiveDebugValues is:
|
||||
;
|
||||
; bb.0.entry:
|
||||
; [...]
|
||||
; Bcc %bb.2, 1, killed $cpsr, debug-location !10; simd.swift:5900:12
|
||||
; bb.1:
|
||||
; [...]
|
||||
; DBG_VALUE $q8, $noreg, !"self", !DIExpression(DW_OP_LLVM_fragment, 0, 96)
|
||||
; B %bb.3
|
||||
; bb.2.select.false:
|
||||
; [...]
|
||||
; DBG_VALUE $q8, $noreg, !"self", !DIExpression(DW_OP_LLVM_fragment, 0, 96)
|
||||
; bb.3.select.end:
|
||||
; [...]
|
||||
;
|
||||
; The two DBG_VALUEs in the blocks describe different fragments of the
|
||||
; variable. However, LiveDebugValues is not aware of fragments, so it will
|
||||
; incorrectly insert a copy of the first DBG_VALUE in bb.3.select.end, since
|
||||
; the debug values in its predecessor blocks are described by the same
|
||||
; register.
|
||||
|
||||
source_filename = "simd.ll"
|
||||
target datalayout = "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32"
|
||||
|
258
test/DebugInfo/MIR/X86/live-debug-values-fragments.mir
Normal file
258
test/DebugInfo/MIR/X86/live-debug-values-fragments.mir
Normal file
@ -0,0 +1,258 @@
|
||||
# RUN: llc %s -o - -run-pass=livedebugvalues | FileCheck %s
|
||||
#
|
||||
# The first func tests that, for two independent variable fragments defined in
|
||||
# blocks 1 and 2, _both_ their locations are propagated into the exit block.
|
||||
# LiveDebugValues previously ignored fragments and only propagated the last
|
||||
# variable location seen.
|
||||
#
|
||||
# The second func tests that overlapping variable fragments are handled
|
||||
# correctly -- that the redefinition of a particular fragment also clobbers
|
||||
# any overlaps. The dbg value of $cx in block one should not be propagated to
|
||||
# block two, because an overlapping dbg value inst has been encountered.
|
||||
#
|
||||
# The third function checks that DBG_VALUEs without fragments are seen as
|
||||
# overlapping any DBG_VALUE with a fragment.
|
||||
#
|
||||
# CHECK-LABEL: foo
|
||||
# CHECK-LABEL: bb.3.bb3:
|
||||
# CHECK: DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 32, 32)
|
||||
# CHECK-NEXT: DBG_VALUE $eax, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 32)
|
||||
# CHECK-NEXT: XOR32rr
|
||||
# CHECK-NEXT: RETQ
|
||||
#
|
||||
# CHECK-LABEL: bar
|
||||
# CHECK-LABEL: bb.0.entry:
|
||||
# CHECK: DBG_VALUE $cx, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 16)
|
||||
|
||||
# CHECK-LABEL: bb.1.bb1:
|
||||
# CHECK: DBG_VALUE $cx, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 16)
|
||||
# CHECK-NEXT: MOV32rr
|
||||
# CHECK-NEXT: DBG_VALUE $ax, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 8, 16)
|
||||
# CHECK-NEXT: JMP_1
|
||||
|
||||
# CHECK-LABEL: bb.2.bb2:
|
||||
# CHECK-NOT: DBG_VALUE
|
||||
# CHECK: DBG_VALUE $ax, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 8, 16)
|
||||
# CHECK-NEXT: MOV32rr
|
||||
# CHECK-NEXT: ADD32ri8
|
||||
# CHECK-NEXT: DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME !DIExpression(DW_OP_LLVM_fragment, 32, 32)
|
||||
# CHECK-NEXT: JMP_1
|
||||
|
||||
# CHECK-LABEL: bb.3.bb3:
|
||||
# CHECK: DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME !DIExpression(DW_OP_LLVM_fragment, 32, 32)
|
||||
# CHECK-NEXT: DBG_VALUE $ax, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 8, 16)
|
||||
# CHECK-NEXT: XOR32rr
|
||||
# CHECK-NEXT: RETQ
|
||||
|
||||
# CHECK-LABEL: baz
|
||||
# CHECK-LABEL: bb.0.entry:
|
||||
# CHECK: DBG_VALUE $cx, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 16)
|
||||
# CHECK-NEXT: JMP_1 %bb.1
|
||||
#
|
||||
# CHECK-LABEL: bb.1.bb1:
|
||||
# CHECK: DBG_VALUE $cx, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 16)
|
||||
# CHECK-NEXT: MOV32rr
|
||||
# CHECK-NEXT: DBG_VALUE $rdi, $noreg, !{{[0-9]+}}, !DIExpression()
|
||||
# CHECK-NEXT: JMP_1 %bb.2
|
||||
#
|
||||
# CHECK-LABEL: bb.2.bb2:
|
||||
# CHECK-NOT: DBG_VALUE $cx
|
||||
# CHECK: DBG_VALUE $rdi, $noreg, !{{[0-9]+}}, !DIExpression()
|
||||
# CHECK-NEXT: MOV32rr
|
||||
# CHECK-NEXT: ADD32ri8
|
||||
# CHECK-NEXT: DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 32, 32)
|
||||
# CHECK-NEXT: JMP_1 %bb.3
|
||||
#
|
||||
# CHECK-LABEL: bb.3.bb3:
|
||||
# CHECK-NOT: DBG_VALUE $rdi
|
||||
# CHECK-NOT: DBG_VALUE $cx
|
||||
# CHECK: DBG_VALUE $ebx, $noreg, !{{[0-9]+}},
|
||||
# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 32, 32)
|
||||
# CHECK-NEXT: XOR32rr
|
||||
# CHECK-NEXT: RETQ
|
||||
|
||||
--- |
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
|
||||
define i32 @foo(i32* %bees, i32* %output) !dbg !4 {
|
||||
entry:
|
||||
br label %bb1
|
||||
bb1:
|
||||
br label %bb2
|
||||
bb2:
|
||||
br label %bb3
|
||||
bb3:
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define i32 @bar(i32* %bees, i32* %output) !dbg !40 {
|
||||
entry:
|
||||
br label %bb1
|
||||
bb1:
|
||||
br label %bb2
|
||||
bb2:
|
||||
br label %bb3
|
||||
bb3:
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define i32 @baz(i32* %bees, i32* %output) !dbg !80 {
|
||||
entry:
|
||||
br label %bb1
|
||||
bb1:
|
||||
br label %bb2
|
||||
bb2:
|
||||
br label %bb3
|
||||
bb3:
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
!llvm.module.flags = !{!0, !100}
|
||||
!llvm.dbg.cu = !{!1}
|
||||
|
||||
!100 = !{i32 2, !"Dwarf Version", i32 4}
|
||||
!0 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
!1 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !2, producer: "beards", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
||||
!2 = !DIFile(filename: "bees.cpp", directory: ".")
|
||||
!3 = !DILocalVariable(name: "flannel", scope: !4, file: !2, line: 1, type: !16)
|
||||
!4 = distinct !DISubprogram(name: "nope", scope: !2, file: !2, line: 1, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !13, type: !14, isDefinition: true)
|
||||
!8 = !DILocation(line: 4, scope: !4)
|
||||
!13 = !{!3}
|
||||
!14 = !DISubroutineType(types: !15)
|
||||
!15 = !{!16}
|
||||
!16 = !DIBasicType(name: "looong", size: 64, align: 64, encoding: DW_ATE_signed)
|
||||
!40 = distinct !DISubprogram(name: "toast", scope: !2, file: !2, line: 1, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !53, type: !14, isDefinition: true)
|
||||
!43 = !DILocalVariable(name: "charm", scope: !40, file: !2, line: 1, type: !16)
|
||||
!48 = !DILocation(line: 4, scope: !40)
|
||||
!53 = !{!43}
|
||||
!80 = distinct !DISubprogram(name: "mort", scope: !2, file: !2, line: 1, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !93, type: !14, isDefinition: true)
|
||||
!83 = !DILocalVariable(name: "bodkin", scope: !80, file: !2, line: 1, type: !16)
|
||||
!88 = !DILocation(line: 4, scope: !80)
|
||||
!93 = !{!83}
|
||||
|
||||
...
|
||||
---
|
||||
name: foo
|
||||
tracksRegLiveness: true
|
||||
registers: []
|
||||
liveins:
|
||||
- { reg: '$rdi', virtual-reg: '' }
|
||||
body: |
|
||||
bb.0.entry:
|
||||
successors: %bb.1
|
||||
liveins: $rdi
|
||||
|
||||
$ecx = XOR32rr undef $ecx, undef $ecx, implicit-def $eflags
|
||||
JMP_1 %bb.1
|
||||
|
||||
bb.1.bb1 (align 4):
|
||||
successors: %bb.2
|
||||
liveins: $ecx, $rdi
|
||||
|
||||
$eax = MOV32rr killed $ecx, implicit-def $rax
|
||||
DBG_VALUE $eax, $noreg, !3, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !8
|
||||
JMP_1 %bb.2
|
||||
|
||||
bb.2.bb2:
|
||||
successors: %bb.3
|
||||
liveins: $eax
|
||||
|
||||
$ebx = MOV32rr $eax
|
||||
$ebx = ADD32ri8 $ebx, 3, implicit-def dead $eflags, implicit killed $rbx, implicit-def $rbx
|
||||
DBG_VALUE $ebx, $noreg, !3, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !8
|
||||
JMP_1 %bb.3
|
||||
|
||||
bb.3.bb3:
|
||||
liveins: $eax, $ebx
|
||||
$eax = XOR32rr killed $eax, killed $ebx, implicit-def $eflags
|
||||
RETQ $eax, debug-location !8
|
||||
|
||||
...
|
||||
---
|
||||
name: bar
|
||||
tracksRegLiveness: true
|
||||
registers: []
|
||||
liveins:
|
||||
- { reg: '$rdi', virtual-reg: '' }
|
||||
body: |
|
||||
bb.0.entry:
|
||||
successors: %bb.1
|
||||
liveins: $rdi
|
||||
|
||||
$ecx = XOR32rr undef $ecx, undef $ecx, implicit-def $eflags
|
||||
DBG_VALUE $cx, $noreg, !43, !DIExpression(DW_OP_LLVM_fragment, 0, 16), debug-location !48
|
||||
JMP_1 %bb.1
|
||||
|
||||
bb.1.bb1:
|
||||
successors: %bb.2
|
||||
liveins: $ecx, $rdi
|
||||
|
||||
$eax = MOV32rr killed $ecx, implicit-def $rax
|
||||
DBG_VALUE $ax, $noreg, !43, !DIExpression(DW_OP_LLVM_fragment, 8, 16), debug-location !48
|
||||
JMP_1 %bb.2
|
||||
|
||||
bb.2.bb2:
|
||||
successors: %bb.3
|
||||
liveins: $eax
|
||||
|
||||
$ebx = MOV32rr $eax
|
||||
$ebx = ADD32ri8 $ebx, 3, implicit-def dead $eflags, implicit killed $rbx, implicit-def $rbx
|
||||
DBG_VALUE $ebx, $noreg, !43, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !48
|
||||
JMP_1 %bb.3
|
||||
|
||||
bb.3.bb3:
|
||||
liveins: $eax, $ebx
|
||||
$eax = XOR32rr killed $eax, killed $ebx, implicit-def $eflags
|
||||
RETQ $eax, debug-location !48
|
||||
|
||||
...
|
||||
---
|
||||
name: baz
|
||||
tracksRegLiveness: true
|
||||
registers: []
|
||||
liveins:
|
||||
- { reg: '$rdi', virtual-reg: '' }
|
||||
body: |
|
||||
bb.0.entry:
|
||||
successors: %bb.1
|
||||
liveins: $rdi
|
||||
|
||||
$ecx = XOR32rr undef $ecx, undef $ecx, implicit-def $eflags
|
||||
DBG_VALUE $cx, $noreg, !83, !DIExpression(DW_OP_LLVM_fragment, 0, 16), debug-location !88
|
||||
JMP_1 %bb.1
|
||||
|
||||
bb.1.bb1:
|
||||
successors: %bb.2
|
||||
liveins: $ecx, $rdi
|
||||
|
||||
$eax = MOV32rr killed $ecx, implicit-def $rax
|
||||
DBG_VALUE $rdi, $noreg, !83, !DIExpression(), debug-location !88
|
||||
JMP_1 %bb.2
|
||||
|
||||
bb.2.bb2:
|
||||
successors: %bb.3
|
||||
liveins: $eax
|
||||
|
||||
$ebx = MOV32rr $eax
|
||||
$ebx = ADD32ri8 $ebx, 3, implicit-def dead $eflags, implicit killed $rbx, implicit-def $rbx
|
||||
DBG_VALUE $ebx, $noreg, !83, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !88
|
||||
JMP_1 %bb.3
|
||||
|
||||
bb.3.bb3:
|
||||
liveins: $eax, $ebx
|
||||
$eax = XOR32rr killed $eax, killed $ebx, implicit-def $eflags
|
||||
RETQ $eax, debug-location !88
|
||||
|
||||
...
|
Loading…
Reference in New Issue
Block a user