diff --git a/llvm/lib/CodeGen/LiveDebugValues.cpp b/llvm/lib/CodeGen/LiveDebugValues.cpp --- a/llvm/lib/CodeGen/LiveDebugValues.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues.cpp @@ -13,19 +13,20 @@ /// (registers, spill slots, constants) that a variable fragment might be /// located, qualified by a DIExpression and indirect-ness flag, while each /// variable is identified by a DebugVariable object. The availability of an -/// expression begins when a DBG_VALUE instruction specifies the location of a -/// DebugVariable, and continues until that location is clobbered or -/// re-specified by a different DBG_VALUE for the same DebugVariable. +/// expression begins when a DBG_VALUE or DBG_VALUE_LIST instruction specifies +/// the location of a DebugVariable, and continues until that location is +/// clobbered or re-specified by a different DBG_VALUE or DBG_VALUE_LIST for the +/// same DebugVariable. /// /// The cannonical "available expressions" problem doesn't have expression /// clobbering, instead when a variable is re-assigned, any expressions using /// that variable get invalidated. LiveDebugValues can map onto "available /// expressions" by having every register represented by a variable, which is -/// used in an expression that becomes available at a DBG_VALUE instruction. -/// When the register is clobbered, its variable is effectively reassigned, and -/// expressions computed from it become unavailable. A similar construct is -/// needed when a DebugVariable has its location re-specified, to invalidate -/// all other locations for that DebugVariable. +/// used in an expression that becomes available at a DBG_VALUE/DBG_VALUE_LIST +/// instruction. When the register is clobbered, its variable is effectively +/// reassigned, and expressions computed from it become unavailable. A similar +/// construct is needed when a DebugVariable has its location re-specified, to +/// invalidate all other locations for that DebugVariable. /// /// Using the dataflow analysis to compute the available expressions, we create /// a DBG_VALUE at the beginning of each block where the expression is @@ -71,20 +72,23 @@ /// that are not through dataflow. /// /// Within LiveDebugValues: each variable location is represented by a -/// VarLoc object that identifies the source variable, its current -/// machine-location, and the DBG_VALUE inst that specifies the location. Each -/// VarLoc is indexed in the (function-scope) \p VarLocMap, giving each VarLoc a -/// unique index. Rather than operate directly on machine locations, the -/// dataflow analysis in this pass identifies locations by their index in the -/// VarLocMap, meaning all the variable locations in a block can be described -/// by a sparse vector of VarLocMap indicies. +/// VarLoc object that identifies the source variable, the set of +/// machine-locations that currently describe it (a single location for +/// DBG_VALUE or multiple for DBG_VALUE_LIST), and the DBG_VALUE inst that +/// specifies the location. Each VarLoc is indexed in the (function-scope) \p +/// VarLocMap, giving each VarLoc a set of unique indexes, each of which +/// corresponds to one of the VarLoc's machine-locations and can be used to +/// lookup the VarLoc in the VarLocMap. Rather than operate directly on machine +/// locations, the dataflow analysis in this pass identifies locations by their +/// indices in the VarLocMap, meaning all the variable locations in a block can +/// be described by a sparse vector of VarLocMap indicies. /// /// All the storage for the dataflow analysis is local to the ExtendRanges /// method and passed down to helper methods. "OutLocs" and "InLocs" record the /// in and out lattice values for each block. "OpenRanges" maintains a list of /// variable locations and, with the "process" method, evaluates the transfer -/// function of each block. "flushPendingLocs" installs DBG_VALUEs for each -/// live-in location at the start of blocks, while "Transfers" records +/// function of each block. "flushPendingLocs" installs debug value instructions +/// for each live-in location at the start of blocks, while "Transfers" records /// transfers of values between machine-locations. /// /// We avoid explicitly representing the "Unknown" (\top) lattice value in the @@ -167,17 +171,6 @@ "Maximum input DBG_VALUE insts supported by debug range extension"), cl::init(50000), cl::Hidden); -// If @MI is a DBG_VALUE with debug value described by a defined -// register, returns the number of this register. In the other case, returns 0. -static Register isDbgValueDescribedByReg(const MachineInstr &MI) { - assert(MI.isDebugValue() && "expected a DBG_VALUE"); - assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE"); - // If location of variable is described using a register (directly - // or indirectly), this register is always a first operand. - return MI.getDebugOperand(0).isReg() ? MI.getDebugOperand(0).getReg() - : Register(); -} - /// If \p Op is a stack or frame register return true, otherwise return false. /// This is used to avoid basing the debug entry values on the registers, since /// we do not support it at the moment. @@ -202,6 +195,12 @@ // this prevents fallback to std::set::count() operations. using DefinedRegsSet = SmallSet; +// The IDs in this set correspond to MachineLocs in VarLocs, as well as VarLocs +// that represent Entry Values; every VarLoc in the set will also appear +// exactly once at Location=0. +// As a result, each VarLoc may appear more than once in this set, but each +// range corresponding to a Reg, SpillLoc, or EntryValue type will still be a +// true set, and the range Location=0 is the set of all VarLocs. using VarLocSet = CoalescingBitVector; /// A type-checked pair of {Register Location (or 0), Index}, used to index @@ -264,6 +263,13 @@ } }; +// Simple Set for storing all the VarLoc Indices at a Location bucket. +using VarLocsInRange = SmallSet; +// Vector of all `LocIndex`s for a given VarLoc; the same Location should not +// appear in any two of these, as each VarLoc appears at most once in any +// Location bucket. +using LocIndices = SmallVector; + class LiveDebugValues : public MachineFunctionPass { private: const TargetRegisterInfo *TRI; @@ -303,52 +309,100 @@ /// is moved. const MachineInstr &MI; - enum VarLocKind { + enum class MachineLocKind { InvalidKind = 0, RegisterKind, SpillLocKind, - ImmediateKind, + ImmediateKind + }; + + enum class EntryValueLocKind { + NonEntryValueKind = 0, EntryValueKind, EntryValueBackupKind, EntryValueCopyBackupKind - } Kind = InvalidKind; + } EVKind; - /// The value location. Stored separately to avoid repeatedly - /// extracting it from MI. - union { + typedef union { uint64_t RegNo; SpillLoc SpillLocation; uint64_t Hash; int64_t Immediate; const ConstantFP *FPImm; const ConstantInt *CImm; - } Loc; + } MachineLocValue; + + /// A single machine location; its Kind is either a register, spill + /// location, or immediate value. + struct MachineLoc { + MachineLocKind Kind; + MachineLocValue Value; + bool operator==(const MachineLoc &Other) const { + return Kind == Other.Kind && Value.Hash == Other.Value.Hash; + } + bool operator<(const MachineLoc &Other) const { + return std::tie(Kind, Value.Hash) < + std::tie(Other.Kind, Other.Value.Hash); + } + }; + + /// The set of machine locations used to determine the variable's value, in + /// conjunction with Expr. Initially populated with MI's debug operands, + /// but maybe modified independently afterwards. + SmallVector Locs; + /// Used to map the index of each location in Locs back to the index of its + /// original debug operand in MI. + SmallVector OrigLocMap; VarLoc(const MachineInstr &MI, LexicalScopes &LS) : Var(MI.getDebugVariable(), MI.getDebugExpression(), MI.getDebugLoc()->getInlinedAt()), - Expr(MI.getDebugExpression()), MI(MI) { - static_assert((sizeof(Loc) == sizeof(uint64_t)), + Expr(MI.getDebugExpression()), MI(MI), + EVKind(EntryValueLocKind::NonEntryValueKind) { + static_assert((sizeof(MachineLocValue) == sizeof(uint64_t)), "hash does not cover all members of Loc"); assert(MI.isDebugValue() && "not a DBG_VALUE"); - assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE"); - if (int RegNo = isDbgValueDescribedByReg(MI)) { - Kind = RegisterKind; - Loc.RegNo = RegNo; - } else if (MI.getDebugOperand(0).isImm()) { - Kind = ImmediateKind; - Loc.Immediate = MI.getDebugOperand(0).getImm(); - } else if (MI.getDebugOperand(0).isFPImm()) { - Kind = ImmediateKind; - Loc.FPImm = MI.getDebugOperand(0).getFPImm(); - } else if (MI.getDebugOperand(0).isCImm()) { - Kind = ImmediateKind; - Loc.CImm = MI.getDebugOperand(0).getCImm(); + assert(MI.isDebugValueList() || + MI.getNumOperands() == 4 && "malformed DBG_VALUE"); + for (const MachineOperand &Op : MI.debug_operands()) { + MachineLoc ML = GetLocForOp(Op); + auto It = find(Locs, ML); + if (It == Locs.end()) { + Locs.push_back(ML); + OrigLocMap.push_back(MI.getDebugOperandIndex(&Op)); + } else { + // ML duplicates an element in Locs; replace references to Op + // with references to the duplicating element. + unsigned OpIdx = Locs.size(); + unsigned DuplicatingIdx = std::distance(Locs.begin(), It); + Expr = DIExpression::replaceArg(Expr, OpIdx, DuplicatingIdx); + } } - // We create the debug entry values from the factory functions rather than - // from this ctor. - assert(Kind != EntryValueKind && !isEntryBackupLoc()); + // We create the debug entry values from the factory functions rather + // than from this ctor. + assert(EVKind != EntryValueLocKind::EntryValueKind && + !isEntryBackupLoc()); + } + + static MachineLoc GetLocForOp(const MachineOperand &Op) { + MachineLocKind Kind; + MachineLocValue Loc; + if (Op.isReg()) { + Kind = MachineLocKind::RegisterKind; + Loc.RegNo = Op.getReg(); + } else if (Op.isImm()) { + Kind = MachineLocKind::ImmediateKind; + Loc.Immediate = Op.getImm(); + } else if (Op.isFPImm()) { + Kind = MachineLocKind::ImmediateKind; + Loc.FPImm = Op.getFPImm(); + } else if (Op.isCImm()) { + Kind = MachineLocKind::ImmediateKind; + Loc.CImm = Op.getCImm(); + } else + llvm_unreachable("Invalid Op kind for MachineLoc."); + return {Kind, Loc}; } /// Take the variable and machine-location in DBG_VALUE MI, and build an @@ -356,10 +410,11 @@ static VarLoc CreateEntryLoc(const MachineInstr &MI, LexicalScopes &LS, const DIExpression *EntryExpr, unsigned Reg) { VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Kind = EntryValueKind; + assert(VL.Locs.size() == 1 && + VL.Locs[0].Kind == MachineLocKind::RegisterKind); + VL.EVKind = EntryValueLocKind::EntryValueKind; VL.Expr = EntryExpr; - VL.Loc.RegNo = Reg; + VL.Locs[0].Value.RegNo = Reg; return VL; } @@ -371,8 +426,9 @@ LexicalScopes &LS, const DIExpression *EntryExpr) { VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Kind = EntryValueBackupKind; + assert(VL.Locs.size() == 1 && + VL.Locs[0].Kind == MachineLocKind::RegisterKind); + VL.EVKind = EntryValueLocKind::EntryValueBackupKind; VL.Expr = EntryExpr; return VL; } @@ -385,32 +441,40 @@ const DIExpression *EntryExpr, unsigned NewReg) { VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Kind = EntryValueCopyBackupKind; + assert(VL.Locs.size() == 1 && + VL.Locs[0].Kind == MachineLocKind::RegisterKind); + VL.EVKind = EntryValueLocKind::EntryValueCopyBackupKind; VL.Expr = EntryExpr; - VL.Loc.RegNo = NewReg; + VL.Locs[0].Value.RegNo = NewReg; return VL; } /// Copy the register location in DBG_VALUE MI, updating the register to /// be NewReg. - static VarLoc CreateCopyLoc(const MachineInstr &MI, LexicalScopes &LS, + static VarLoc CreateCopyLoc(const VarLoc &OldVL, const MachineLoc &OldML, unsigned NewReg) { - VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Loc.RegNo = NewReg; - return VL; + VarLoc VL = OldVL; + for (size_t I = 0, E = VL.Locs.size(); I < E; ++I) + if (VL.Locs[I] == OldML) { + VL.Locs[I].Kind = MachineLocKind::RegisterKind; + VL.Locs[I].Value.RegNo = NewReg; + return VL; + } + llvm_unreachable("Should have found OldML in new VarLoc."); } /// Take the variable described by DBG_VALUE MI, and create a VarLoc /// locating it in the specified spill location. - static VarLoc CreateSpillLoc(const MachineInstr &MI, unsigned SpillBase, - int SpillOffset, LexicalScopes &LS) { - VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Kind = SpillLocKind; - VL.Loc.SpillLocation = {SpillBase, SpillOffset}; - return VL; + static VarLoc CreateSpillLoc(const VarLoc &OldVL, const MachineLoc &OldML, + unsigned SpillBase, int SpillOffset) { + VarLoc VL = OldVL; + for (int I = 0, E = VL.Locs.size(); I < E; ++I) + if (VL.Locs[I] == OldML) { + VL.Locs[I].Kind = MachineLocKind::SpillLocKind; + VL.Locs[I].Value.SpillLocation = {SpillBase, SpillOffset}; + return VL; + } + llvm_unreachable("Should have found OldML in new VarLoc."); } /// Create a DBG_VALUE representing this VarLoc in the given function. @@ -418,78 +482,140 @@ /// inlining information from the original DBG_VALUE instruction, which may /// have been several transfers ago. MachineInstr *BuildDbgValue(MachineFunction &MF) const { + assert(!isEntryBackupLoc() && + "Tried to produce DBG_VALUE for backup VarLoc"); const DebugLoc &DbgLoc = MI.getDebugLoc(); bool Indirect = MI.isIndirectDebugValue(); const auto &IID = MI.getDesc(); const DILocalVariable *Var = MI.getDebugVariable(); - const DIExpression *DIExpr = MI.getDebugExpression(); NumInserted++; - - switch (Kind) { - case EntryValueKind: - // An entry value is a register location -- but with an updated - // expression. The register location of such DBG_VALUE is always the one - // from the entry DBG_VALUE, it does not matter if the entry value was - // copied in to another register due to some optimizations. - return BuildMI(MF, DbgLoc, IID, Indirect, - MI.getDebugOperand(0).getReg(), Var, Expr); - case RegisterKind: - // Register locations are like the source DBG_VALUE, but with the - // register number from this VarLoc. - return BuildMI(MF, DbgLoc, IID, Indirect, Loc.RegNo, Var, DIExpr); - case SpillLocKind: { - // Spills are indirect DBG_VALUEs, with a base register and offset. - // Use the original DBG_VALUEs expression to build the spilt location - // on top of. FIXME: spill locations created before this pass runs - // are not recognized, and not handled here. - auto *SpillExpr = DIExpression::prepend( - DIExpr, DIExpression::ApplyOffset, Loc.SpillLocation.SpillOffset); - unsigned Base = Loc.SpillLocation.SpillBase; - return BuildMI(MF, DbgLoc, IID, true, Base, Var, SpillExpr); - } - case ImmediateKind: { - MachineOperand MO = MI.getDebugOperand(0); - return BuildMI(MF, DbgLoc, IID, Indirect, MO, Var, DIExpr); - } - case EntryValueBackupKind: - case EntryValueCopyBackupKind: - case InvalidKind: - llvm_unreachable( - "Tried to produce DBG_VALUE for invalid or backup VarLoc"); + const DIExpression *DIExpr = Expr; + SmallVector MOs; + for (unsigned I = 0, E = Locs.size(); I < E; ++I) { + MachineLocKind LocKind = Locs[I].Kind; + MachineLocValue Loc = Locs[I].Value; + const MachineOperand &Orig = MI.getDebugOperand(OrigLocMap[I]); + switch (LocKind) { + case MachineLocKind::RegisterKind: + // An entry value is a register location -- but with an updated + // expression. The register location of such DBG_VALUE is always the + // one from the entry DBG_VALUE, it does not matter if the entry value + // was copied in to another register due to some optimizations. + // Non-entry value register locations are like the source + // DBG_VALUE, but with the register number from this VarLoc. + MOs.push_back(MachineOperand::CreateReg( + EVKind == EntryValueLocKind::EntryValueKind ? Orig.getReg() + : Register(Loc.RegNo), + false)); + MOs.back().setIsDebug(); + break; + case MachineLocKind::SpillLocKind: { + // Spills are indirect DBG_VALUEs, with a base register and offset. + // Use the original DBG_VALUEs expression to build the spilt location + // on top of. FIXME: spill locations created before this pass runs + // are not recognized, and not handled here. + unsigned Base = Loc.SpillLocation.SpillBase; + if (MI.isNonListDebugValue()) { + DIExpr = DIExpression::prepend(DIExpr, DIExpression::ApplyOffset, + Loc.SpillLocation.SpillOffset); + Indirect = true; + } else { + SmallVector Ops; + DIExpression::appendOffset(Ops, Loc.SpillLocation.SpillOffset); + Ops.push_back(dwarf::DW_OP_deref); + DIExpr = DIExpression::appendOpsToArg(DIExpr, Ops, I); + } + MOs.push_back(MachineOperand::CreateReg(Base, false)); + MOs.back().setIsDebug(); + break; + } + case MachineLocKind::ImmediateKind: { + MOs.push_back(Orig); + break; + } + case MachineLocKind::InvalidKind: + llvm_unreachable("Tried to produce DBG_VALUE for invalid VarLoc"); + } } - llvm_unreachable("Unrecognized LiveDebugValues.VarLoc.Kind enum"); + return BuildMI(MF, DbgLoc, IID, Indirect, MOs, Var, DIExpr); } /// Is the Loc field a constant or constant object? - bool isConstant() const { return Kind == ImmediateKind; } + bool isConstant(MachineLocKind Kind) const { + return Kind == MachineLocKind::ImmediateKind; + } /// Check if the Loc field is an entry backup location. bool isEntryBackupLoc() const { - return Kind == EntryValueBackupKind || Kind == EntryValueCopyBackupKind; + return EVKind == EntryValueLocKind::EntryValueBackupKind || + EVKind == EntryValueLocKind::EntryValueCopyBackupKind; + } + + /// If this variable is described by register \p Reg holding the entry + /// value, return true. + bool isEntryValueBackupReg(Register Reg) const { + return EVKind == EntryValueLocKind::EntryValueBackupKind && usesReg(Reg); + } + + /// If this variable is described by register \p Reg holding a copy of the + /// entry value, return true. + bool isEntryValueCopyBackupReg(Register Reg) const { + return EVKind == EntryValueLocKind::EntryValueCopyBackupKind && + usesReg(Reg); } - /// If this variable is described by a register holding the entry value, - /// return it, otherwise return 0. - unsigned getEntryValueBackupReg() const { - if (Kind == EntryValueBackupKind) - return Loc.RegNo; - return 0; + /// If this variable is described in whole or part by \p Reg, return true. + bool usesReg(Register Reg) const { + MachineLoc RegML; + RegML.Kind = MachineLocKind::RegisterKind; + RegML.Value.RegNo = Reg; + return is_contained(Locs, RegML); } - /// If this variable is described by a register holding the copy of the - /// entry value, return it, otherwise return 0. - unsigned getEntryValueCopyBackupReg() const { - if (Kind == EntryValueCopyBackupKind) - return Loc.RegNo; - return 0; + /// If this variable is described in whole or part by \p Reg, return true. + unsigned getRegIdx(Register Reg) const { + for (unsigned Idx = 0; Idx < Locs.size(); ++Idx) + if (Locs[Idx].Kind == MachineLocKind::RegisterKind && + Locs[Idx].Value.RegNo == Reg) + return Idx; + llvm_unreachable("Could not find given Reg in Locs"); } - /// If this variable is described by a register, return it, - /// otherwise return 0. - unsigned isDescribedByReg() const { - if (Kind == RegisterKind) - return Loc.RegNo; - return 0; + /// If this variable is described in whole or part by 1 or more registers, + /// add each of them to \p Regs and return true. + bool getDescribingRegs(SmallVectorImpl &Regs) const { + bool AnyRegs = false; + for (auto Loc : Locs) + if (Loc.Kind == MachineLocKind::RegisterKind) { + Regs.push_back(Loc.Value.RegNo); + AnyRegs = true; + } + return AnyRegs; + } + + bool containsSpillLocs() const { + return any_of(Locs, [](VarLoc::MachineLoc ML) { + return ML.Kind == VarLoc::MachineLocKind::SpillLocKind; + }); + } + + /// If this variable is described in whole or part by \p SpillLocation, + /// return true. + bool usesSpillLoc(SpillLoc SpillLocation) const { + MachineLoc SpillML; + SpillML.Kind = MachineLocKind::SpillLocKind; + SpillML.Value.SpillLocation = SpillLocation; + return is_contained(Locs, SpillML); + } + + /// If this variable is described in whole or part by \p SpillLocation, + /// return the index . + unsigned getSpillLocIdx(SpillLoc SpillLocation) const { + for (unsigned Idx = 0; Idx < Locs.size(); ++Idx) + if (Locs[Idx].Kind == MachineLocKind::SpillLocKind && + Locs[Idx].Value.SpillLocation == SpillLocation) + return Idx; + llvm_unreachable("Could not find given SpillLoc in Locs"); } /// Determine whether the lexical scope of this value's debug location @@ -502,22 +628,23 @@ // TRI can be null. void dump(const TargetRegisterInfo *TRI, raw_ostream &Out = dbgs()) const { Out << "VarLoc("; - switch (Kind) { - case RegisterKind: - case EntryValueKind: - case EntryValueBackupKind: - case EntryValueCopyBackupKind: - Out << printReg(Loc.RegNo, TRI); - break; - case SpillLocKind: - Out << printReg(Loc.SpillLocation.SpillBase, TRI); - Out << "[" << Loc.SpillLocation.SpillOffset << "]"; - break; - case ImmediateKind: - Out << Loc.Immediate; - break; - case InvalidKind: - llvm_unreachable("Invalid VarLoc in dump method"); + for (const MachineLoc &MLoc : Locs) { + if (Locs.begin() != &MLoc) + Out << ", "; + switch (MLoc.Kind) { + case MachineLocKind::RegisterKind: + Out << printReg(MLoc.Value.RegNo, TRI); + break; + case MachineLocKind::SpillLocKind: + Out << printReg(MLoc.Value.SpillLocation.SpillBase, TRI); + Out << "[" << MLoc.Value.SpillLocation.SpillOffset << "]"; + break; + case MachineLocKind::ImmediateKind: + Out << MLoc.Value.Immediate; + break; + case MachineLocKind::InvalidKind: + llvm_unreachable("Invalid VarLoc in dump method"); + } } Out << ", \"" << Var.getVariable()->getName() << "\", " << *Expr << ", "; @@ -534,58 +661,67 @@ #endif bool operator==(const VarLoc &Other) const { - return Kind == Other.Kind && Var == Other.Var && - Loc.Hash == Other.Loc.Hash && Expr == Other.Expr; + return EVKind == Other.EVKind && Var == Other.Var && Expr == Other.Expr && + Locs == Other.Locs; } /// This operator guarantees that VarLocs are sorted by Variable first. bool operator<(const VarLoc &Other) const { - return std::tie(Var, Kind, Loc.Hash, Expr) < - std::tie(Other.Var, Other.Kind, Other.Loc.Hash, Other.Expr); + return std::tie(Var, EVKind, Locs, Expr) < + std::tie(Other.Var, Other.EVKind, Other.Locs, Other.Expr); } }; + using VarVec = SmallVector; + /// VarLocMap is used for two things: - /// 1) Assigning a unique LocIndex to a VarLoc. This LocIndex can be used to + /// 1) Assigning LocIndices to a VarLoc. The LocIndices can be used to /// virtually insert a VarLoc into a VarLocSet. /// 2) Given a LocIndex, look up the unique associated VarLoc. class VarLocMap { /// Map a VarLoc to an index within the vector reserved for its location /// within Loc2Vars. - std::map Var2Index; + std::map Var2Indices; /// Map a location to a vector which holds VarLocs which live in that /// location. SmallDenseMap> Loc2Vars; - /// Determine the 32-bit location reserved for \p VL, based on its kind. - static LocIndex::u32_location_t getLocationForVar(const VarLoc &VL) { - switch (VL.Kind) { - case VarLoc::RegisterKind: - assert((VL.Loc.RegNo < LocIndex::kFirstInvalidRegLocation) && - "Physreg out of range?"); - return VL.Loc.RegNo; - case VarLoc::SpillLocKind: - return LocIndex::kSpillLocation; - case VarLoc::EntryValueBackupKind: - case VarLoc::EntryValueCopyBackupKind: - return LocIndex::kEntryValueBackupLocation; - default: - return 0; - } - } - public: - /// Retrieve a unique LocIndex for \p VL. - LocIndex insert(const VarLoc &VL) { - LocIndex::u32_location_t Location = getLocationForVar(VL); - LocIndex::u32_index_t &Index = Var2Index[VL]; - if (!Index) { + /// Retrieve LocIndices for \p VL. + LocIndices insert(const VarLoc &VL) { + LocIndices &Indices = Var2Indices[VL]; + // If Indices is not empty, VL is already in the map. + if (!Indices.empty()) + return Indices; + SmallVector Locations; + // LocIndices are determined by EVKind and MLs; each Register has a + // unique location, while all SpillLocs use a single bucket, and any EV + // VarLocs use only the Backup bucket or none at all (except Location 0). + // LocIndices will always have an index at Location=0 as the last index. + if (VL.EVKind == VarLoc::EntryValueLocKind::NonEntryValueKind) { + VL.getDescribingRegs(Locations); + for (auto RegNo : Locations) + assert((RegNo < LocIndex::kFirstInvalidRegLocation) && + "Physreg out of range?"); + if (VL.containsSpillLocs()) + Locations.push_back(LocIndex::kSpillLocation); + } else if (VL.EVKind != VarLoc::EntryValueLocKind::EntryValueKind) + Locations.push_back(LocIndex::kEntryValueBackupLocation); + Locations.push_back(0); + for (LocIndex::u32_location_t Location : Locations) { auto &Vars = Loc2Vars[Location]; + Indices.push_back( + {Location, static_cast(Vars.size())}); Vars.push_back(VL); - Index = Vars.size(); } - return {Location, Index - 1}; + return Indices; + } + + LocIndices getAllIndices(const VarLoc &VL) const { + auto IndIt = Var2Indices.find(VL); + assert(IndIt != Var2Indices.end() && "VarLoc not tracked"); + return IndIt->second; } /// Retrieve the unique VarLoc associated with \p ID. @@ -617,6 +753,15 @@ using VarToFragments = DenseMap>; + static void collectVarLocsForReg(SmallVectorImpl &Collected, + uint32_t Reg, const VarLocSet &CollectFrom, + const VarLocMap &VarLocIDs); + + /// Get the registers which are used by VarLocs of kind RegisterKind tracked + /// by \p CollectFrom. + void getUsedRegs(const VarLocSet &CollectFrom, + SmallVectorImpl &UsedRegs) const; + /// 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 @@ -627,39 +772,44 @@ /// we will erase/insert from the EntryValuesBackupVars map, otherwise /// we perform the operation on the Vars. class OpenRangesSet { + VarLocSet::Allocator &Alloc; VarLocSet VarLocs; // Map the DebugVariable to recent primary location ID. - SmallDenseMap Vars; + SmallDenseMap Vars; // Map the DebugVariable to recent backup location ID. - SmallDenseMap EntryValuesBackupVars; + SmallDenseMap EntryValuesBackupVars; OverlapMap &OverlappingFragments; public: OpenRangesSet(VarLocSet::Allocator &Alloc, OverlapMap &_OLapMap) - : VarLocs(Alloc), OverlappingFragments(_OLapMap) {} + : Alloc(Alloc), VarLocs(Alloc), OverlappingFragments(_OLapMap) {} const VarLocSet &getVarLocs() const { return VarLocs; } + // Fetches all VarLocs in \p VarLocIDs and inserts them into \p Collected. + // This method is needed to get every VarLoc once, as each VarLoc may have + // multiple indices in a VarLocMap (corresponding to each applicable + // location), but all VarLocs appear exactly once at Location 0. + void getUniqueVarLocs(SmallVectorImpl &Collected, + const VarLocMap &VarLocIDs) const { + collectVarLocsForReg(Collected, 0, VarLocs, VarLocIDs); + } + /// Terminate all open ranges for VL.Var by removing it from the set. void erase(const VarLoc &VL); - /// Terminate all open ranges listed in \c KillSet by removing - /// them from the set. - void erase(const VarLocSet &KillSet, const VarLocMap &VarLocIDs); + /// Terminate all open ranges listed as indices in \c KillSet with + /// \c Location (default 0) by removing them from the set. + void erase(const VarLocsInRange &KillSet, const VarLocMap &VarLocIDs, + LocIndex::u32_location_t Location = 0); /// Insert a new range into the set. - void insert(LocIndex VarLocID, const VarLoc &VL); + void insert(LocIndices VarLocIDs, const VarLoc &VL); /// Insert a set of ranges. - void insertFromLocSet(const VarLocSet &ToLoad, const VarLocMap &Map) { - for (uint64_t ID : ToLoad) { - LocIndex Idx = LocIndex::fromRawInteger(ID); - const VarLoc &VarL = Map[Idx]; - insert(Idx, VarL); - } - } + void insertFromLocSet(const VarLocSet &ToLoad, const VarLocMap &Map); - llvm::Optional getEntryValueBackup(DebugVariable Var); + llvm::Optional getEntryValueBackup(DebugVariable Var); /// Empty the set. void clear() { @@ -682,18 +832,18 @@ getVarLocs().end()); } - /// Get all set IDs for VarLocs of kind RegisterKind in \p Reg. + /// Get all set IDs for VarLocs with MLs of kind RegisterKind in \p Reg. auto getRegisterVarLocs(Register Reg) const { return LocIndex::indexRangeForLocation(getVarLocs(), Reg); } - /// Get all set IDs for VarLocs of kind SpillLocKind. + /// Get all set IDs for VarLocs with MLs of kind SpillLocKind. auto getSpillVarLocs() const { return LocIndex::indexRangeForLocation(getVarLocs(), LocIndex::kSpillLocation); } - /// Get all set IDs for VarLocs of kind EntryValueBackupKind or + /// Get all set IDs for VarLocs of EVKind EntryValueBackupKind or /// EntryValueCopyBackupKind. auto getEntryValueBackupVarLocs() const { return LocIndex::indexRangeForLocation( @@ -701,16 +851,13 @@ } }; - /// Collect all VarLoc IDs from \p CollectFrom for VarLocs of kind - /// RegisterKind which are located in any reg in \p Regs. Insert collected IDs - /// into \p Collected. - void collectIDsForRegs(VarLocSet &Collected, const DefinedRegsSet &Regs, - const VarLocSet &CollectFrom) const; - - /// Get the registers which are used by VarLocs of kind RegisterKind tracked - /// by \p CollectFrom. - void getUsedRegs(const VarLocSet &CollectFrom, - SmallVectorImpl &UsedRegs) const; + /// Collect all VarLoc IDs from \p CollectFrom for VarLocs with MLs of kind + /// RegisterKind which are located in any reg in \p Regs. Insert collected + /// IDs into \p Collected. + static void collectIDsForRegs(VarLocsInRange &Collected, + const DefinedRegsSet &Regs, + const VarLocSet &CollectFrom, + const VarLocMap &VarLocIDs); VarLocSet &getVarLocsInMBB(const MachineBasicBlock *MBB, VarLocInMBB &Locs) { std::unique_ptr &VLS = Locs[MBB]; @@ -757,6 +904,7 @@ void insertTransferDebugPair(MachineInstr &MI, OpenRangesSet &OpenRanges, TransferMap &Transfers, VarLocMap &VarLocIDs, LocIndex OldVarID, TransferKind Kind, + const VarLoc::MachineLoc &OldLoc, unsigned NewReg = 0); void transferDebugValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, @@ -767,7 +915,7 @@ VarLocMap &VarLocIDs, const VarLoc &EntryVL); void emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers, - VarLocSet &KillSet); + VarLocsInRange &KillSet); void recordEntryValue(const MachineInstr &MI, const DefinedRegsSet &DefinedRegs, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs); @@ -855,8 +1003,9 @@ auto *EraseFrom = VL.isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; auto It = EraseFrom->find(VarToErase); if (It != EraseFrom->end()) { - LocIndex ID = It->second; - VarLocs.reset(ID.getAsRawInteger()); + LocIndices IDs = It->second; + for (LocIndex ID : IDs) + VarLocs.reset(ID.getAsRawInteger()); EraseFrom->erase(It); } }; @@ -883,26 +1032,46 @@ } } -void LiveDebugValues::OpenRangesSet::erase(const VarLocSet &KillSet, - const VarLocMap &VarLocIDs) { - VarLocs.intersectWithComplement(KillSet); - for (uint64_t ID : KillSet) { - const VarLoc *VL = &VarLocIDs[LocIndex::fromRawInteger(ID)]; - auto *EraseFrom = VL->isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; - EraseFrom->erase(VL->Var); +void LiveDebugValues::OpenRangesSet::erase(const VarLocsInRange &KillSet, + const VarLocMap &VarLocIDs, + LocIndex::u32_location_t Location) { + VarLocSet RemoveSet(Alloc); + for (uint32_t ID : KillSet) { + const VarLoc &VL = VarLocIDs[LocIndex(Location, ID)]; + auto *EraseFrom = VL.isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; + EraseFrom->erase(VL.Var); + LocIndices VLI = VarLocIDs.getAllIndices(VL); + for (LocIndex ID : VLI) + RemoveSet.set(ID.getAsRawInteger()); } + VarLocs.intersectWithComplement(RemoveSet); } -void LiveDebugValues::OpenRangesSet::insert(LocIndex VarLocID, +void LiveDebugValues::OpenRangesSet::insertFromLocSet(const VarLocSet &ToLoad, + const VarLocMap &Map) { + VarLocsInRange UniqueVarLocIDs; + DefinedRegsSet Regs; + Regs.insert(0); + collectIDsForRegs(UniqueVarLocIDs, Regs, ToLoad, Map); + for (uint64_t ID : UniqueVarLocIDs) { + LocIndex Idx = LocIndex::fromRawInteger(ID); + const VarLoc &VarL = Map[Idx]; + const LocIndices Indices = Map.getAllIndices(VarL); + insert(Indices, VarL); + } +} + +void LiveDebugValues::OpenRangesSet::insert(LocIndices VarLocIDs, const VarLoc &VL) { auto *InsertInto = VL.isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; - VarLocs.set(VarLocID.getAsRawInteger()); - InsertInto->insert({VL.Var, VarLocID}); + for (LocIndex ID : VarLocIDs) + VarLocs.set(ID.getAsRawInteger()); + InsertInto->insert({VL.Var, VarLocIDs}); } /// Return the Loc ID of an entry value backup location, if it exists for the /// variable. -llvm::Optional +llvm::Optional LiveDebugValues::OpenRangesSet::getEntryValueBackup(DebugVariable Var) { auto It = EntryValuesBackupVars.find(Var); if (It != EntryValuesBackupVars.end()) @@ -911,9 +1080,10 @@ return llvm::None; } -void LiveDebugValues::collectIDsForRegs(VarLocSet &Collected, +void LiveDebugValues::collectIDsForRegs(VarLocsInRange &Collected, const DefinedRegsSet &Regs, - const VarLocSet &CollectFrom) const { + const VarLocSet &CollectFrom, + const VarLocMap &VarLocIDs) { assert(!Regs.empty() && "Nothing to collect"); SmallVector SortedRegs; for (Register Reg : Regs) @@ -922,21 +1092,45 @@ auto It = CollectFrom.find(LocIndex::rawIndexForReg(SortedRegs.front())); auto End = CollectFrom.end(); for (uint32_t Reg : SortedRegs) { - // The half-open interval [FirstIndexForReg, FirstInvalidIndex) contains all - // possible VarLoc IDs for VarLocs of kind RegisterKind which live in Reg. + // The half-open interval [FirstIndexForReg, FirstInvalidIndex) contains + // all possible VarLoc IDs for VarLocs with MLs of kind RegisterKind which + // live in Reg. uint64_t FirstIndexForReg = LocIndex::rawIndexForReg(Reg); uint64_t FirstInvalidIndex = LocIndex::rawIndexForReg(Reg + 1); It.advanceToLowerBound(FirstIndexForReg); // Iterate through that half-open interval and collect all the set IDs. - for (; It != End && *It < FirstInvalidIndex; ++It) - Collected.set(*It); + for (; It != End && *It < FirstInvalidIndex; ++It) { + LocIndex ItIdx = LocIndex::fromRawInteger(*It); + const VarLoc &VL = VarLocIDs[ItIdx]; + LocIndices LI = VarLocIDs.getAllIndices(VL); + // For now, the back index is always Loc=0 + Collected.insert(LI.back().Index); + } if (It == End) return; } } +// This should be removed later, doesn't fit the new design. +void LiveDebugValues::collectVarLocsForReg(SmallVectorImpl &Collected, + uint32_t Reg, + const VarLocSet &CollectFrom, + const VarLocMap &VarLocIDs) { + // The half-open interval [FirstIndexForReg, FirstInvalidIndex) contains all + // possible VarLoc IDs for VarLocs with MLs of kind RegisterKind which live + // in Reg. + uint64_t FirstIndexForReg = LocIndex::rawIndexForReg(Reg); + uint64_t FirstInvalidIndex = LocIndex::rawIndexForReg(Reg + 1); + // Iterate through that half-open interval and collect all the set IDs. + for (auto It = CollectFrom.find(FirstIndexForReg), End = CollectFrom.end(); + It != End && *It < FirstInvalidIndex; ++It) { + LocIndex RegIdx = LocIndex::fromRawInteger(*It); + Collected.push_back(VarLocIDs[RegIdx]); + } +} + void LiveDebugValues::getUsedRegs(const VarLocSet &CollectFrom, SmallVectorImpl &UsedRegs) const { // All register-based VarLocs are assigned indices greater than or equal to @@ -979,9 +1173,10 @@ const VarLocSet &L = getVarLocsInMBB(&BB, V); if (L.empty()) continue; + SmallVector VarLocs; + collectVarLocsForReg(VarLocs, 0, L, VarLocIDs); Out << "MBB: " << BB.getNumber() << ":\n"; - for (uint64_t VLL : L) { - const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(VLL)]; + for (const VarLoc &VL : VarLocs) { Out << " Var: " << VL.Var.getVariable()->getName(); Out << " MI: "; VL.dump(TRI, Out); @@ -1028,32 +1223,30 @@ // If the DBG_VALUE comes from a copy instruction that copies the entry value, // it means the parameter's value has not changed and we should be able to use // its entry value. - bool TrySalvageEntryValue = false; Register Reg = MI.getDebugOperand(0).getReg(); auto I = std::next(MI.getReverseIterator()); const MachineOperand *SrcRegOp, *DestRegOp; - if (I != MI.getParent()->rend()) { - // TODO: Try to keep tracking of an entry value if we encounter a propagated - // DBG_VALUE describing the copy of the entry value. (Propagated entry value - // does not indicate the parameter modification.) - auto DestSrc = TII->isCopyInstr(*I); - if (!DestSrc) - return true; + if (I == MI.getParent()->rend()) + return true; - SrcRegOp = DestSrc->Source; - DestRegOp = DestSrc->Destination; - if (Reg != DestRegOp->getReg()) - return true; - TrySalvageEntryValue = true; - } + // TODO: Try to keep tracking of an entry value if we encounter a propagated + // DBG_VALUE describing the copy of the entry value. (Propagated entry value + // does not indicate the parameter modification.) + auto DestSrc = TII->isCopyInstr(*I); + if (!DestSrc) + return true; - if (TrySalvageEntryValue) { - for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) { - const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(ID)]; - if (VL.getEntryValueCopyBackupReg() == Reg && - VL.MI.getDebugOperand(0).getReg() == SrcRegOp->getReg()) - return false; - } + SrcRegOp = DestSrc->Source; + DestRegOp = DestSrc->Destination; + if (Reg != DestRegOp->getReg()) + return true; + + for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) { + const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(ID)]; + if (VL.isEntryValueCopyBackupReg(Reg) && + // Entry Values should not be variadic. + VL.MI.getDebugOperand(0).getReg() == SrcRegOp->getReg()) + return false; } return true; @@ -1079,7 +1272,7 @@ // If that is the case, we should stop tracking its entry value. auto EntryValBackupID = OpenRanges.getEntryValueBackup(V); if (Var->isParameter() && EntryValBackupID) { - const VarLoc &EntryVL = VarLocIDs[*EntryValBackupID]; + const VarLoc &EntryVL = VarLocIDs[EntryValBackupID->back()]; if (removeEntryValue(MI, OpenRanges, VarLocIDs, EntryVL)) { LLVM_DEBUG(dbgs() << "Deleting a DBG entry value because of: "; MI.print(dbgs(), /*IsStandalone*/ false, @@ -1089,22 +1282,23 @@ } } - if (isDbgValueDescribedByReg(MI) || MI.getDebugOperand(0).isImm() || - MI.getDebugOperand(0).isFPImm() || MI.getDebugOperand(0).isCImm()) { + if (all_of(MI.debug_operands(), [](const MachineOperand &MO) { + return (MO.isReg() && MO.getReg()) || MO.isImm() || MO.isFPImm() || + MO.isCImm(); + })) { // Use normal VarLoc constructor for registers and immediates. VarLoc VL(MI, LS); // End all previous ranges of VL.Var. OpenRanges.erase(VL); - LocIndex ID = VarLocIDs.insert(VL); + LocIndices IDs = VarLocIDs.insert(VL); // Add the VarLoc to OpenRanges from this DBG_VALUE. - OpenRanges.insert(ID, VL); - } else if (MI.hasOneMemOperand()) { + OpenRanges.insert(IDs, VL); + } else if (MI.memoperands().size() > 0) { llvm_unreachable("DBG_VALUE with mem operand encountered after regalloc?"); } else { // This must be an undefined location. If it has an open range, erase it. - assert(MI.getDebugOperand(0).isReg() && - MI.getDebugOperand(0).getReg() == 0 && + assert(MI.isUndefDebugValue() && "Unexpected non-undef DBG_VALUE encountered"); VarLoc VL(MI, LS); OpenRanges.erase(VL); @@ -1116,57 +1310,57 @@ OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers, - VarLocSet &KillSet) { + VarLocsInRange &KillSet) { // Do not insert entry value locations after a terminator. if (MI.isTerminator()) return; - for (uint64_t ID : KillSet) { - LocIndex Idx = LocIndex::fromRawInteger(ID); + for (LocIndex::u32_index_t ID : KillSet) { + LocIndex Idx = LocIndex(0, ID); const VarLoc &VL = VarLocIDs[Idx]; if (!VL.Var.getVariable()->isParameter()) continue; auto DebugVar = VL.Var; - Optional EntryValBackupID = + Optional EntryValBackupIDs = OpenRanges.getEntryValueBackup(DebugVar); // If the parameter has the entry value backup, it means we should // be able to use its entry value. - if (!EntryValBackupID) + if (!EntryValBackupIDs) continue; - const VarLoc &EntryVL = VarLocIDs[*EntryValBackupID]; - VarLoc EntryLoc = - VarLoc::CreateEntryLoc(EntryVL.MI, LS, EntryVL.Expr, EntryVL.Loc.RegNo); - LocIndex EntryValueID = VarLocIDs.insert(EntryLoc); - Transfers.push_back({&MI, EntryValueID}); - OpenRanges.insert(EntryValueID, EntryLoc); + const VarLoc &EntryVL = VarLocIDs[EntryValBackupIDs->back()]; + VarLoc EntryLoc = VarLoc::CreateEntryLoc(EntryVL.MI, LS, EntryVL.Expr, + EntryVL.Locs[0].Value.RegNo); + LocIndices EntryValueIDs = VarLocIDs.insert(EntryLoc); + Transfers.push_back({&MI, EntryValueIDs.back()}); + OpenRanges.insert(EntryValueIDs, EntryLoc); } } /// Create new TransferDebugPair and insert it in \p Transfers. The VarLoc -/// with \p OldVarID should be deleted form \p OpenRanges and replaced with +/// with \p OldVarID should be deleted from \p OpenRanges and replaced with /// new VarLoc. If \p NewReg is different than default zero value then the /// new location will be register location created by the copy like instruction, /// otherwise it is variable's location on the stack. void LiveDebugValues::insertTransferDebugPair( MachineInstr &MI, OpenRangesSet &OpenRanges, TransferMap &Transfers, VarLocMap &VarLocIDs, LocIndex OldVarID, TransferKind Kind, - unsigned NewReg) { - const MachineInstr *DebugInstr = &VarLocIDs[OldVarID].MI; + const VarLoc::MachineLoc &OldLoc, unsigned NewReg) { + const VarLoc &OldVarLoc = VarLocIDs[OldVarID]; auto ProcessVarLoc = [&MI, &OpenRanges, &Transfers, &VarLocIDs](VarLoc &VL) { - LocIndex LocId = VarLocIDs.insert(VL); + LocIndices LocIds = VarLocIDs.insert(VL); // Close this variable's previous location range. OpenRanges.erase(VL); // Record the new location as an open range, and a postponed transfer // inserting a DBG_VALUE for this location. - OpenRanges.insert(LocId, VL); + OpenRanges.insert(LocIds, VL); assert(!MI.isTerminator() && "Cannot insert DBG_VALUE after terminator"); - TransferDebugPair MIP = {&MI, LocId}; + TransferDebugPair MIP = {&MI, LocIds.back()}; Transfers.push_back(MIP); }; @@ -1178,7 +1372,7 @@ "No register supplied when handling a copy of a debug value"); // Create a DBG_VALUE instruction to describe the Var in its new // register location. - VarLoc VL = VarLoc::CreateCopyLoc(*DebugInstr, LS, NewReg); + VarLoc VL = VarLoc::CreateCopyLoc(OldVarLoc, OldLoc, NewReg); ProcessVarLoc(VL); LLVM_DEBUG({ dbgs() << "Creating VarLoc for register copy:"; @@ -1190,8 +1384,8 @@ // Create a DBG_VALUE instruction to describe the Var in its spilled // location. VarLoc::SpillLoc SpillLocation = extractSpillBaseRegAndOffset(MI); - VarLoc VL = VarLoc::CreateSpillLoc(*DebugInstr, SpillLocation.SpillBase, - SpillLocation.SpillOffset, LS); + VarLoc VL = VarLoc::CreateSpillLoc( + OldVarLoc, OldLoc, SpillLocation.SpillBase, SpillLocation.SpillOffset); ProcessVarLoc(VL); LLVM_DEBUG({ dbgs() << "Creating VarLoc for spill:"; @@ -1204,7 +1398,7 @@ "No register supplied when handling a restore of a debug value"); // DebugInstr refers to the pre-spill location, therefore we can reuse // its expression. - VarLoc VL = VarLoc::CreateCopyLoc(*DebugInstr, LS, NewReg); + VarLoc VL = VarLoc::CreateCopyLoc(OldVarLoc, OldLoc, NewReg); ProcessVarLoc(VL); LLVM_DEBUG({ dbgs() << "Creating VarLoc for restore:"; @@ -1274,8 +1468,8 @@ if (DeadRegs.empty()) return; - VarLocSet KillSet(Alloc); - collectIDsForRegs(KillSet, DeadRegs, OpenRanges.getVarLocs()); + VarLocsInRange KillSet; + collectIDsForRegs(KillSet, DeadRegs, OpenRanges.getVarLocs(), VarLocIDs); OpenRanges.erase(KillSet, VarLocIDs); if (auto *TPC = getAnalysisIfAvailable()) { @@ -1374,14 +1568,14 @@ // First, if there are any DBG_VALUEs pointing at a spill slot that is // written to, then close the variable location. The value in memory // will have changed. - VarLocSet KillSet(Alloc); + VarLocsInRange KillSet; if (isSpillInstruction(MI, MF)) { Loc = extractSpillBaseRegAndOffset(MI); for (uint64_t ID : OpenRanges.getSpillVarLocs()) { LocIndex Idx = LocIndex::fromRawInteger(ID); const VarLoc &VL = VarLocIDs[Idx]; - assert(VL.Kind == VarLoc::SpillLocKind && "Broken VarLocSet?"); - if (VL.Loc.SpillLocation == *Loc) { + assert(VL.containsSpillLocs() && "Broken VarLocSet?"); + if (VL.usesSpillLoc(*Loc)) { // This location is overwritten by the current instruction -- terminate // the open range, and insert an explicit DBG_VALUE $noreg. // @@ -1392,13 +1586,15 @@ // // At this stage, we already know which DBG_VALUEs are for spills and // where they are located; it's best to fix handle overwrites now. - KillSet.set(ID); - VarLoc UndefVL = VarLoc::CreateCopyLoc(VL.MI, LS, 0); - LocIndex UndefLocID = VarLocIDs.insert(UndefVL); - Transfers.push_back({&MI, UndefLocID}); + KillSet.insert(ID); + unsigned SpillLocIdx = VL.getSpillLocIdx(*Loc); + VarLoc::MachineLoc OldLoc = VL.Locs[SpillLocIdx]; + VarLoc UndefVL = VarLoc::CreateCopyLoc(VL, OldLoc, 0); + LocIndices UndefLocIDs = VarLocIDs.insert(UndefVL); + Transfers.push_back({&MI, UndefLocIDs.back()}); } } - OpenRanges.erase(KillSet, VarLocIDs); + OpenRanges.erase(KillSet, VarLocIDs, LocIndex::kSpillLocation); } // Try to recognise spill and restore instructions that may create a new @@ -1425,21 +1621,25 @@ for (uint64_t ID : TransferCandidates) { LocIndex Idx = LocIndex::fromRawInteger(ID); const VarLoc &VL = VarLocIDs[Idx]; + unsigned LocIdx; if (TKind == TransferKind::TransferSpill) { - assert(VL.isDescribedByReg() == Reg && "Broken VarLocSet?"); + assert(VL.usesReg(Reg) && "Broken VarLocSet?"); LLVM_DEBUG(dbgs() << "Spilling Register " << printReg(Reg, TRI) << '(' << VL.Var.getVariable()->getName() << ")\n"); + LocIdx = VL.getRegIdx(Reg); } else { - assert(TKind == TransferKind::TransferRestore && - VL.Kind == VarLoc::SpillLocKind && "Broken VarLocSet?"); - if (VL.Loc.SpillLocation != *Loc) + assert(TKind == TransferKind::TransferRestore && VL.containsSpillLocs() && + "Broken VarLocSet?"); + if (!VL.usesSpillLoc(*Loc)) // The spill location is not the location of a debug value. continue; LLVM_DEBUG(dbgs() << "Restoring Register " << printReg(Reg, TRI) << '(' << VL.Var.getVariable()->getName() << ")\n"); + LocIdx = VL.getSpillLocIdx(*Loc); } + VarLoc::MachineLoc MLoc = VL.Locs[LocIdx]; insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, Idx, TKind, - Reg); + MLoc, Reg); // FIXME: A comment should explain why it's correct to return early here, // if that is in fact correct. return; @@ -1488,17 +1688,16 @@ for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) { LocIndex Idx = LocIndex::fromRawInteger(ID); const VarLoc &VL = VarLocIDs[Idx]; - if (VL.getEntryValueBackupReg() == SrcReg) { + if (VL.isEntryValueBackupReg(SrcReg)) { LLVM_DEBUG(dbgs() << "Copy of the entry value: "; MI.dump();); VarLoc EntryValLocCopyBackup = VarLoc::CreateEntryCopyBackupLoc(VL.MI, LS, VL.Expr, DestReg); - // Stop tracking the original entry value. OpenRanges.erase(VL); // Start tracking the entry value copy. - LocIndex EntryValCopyLocID = VarLocIDs.insert(EntryValLocCopyBackup); - OpenRanges.insert(EntryValCopyLocID, EntryValLocCopyBackup); + LocIndices EntryValCopyLocIDs = VarLocIDs.insert(EntryValLocCopyBackup); + OpenRanges.insert(EntryValCopyLocIDs, EntryValLocCopyBackup); break; } } @@ -1509,9 +1708,12 @@ for (uint64_t ID : OpenRanges.getRegisterVarLocs(SrcReg)) { LocIndex Idx = LocIndex::fromRawInteger(ID); - assert(VarLocIDs[Idx].isDescribedByReg() == SrcReg && "Broken VarLocSet?"); + assert(VarLocIDs[Idx].usesReg(SrcReg) && "Broken VarLocSet?"); + VarLoc::MachineLocValue Loc; + Loc.RegNo = SrcReg; + VarLoc::MachineLoc MLoc{VarLoc::MachineLocKind::RegisterKind, Loc}; insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, Idx, - TransferKind::TransferCopy, DestReg); + TransferKind::TransferCopy, MLoc, DestReg); // FIXME: A comment should explain why it's correct to return early here, // if that is in fact correct. return; @@ -1524,12 +1726,14 @@ VarLocInMBB &OutLocs, const VarLocMap &VarLocIDs) { bool Changed = false; - - LLVM_DEBUG(for (uint64_t ID - : OpenRanges.getVarLocs()) { - // Copy OpenRanges to OutLocs, if not already present. - dbgs() << "Add to OutLocs in MBB #" << CurMBB->getNumber() << ": "; - VarLocIDs[LocIndex::fromRawInteger(ID)].dump(TRI); + LLVM_DEBUG({ + VarVec VarLocs; + OpenRanges.getUniqueVarLocs(VarLocs, VarLocIDs); + for (VarLoc &VL : VarLocs) { + // Copy OpenRanges to OutLocs, if not already present. + dbgs() << "Add to OutLocs in MBB #" << CurMBB->getNumber() << ": "; + VL.dump(TRI); + } }); VarLocSet &VLS = getVarLocsInMBB(CurMBB, OutLocs); Changed = VLS != OpenRanges.getVarLocs(); @@ -1652,12 +1856,11 @@ LLVM_DEBUG({ if (!InLocsT.empty()) { - for (uint64_t ID : InLocsT) + VarVec VarLocs; + collectVarLocsForReg(VarLocs, 0, InLocsT, VarLocIDs); + for (const VarLoc &VL : VarLocs) dbgs() << " gathered candidate incoming var: " - << VarLocIDs[LocIndex::fromRawInteger(ID)] - .Var.getVariable() - ->getName() - << "\n"; + << VL.Var.getVariable()->getName() << "\n"; } }); @@ -1706,10 +1909,12 @@ auto &MBB = const_cast(*Iter.first); VarLocSet &Pending = *Iter.second.get(); - for (uint64_t ID : Pending) { + SmallVector VarLocs; + collectVarLocsForReg(VarLocs, 0, Pending, VarLocIDs); + + for (VarLoc DiffIt : VarLocs) { // The ID location is live-in to MBB -- work out what kind of machine // location it is and create a DBG_VALUE. - const VarLoc &DiffIt = VarLocIDs[LocIndex::fromRawInteger(ID)]; if (DiffIt.isEntryBackupLoc()) continue; MachineInstr *MI = DiffIt.BuildDbgValue(*MBB.getParent()); @@ -1794,8 +1999,8 @@ DIExpression *NewExpr = DIExpression::prepend(MI.getDebugExpression(), DIExpression::EntryValue); VarLoc EntryValLocAsBackup = VarLoc::CreateEntryBackupLoc(MI, LS, NewExpr); - LocIndex EntryValLocID = VarLocIDs.insert(EntryValLocAsBackup); - OpenRanges.insert(EntryValLocID, EntryValLocAsBackup); + LocIndices EntryValLocIDs = VarLocIDs.insert(EntryValLocAsBackup); + OpenRanges.insert(EntryValLocIDs, EntryValLocAsBackup); } /// Calculate the liveness information for the given machine function and diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-clobber.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-clobber.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-clobber.mir @@ -0,0 +1,106 @@ +# RUN: llc -mtriple=x86_64-- -run-pass livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE_LIST +# +# Test that even after a move, clobbering a register terminates a DBG_VALUE_LIST. +# Check the same for DBG_VALUE $noreg. +# +# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "c" +# +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi +# CHECK: $rbx = COPY killed $rdi +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK-LABEL: bb.1: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK: $rsi = MOV64ri 0 +# CHECK-LABEL: bb.2: +# no live-in! +# CHECK: $rsi = MOV64ri 0 +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK-LABEL: bb.3: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# live-in to bb.3, then explicitly undef'd, should be no further locations +# propagated. +# CHECK-LABEL: bb.4: +--- | + ; ModuleID = 'test.cpp' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: norecurse nounwind readnone uwtable + define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 { + entry: + ret i32 0, !dbg !17 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10) + !13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10) + !14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 0, scope: !7) + !16 = !DILocation(line: 4, column: 12, scope: !7) + !17 = !DILocation(line: 4, column: 3, scope: !7) + +... +--- +name: _Z3fooii +fixedStack: + - { id: 0, type: spill-slot, offset: -32, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbx', callee-saved-restored: true } + - { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbp', callee-saved-restored: true } +body: | + bb.0.entry: + liveins: $rdi, $rsi + + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + $rbx = COPY killed $rdi + $rdi = MOV64ri 0 + JMP_1 %bb.1 + + bb.1: + liveins: $rbx, $rsi + $rsi = MOV64ri 0 + JMP_1 %bb.2 + + bb.2: + liveins: $rbx, $rsi + + $rsi = MOV64ri 0 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi, debug-location !15 + JMP_1 %bb.3 + + bb.3: + liveins: $rbx, $rsi + DBG_VALUE $noreg, $noreg, !14, !DIExpression(), debug-location !15 + JMP_1 %bb.4 + + bb.4: + liveins: $rbx, $rsi + RETQ $rbx, debug-location !17 + +... + diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-join.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-join.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-join.mir @@ -0,0 +1,160 @@ +# RUN: llc -mtriple=x86_64-- -run-pass livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE_LIST +# +# Test a series of joins, where: +# * The locations agree, although registers have changed, +# * A register down one of the predecessors has been def'd, +# * The register operands to DBG_VALUE_LIST have been swapped, +# * A spurious additional operand has been added to one path, +# * The expressions are not the same (plus -> minus). +# +# Each join block below checks for one DBG_VALUE_LIST: either we re-state the var +# location in a block for the next test, or it's created as a live-in and we +# use that for the next test. Two DBG_VALUE_LISTs in a block would represent +# a live-in that we didn't expect, and a test failure. +# +# Each conditional block should have at least one, possibly two. +# +# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "c" +# +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.1: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.2: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi +# CHECK-LABEL: bb.3: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.4: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: $rdi, $rsi +# CHECK-LABEL: bb.5: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: $rsi, $rdi +# CHECK-LABEL: bb.6: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.7: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: $rdi, $rsi, $rax +# CHECK-LABEL: bb.8: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.9: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: DW_OP_minus +# CHECK-LABEL: bb.10: +--- | + ; ModuleID = 'test.cpp' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: norecurse nounwind readnone uwtable + define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 { + entry: + ret i32 0, !dbg !17 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10) + !13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10) + !14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 0, scope: !7) + !16 = !DILocation(line: 4, column: 12, scope: !7) + !17 = !DILocation(line: 4, column: 3, scope: !7) + +... +--- +name: _Z3fooii +body: | + bb.0.entry: + liveins: $rdi, $rsi + + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.2, 4, implicit $eflags + + bb.1: + liveins: $rdi, $rsi + $rdi = MOV64ri 0 + $rsi = MOV64ri 0 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + + bb.2: + liveins: $rdi, $rsi + ; Should be a live-in loc here, + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.4, 4, implicit $eflags + + bb.3: + liveins: $rdi, $rsi + $rsi = MOV64ri 0 + + bb.4: + liveins: $rdi, $rsi + ; Should _not_ be a live-in loc here. + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.6, 4, implicit $eflags + + bb.5: + liveins: $rdi, $rsi + ; Flip some args, + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rsi, $rdi, debug-location !15 + + bb.6: + liveins: $rdi, $rsi + ; Should _not_ be a live-in loc here. + $rax = MOV64ri 0 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.8, 4, implicit $eflags + + bb.7: + liveins: $rdi, $rsi + ; Add an extra, spurious, unused argument + $rax = MOV64ri 1 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, $rax, debug-location !15 + + bb.8: + liveins: $rdi, $rsi + ; Should _not_ be a live-in loc here. + $rax = MOV64ri 0 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.10, 4, implicit $eflags + + bb.9: + liveins: $rdi, $rsi + ; Replace add with sub in the expr + $rax = MOV64ri 1 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_minus), $rdi, $rsi, debug-location !15 + + bb.10: + liveins: $rdi, $rsi + ; Should _not_ be a live-in loc here. + RETQ + +... + diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-movements.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-movements.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-movements.mir @@ -0,0 +1,90 @@ +# RUN: llc -mtriple=x86_64-- -run-pass livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE_LIST +# +# The MIR below moves values from argument registers to callee saved registers, +# moves that are followed by DBG_VALUEs and which should also result in +# DBG_VALUE_LISTs moving their operands. +# +# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "c" +# +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi +# CHECK: $rbx = COPY killed $rdi +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK-LABEL: bb.1: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK: $rbp = COPY killed $rsi +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rbp +# CHECK-LABEL: bb.2: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rbp +--- | + ; ModuleID = 'test.cpp' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: norecurse nounwind readnone uwtable + define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 { + entry: + ret i32 0, !dbg !17 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10) + !13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10) + !14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 0, scope: !7) + !16 = !DILocation(line: 4, column: 12, scope: !7) + !17 = !DILocation(line: 4, column: 3, scope: !7) + +... +--- +name: _Z3fooii +fixedStack: + - { id: 0, type: spill-slot, offset: -32, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbx', callee-saved-restored: true } + - { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbp', callee-saved-restored: true } +body: | + bb.0.entry: + liveins: $rdi, $rsi + + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + $rbx = COPY killed $rdi + $rdi = MOV64ri 0 + JMP_1 %bb.1 + + bb.1: + liveins: $rbx, $rsi + $rbp = COPY killed $rsi + $rsi = MOV64ri 0 + JMP_1 %bb.2 + + bb.2: + liveins: $rbx, $rbp + RETQ $rbp, debug-location !17 + +... + diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-spillrestore.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-spillrestore.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-spillrestore.mir @@ -0,0 +1,77 @@ +# RUN: llc -mtriple=x86_64-- -run-pass livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE_LIST +# +# A DBG_VALUE_LIST that has a component spilt and restored should had its +# expression and operands updated to refer to the stack for that period, and +# then return to normal once the value is restored. +# +# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "c" +# +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), +# CHECK-SAME: $rdi, $rsi, +# CHECK: MOV64mr $rsp, 1, $noreg, -16, $noreg, $rdi +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_constu, 8, DW_OP_minus, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_plus), +# CHECK-SAME: $rsp, $rsi, +# CHECK: $rdi = MOV64rm $rsp, 1, $noreg, -16, +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), +# CHECK-SAME: $rdi, $rsi, + +--- | + ; ModuleID = 'test.cpp' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: norecurse nounwind readnone uwtable + define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 { + entry: + ret i32 0, !dbg !17 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10) + !13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10) + !14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 0, scope: !7) + !16 = !DILocation(line: 4, column: 12, scope: !7) + !17 = !DILocation(line: 4, column: 3, scope: !7) + +... +--- +name: _Z3fooii +stack: + - { id: 0, offset: -16, size: 8, alignment: 8, type: spill-slot } +body: | + bb.0.entry: + liveins: $rdi, $rsi + + $rsp = frame-setup SUB64ri8 $rsp, 24, implicit-def dead $eflags + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + MOV64mr $rsp, 1, _, -16, _, $rdi, debug-location !15 :: (store 8 into %stack.0) + $rax = COPY killed $rdi + $rdi = MOV64ri 0 + $rdi = MOV64rm $rsp, 1, $noreg, -16, $noreg, debug-location !15 :: (load 8 from %stack.0) + RETQ +... +