Index: docs/SourceLevelDebugging.rst =================================================================== --- docs/SourceLevelDebugging.rst +++ docs/SourceLevelDebugging.rst @@ -570,6 +570,7 @@ metadata, ;; Reference to the type descriptor i32, ;; flags metadata ;; (optional) Reference to inline location + i32, ... ;; (optional) Complex expression (see below) } These descriptors are used to define variables local to a sub program. The @@ -588,6 +589,16 @@ Name the source variable name. Context and line indicate where the variable was defined. Type descriptor defines the declared type of the variable. +Local variables may have a complex address, which may consist of any +combination of ``OpPlus`` and ``OpDeref`` or an ``OpPiece``. The +``OpPlus`` operator takes one i32 argument that is an offset to add to +the address and ``OpDeref`` dereferences the address. The ``OpPiece`` +operator is used for (typically larger aggregate) variables that are +fragmented across several addresses. It takes two i32 arguments, an +offset and a size to describe which piece of the variable is at this +address. + + .. _format_common_intrinsics: Debugger intrinsic functions Index: include/llvm/CodeGen/AsmPrinter.h =================================================================== --- include/llvm/CodeGen/AsmPrinter.h +++ include/llvm/CodeGen/AsmPrinter.h @@ -430,7 +430,17 @@ /// encoding specified. virtual unsigned getISAEncoding() { return 0; } + /// EmitDwarfRegOp - Emit a partial dwarf register operation. + /// \param MLoc the register + /// \param PieceSize size and + /// \param Indirect offset of the piece in bits, if this is one + /// piece of an aggregate value. + void EmitDwarfRegOpPiece(ByteStreamer &BS, const MachineLocation &MLoc, + unsigned PieceSize, + unsigned PieceOffset) const; + /// EmitDwarfRegOp - Emit dwarf register operation. + /// \param Indirect whether this is a register-indirect address virtual void EmitDwarfRegOp(ByteStreamer &BS, const MachineLocation &MLoc, bool Indirect) const; Index: include/llvm/IR/DIBuilder.h =================================================================== --- include/llvm/IR/DIBuilder.h +++ include/llvm/IR/DIBuilder.h @@ -86,7 +86,7 @@ public: explicit DIBuilder(Module &M); - enum ComplexAddrKind { OpPlus=1, OpDeref }; + enum ComplexAddrKind { OpPlus=1, OpDeref, OpPiece }; enum DebugEmissionKind { FullDebug=1, LineTablesOnly }; /// finalize - Construct any deferred debug info descriptors. @@ -541,6 +541,16 @@ DITypeRef Ty, ArrayRef Addr, unsigned ArgNo = 0); + /// createVariablePiece - Create a descriptor to describe one part + /// of aggregate variable that is fragmented across multiple Values. + /// + /// @param Variable Variable that is partially represented by this. + /// @param OffsetInBytes Offset of the piece in bytes. + /// @param SizeInBytes Size of the piece in bytes. + DIVariable createVariablePiece(DIVariable Variable, + unsigned OffsetInBytes, + unsigned SizeInBytes); + /// createFunction - Create a new descriptor for the specified subprogram. /// See comments in DISubprogram for descriptions of these fields. /// @param Scope Function scope. Index: include/llvm/IR/DebugInfo.h =================================================================== --- include/llvm/IR/DebugInfo.h +++ include/llvm/IR/DebugInfo.h @@ -704,6 +704,17 @@ /// information for an inlined function arguments. bool isInlinedFnArgument(const Function *CurFn); + /// isVariablePiece - Return whether this is a piece of an aggregate + /// variable. + bool isVariablePiece() const; + /// getPieceOffset - Return the offset of this piece in bytes. + uint64_t getPieceOffset() const; + /// getPieceSize - Return the size of this piece in bytes. + uint64_t getPieceSize() const; + + /// Return the size reported by the variable's type. + unsigned getSizeInBits(const DITypeIdentifierMap &Map); + void printExtendedName(raw_ostream &OS) const; }; @@ -821,6 +832,9 @@ /// cleanseInlinedVariable - Remove inlined scope from the variable. DIVariable cleanseInlinedVariable(MDNode *DV, LLVMContext &VMContext); +/// getEntireVariable - Remove OpPiece exprs from the variable. +DIVariable getEntireVariable(DIVariable DV); + /// Construct DITypeIdentifierMap by going through retained types of each CU. DITypeIdentifierMap generateDITypeIdentifierMap(const NamedMDNode *CU_Nodes); Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -644,7 +644,11 @@ if (!Name.empty()) OS << Name << ":"; } - OS << V.getName() << " <- "; + OS << V.getName(); + if (V.isVariablePiece()) + OS << " [piece offset=" << V.getPieceOffset() + << " size="<getOperand(0).isReg() && MI->getOperand(1).isImm(); Index: lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp @@ -216,30 +216,48 @@ /// Emit a dwarf register operation for describing /// - a small value occupying only part of a register or /// - a small register representing only part of a value. -static void emitDwarfOpPiece(ByteStreamer &Streamer, unsigned Size, - unsigned Offset) { - assert(Size > 0); - if (Offset > 0) { +static void emitDwarfOpPiece(ByteStreamer &Streamer, + unsigned SizeInBits, + unsigned OffsetInBits) { + assert(SizeInBits > 8); + if (OffsetInBits > 0) { Streamer.EmitInt8(dwarf::DW_OP_bit_piece, "DW_OP_bit_piece"); - Streamer.EmitULEB128(Size, Twine(Size)); - Streamer.EmitULEB128(Offset, Twine(Offset)); + Streamer.EmitULEB128(SizeInBits, Twine(SizeInBits)); + Streamer.EmitULEB128(OffsetInBits, Twine(OffsetInBits)); } else { Streamer.EmitInt8(dwarf::DW_OP_piece, "DW_OP_piece"); - unsigned ByteSize = Size / 8; // Assuming 8 bits per byte. + assert( SizeInBits % 8 == 0 ); + unsigned ByteSize = SizeInBits / 8; // Assuming 8 bits per byte. Streamer.EmitULEB128(ByteSize, Twine(ByteSize)); } } +/// Emit a shift-right dwarf expression. +static void emitDwarfOpShr(ByteStreamer &Streamer, + unsigned ShiftBy) { + Streamer.EmitInt8(dwarf::DW_OP_constu, "DW_OP_constu"); + Streamer.EmitULEB128(ShiftBy); + Streamer.EmitInt8(dwarf::DW_OP_shr, "DW_OP_shr"); +} + /// Some targets do not provide a DWARF register number for every /// register. This function attempts to emit a dwarf register by /// emitting a piece of a super-register or by piecing together /// multiple subregisters that alias the register. -static void EmitDwarfRegOpPiece(ByteStreamer &Streamer, const AsmPrinter &AP, - const MachineLocation &MLoc) { +void AsmPrinter::EmitDwarfRegOpPiece(ByteStreamer &Streamer, + const MachineLocation &MLoc, + unsigned PieceSizeInBits, + unsigned PieceOffsetInBits) const { assert(!MLoc.isIndirect()); - const TargetRegisterInfo *TRI = AP.TM.getRegisterInfo(); + const TargetRegisterInfo *TRI = TM.getRegisterInfo(); int Reg = TRI->getDwarfRegNum(MLoc.getReg(), false); + if (Reg >= 0) { + emitDwarfRegOp(Streamer, Reg); + emitDwarfOpPiece(Streamer, PieceSizeInBits, PieceOffsetInBits); + return; + } + // Walk up the super-register chain until we find a valid number. // For example, EAX on x86_64 is a 32-bit piece of RAX with offset 0. for (MCSuperRegIterator SR(MLoc.getReg(), TRI); SR.isValid(); ++SR) { @@ -248,9 +266,19 @@ unsigned Idx = TRI->getSubRegIndex(*SR, MLoc.getReg()); unsigned Size = TRI->getSubRegIdxSize(Idx); unsigned Offset = TRI->getSubRegIdxOffset(Idx); - AP.OutStreamer.AddComment("super-register"); + OutStreamer.AddComment("super-register"); emitDwarfRegOp(Streamer, Reg); - emitDwarfOpPiece(Streamer, Size, Offset); + if (PieceOffsetInBits == Offset) { + emitDwarfOpPiece(Streamer, Size, Offset); + } else { + // If this is part of a variable in a sub-register at a + // non-zero offset, we need to manually shift the value into + // place, since the dwarf op_piece describes the part of the + // variable, not the position of the subregister. + emitDwarfOpPiece(Streamer, Size, PieceOffsetInBits); + if (Offset) + emitDwarfOpShr(Streamer, Offset); + } return; } } @@ -260,7 +288,7 @@ // // Keep track of the current position so we can emit the more // efficient DW_OP_piece. - unsigned CurPos = 0; + unsigned CurPos = PieceOffsetInBits; // The size of the register in bits, assuming 8 bits per byte. unsigned RegSize = TRI->getMinimalPhysRegClass(MLoc.getReg())->getSize() * 8; // Keep track of the bits in the register we already emitted, so we @@ -281,7 +309,7 @@ // If this sub-register has a DWARF number and we haven't covered // its range, emit a DWARF piece for it. if (Reg >= 0 && Intersection.any()) { - AP.OutStreamer.AddComment("sub-register"); + OutStreamer.AddComment("sub-register"); emitDwarfRegOp(Streamer, Reg); emitDwarfOpPiece(Streamer, Size, Offset == CurPos ? 0 : Offset); CurPos = Offset + Size; @@ -291,7 +319,7 @@ } } - if (CurPos == 0) { + if (CurPos == PieceOffsetInBits) { // FIXME: We have no reasonable way of handling errors in here. Streamer.EmitInt8(dwarf::DW_OP_nop, "nop (could not find a dwarf register number)"); @@ -317,8 +345,7 @@ } // Attempt to find a valid super- or sub-register. - if (!Indirect && !MLoc.isIndirect()) - return EmitDwarfRegOpPiece(Streamer, *this, MLoc); + return EmitDwarfRegOpPiece(Streamer, MLoc, 0, 0); } if (MLoc.isIndirect()) Index: lib/CodeGen/AsmPrinter/DebugLocEntry.h =================================================================== --- lib/CodeGen/AsmPrinter/DebugLocEntry.h +++ lib/CodeGen/AsmPrinter/DebugLocEntry.h @@ -23,75 +23,82 @@ const MCSymbol *Begin; const MCSymbol *End; - // Type of entry that this represents. - enum EntryType { E_Location, E_Integer, E_ConstantFP, E_ConstantInt }; - enum EntryType EntryKind; +public: + /// A single location or constant. + struct Value { + Value(const MDNode *Var, int64_t i) + : Variable(Var), EntryKind(E_Integer) { + Constant.Int = i; + } + Value(const MDNode *Var, const ConstantFP *CFP) + : Variable(Var), EntryKind(E_ConstantFP) { + Constant.CFP = CFP; + } + Value(const MDNode *Var, const ConstantInt *CIP) + : Variable(Var), EntryKind(E_ConstantInt) { + Constant.CIP = CIP; + } + Value(const MDNode *Var, MachineLocation Loc) + : Variable(Var), EntryKind(E_Location), Loc(Loc) { + } - union { - int64_t Int; - const ConstantFP *CFP; - const ConstantInt *CIP; - } Constants; + // The variable to which this location entry corresponds. + const MDNode *Variable; - // The location in the machine frame. - MachineLocation Loc; + // Type of entry that this represents. + enum EntryType { E_Location, E_Integer, E_ConstantFP, E_ConstantInt }; + enum EntryType EntryKind; - // The variable to which this location entry corresponds. - const MDNode *Variable; + // Either a constant, + union { + int64_t Int; + const ConstantFP *CFP; + const ConstantInt *CIP; + } Constant; - // The compile unit to which this location entry is referenced by. - const DwarfCompileUnit *Unit; + // Or a location in the machine frame. + MachineLocation Loc; - bool hasSameValueOrLocation(const DebugLocEntry &Next) { - if (EntryKind != Next.EntryKind) - return false; + bool operator==(const Value &other) const { + if (EntryKind != other.EntryKind) + return false; - bool EqualValues; - switch (EntryKind) { - case E_Location: - EqualValues = Loc == Next.Loc; - break; - case E_Integer: - EqualValues = Constants.Int == Next.Constants.Int; - break; - case E_ConstantFP: - EqualValues = Constants.CFP == Next.Constants.CFP; - break; - case E_ConstantInt: - EqualValues = Constants.CIP == Next.Constants.CIP; - break; + switch (EntryKind) { + case E_Location: + return Loc == other.Loc; + case E_Integer: + return Constant.Int == other.Constant.Int; + case E_ConstantFP: + return Constant.CFP == other.Constant.CFP; + case E_ConstantInt: + return Constant.CIP == other.Constant.CIP; + } + llvm_unreachable("unhandled EntryKind"); } - return EqualValues; - } + bool isLocation() const { return EntryKind == E_Location; } + bool isInt() const { return EntryKind == E_Integer; } + bool isConstantFP() const { return EntryKind == E_ConstantFP; } + bool isConstantInt() const { return EntryKind == E_ConstantInt; } + int64_t getInt() const { return Constant.Int; } + const ConstantFP *getConstantFP() const { return Constant.CFP; } + const ConstantInt *getConstantInt() const { return Constant.CIP; } + MachineLocation getLoc() const { return Loc; } + const MDNode *getVariable() const { return Variable; } + }; +private: + /// A list of locations/constants belonging to this entry. + SmallVector Values; + + /// The compile unit that this location entry is referenced by. + const DwarfCompileUnit *Unit; public: - DebugLocEntry() : Begin(0), End(0), Variable(0), Unit(0) { - Constants.Int = 0; - } - DebugLocEntry(const MCSymbol *B, const MCSymbol *E, MachineLocation &L, - const MDNode *V, const DwarfCompileUnit *U) - : Begin(B), End(E), Loc(L), Variable(V), Unit(U) { - Constants.Int = 0; - EntryKind = E_Location; - } - DebugLocEntry(const MCSymbol *B, const MCSymbol *E, int64_t i, - const MDNode *V, const DwarfCompileUnit *U) - : Begin(B), End(E), Variable(V), Unit(U) { - Constants.Int = i; - EntryKind = E_Integer; - } - DebugLocEntry(const MCSymbol *B, const MCSymbol *E, const ConstantFP *FPtr, - const MDNode *V, const DwarfCompileUnit *U) - : Begin(B), End(E), Variable(V), Unit(U) { - Constants.CFP = FPtr; - EntryKind = E_ConstantFP; - } - DebugLocEntry(const MCSymbol *B, const MCSymbol *E, const ConstantInt *IPtr, - const MDNode *V, const DwarfCompileUnit *U) - : Begin(B), End(E), Variable(V), Unit(U) { - Constants.CIP = IPtr; - EntryKind = E_ConstantInt; + DebugLocEntry() : Begin(0), End(0), Unit(0) {} + DebugLocEntry(const MCSymbol *B, const MCSymbol *E, + Value Val, const DwarfCompileUnit *U) + : Begin(B), End(E), Unit(U) { + Values.push_back(std::move(Val)); } /// \brief Attempt to merge this DebugLocEntry with Next and return @@ -99,24 +106,18 @@ /// share the same Loc/Constant and if Next immediately follows this /// Entry. bool Merge(const DebugLocEntry &Next) { - if (End == Next.Begin && hasSameValueOrLocation(Next)) { + assert(Next.Values.size() == 1 && "Next entry has more than one value"); + if ((End == Next.Begin && Values == Next.Values)) { End = Next.End; return true; } return false; } - bool isLocation() const { return EntryKind == E_Location; } - bool isInt() const { return EntryKind == E_Integer; } - bool isConstantFP() const { return EntryKind == E_ConstantFP; } - bool isConstantInt() const { return EntryKind == E_ConstantInt; } - int64_t getInt() const { return Constants.Int; } - const ConstantFP *getConstantFP() const { return Constants.CFP; } - const ConstantInt *getConstantInt() const { return Constants.CIP; } - const MDNode *getVariable() const { return Variable; } const MCSymbol *getBeginSym() const { return Begin; } const MCSymbol *getEndSym() const { return End; } const DwarfCompileUnit *getCU() const { return Unit; } - MachineLocation getLoc() const { return Loc; } + const ArrayRef getValues() const { return Values; } + void addValue(Value Val) { Values.push_back(Val); } }; } Index: lib/CodeGen/AsmPrinter/DwarfDebug.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.h +++ lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -86,7 +86,7 @@ StringRef getName() const { return Var.getName(); } DbgVariable *getAbstractVariable() const { return AbsVar; } const MachineInstr *getMInsn() const { return MInsn; } - void setMInsn(const MachineInstr *M) { MInsn = M; } + void setMInsn(const MachineInstr *M); int getFrameIndex() const { return FrameIndex; } void setFrameIndex(int FI) { FrameIndex = FI; } // Translate tag to proper Dwarf tag. Index: lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -109,6 +109,15 @@ namespace llvm { +void DbgVariable::setMInsn(const MachineInstr *M) { + MInsn = M; + // Update Var from MInsn. The DBG_VALUE may describe a specific + // piece of the variable in its complex expression. + assert(Var.getName() == MInsn->getDebugVariable().getName() + && "variable and variable in DBG_VALUE do not match"); + Var = MInsn->getDebugVariable(); +} + /// resolve - Look in the DwarfDebug map for the MDNode that /// corresponds to the reference. template T DbgVariable::resolve(DIRef Ref) const { @@ -1191,13 +1200,9 @@ } // Get .debug_loc entry for the instruction range starting at MI. -static DebugLocEntry getDebugLocEntry(AsmPrinter *Asm, - const MCSymbol *FLabel, - const MCSymbol *SLabel, - const MachineInstr *MI, - DwarfCompileUnit *Unit) { +static DebugLocEntry::Value +getDebugLocValue(const MachineInstr *MI) { const MDNode *Var = MI->getDebugVariable(); - assert(MI->getNumOperands() == 3); if (MI->getOperand(0).isReg()) { MachineLocation MLoc; @@ -1205,25 +1210,64 @@ // register-indirect address. if (!MI->getOperand(1).isImm()) MLoc.set(MI->getOperand(0).getReg()); - else + else { + // Based on the assumption that a pointer always fits into a + // register, an indirect piece should not occur. + assert(!DIVariable(Var).isVariablePiece() && "indirect piece"); MLoc.set(MI->getOperand(0).getReg(), MI->getOperand(1).getImm()); - return DebugLocEntry(FLabel, SLabel, MLoc, Var, Unit); + } + return DebugLocEntry::Value(Var, MLoc); } if (MI->getOperand(0).isImm()) - return DebugLocEntry(FLabel, SLabel, MI->getOperand(0).getImm(), Var, Unit); + return DebugLocEntry::Value(Var, MI->getOperand(0).getImm()); if (MI->getOperand(0).isFPImm()) - return DebugLocEntry(FLabel, SLabel, MI->getOperand(0).getFPImm(), - Var, Unit); + return DebugLocEntry::Value(Var, MI->getOperand(0).getFPImm()); if (MI->getOperand(0).isCImm()) - return DebugLocEntry(FLabel, SLabel, MI->getOperand(0).getCImm(), - Var, Unit); + return DebugLocEntry::Value(Var, MI->getOperand(0).getCImm()); llvm_unreachable("Unexpected 3 operand DBG_VALUE instruction!"); } +/// Determine whether two variable pieces overlap. +static bool piecesOverlap(DIVariable P1, DIVariable P2) { + assert(P1.isVariablePiece() && P1.isVariablePiece() && "must be a piece"); + return (P1.getPieceOffset() < P2.getPieceOffset() + P2.getPieceSize() && + P2.getPieceOffset() < P1.getPieceOffset() + P1.getPieceSize()); +} + +typedef SmallVectorImpl::iterator HistoryIter; + +/// The range of a variable piece is truncated if its value is +/// clobbered, or by an overlapping piece. Return an iterator to the +/// history entry that ends the range of DbgVal. +static HistoryIter +getEndOfRange(HistoryIter DbgVal, HistoryIter End) { + DIVariable DV0 = (*DbgVal)->getDebugVariable(); + if (!DV0.isVariablePiece()) + return DbgVal+1; + + for (HistoryIter I=DbgVal+1; I != End; ++I) { + // Ignore nullptr entries. + if (!*I) + continue; + + // Clobber. + if (!(*I)->isDebugValue()) + return I; + + // Non-piece or overlapping piece? + DIVariable DVI = (*I)->getDebugVariable(); + if (!DVI.isVariablePiece() || piecesOverlap(DV0, DVI)) + return I; + } + return End; +} + // Find variables for each lexical scope. void DwarfDebug::collectVariableInfo(SmallPtrSet &Processed) { + LexicalScope *FnScope = LScopes.getCurrentFunctionScope(); + DwarfCompileUnit *TheCU = SPMap.lookup(FnScope->getScopeNode()); // Grab the variable info that was squirreled away in the MMI side-table. collectVariableInfoFromMMITable(Processed); @@ -1237,13 +1281,19 @@ SmallVectorImpl &History = DbgValues[Var]; if (History.empty()) continue; + + DEBUG(dbgs()<< "History for " << *Var << "[ "; + for (HistoryIter HI = History.begin(); HI != History.end(); ++HI) + (*HI)->dump(); + dbgs()<< "]\n"); + const MachineInstr *MInsn = History.front(); DIVariable DV(Var); LexicalScope *Scope = NULL; if (DV.getTag() == dwarf::DW_TAG_arg_variable && DISubprogram(DV.getContext()).describes(CurFn->getFunction())) - Scope = LScopes.getCurrentFunctionScope(); + Scope = FnScope; else if (MDNode *IA = DV.getInlinedAt()) Scope = LScopes.findInlinedScope(DebugLoc::getFromDILocation(IA)); else @@ -1252,7 +1302,7 @@ if (!Scope) continue; - Processed.insert(DV); + Processed.insert(getEntireVariable(DV)); assert(MInsn->isDebugValue() && "History must begin with debug value"); DbgVariable *AbsVar = findAbstractVariable(DV, MInsn->getDebugLoc()); DbgVariable *RegVar = new DbgVariable(DV, AbsVar, this); @@ -1276,11 +1326,18 @@ LocList.Label = Asm->GetTempSymbol("debug_loc", DotDebugLocEntries.size() - 1); SmallVector &DebugLoc = LocList.List; - for (SmallVectorImpl::const_iterator - HI = History.begin(), - HE = History.end(); - HI != HE; ++HI) { + + // Build the location list for all DBG_VALUEs in the function that + // describe the same variable. + bool IsNonOverlappingPiece = false; + for (HistoryIter HI = History.begin(), HE = History.end(); HI != HE; ++HI) { const MachineInstr *Begin = *HI; + // Skip over end markers that we already processed. + if (!Begin) + continue; + + DEBUG(dbgs()<< "Range for " << *Begin->getDebugVariable() + << "beginning at " << *Begin <<"\n"); assert(Begin->isDebugValue() && "Invalid History entry"); // Check if DBG_VALUE is truncating a range. @@ -1290,14 +1347,15 @@ // Compute the range for a register location. const MCSymbol *FLabel = getLabelBeforeInsn(Begin); - const MCSymbol *SLabel = 0; - - if (HI + 1 == HE) + const MCSymbol *SLabel; + // Skip over other non-overlapping pieces to find the end of the range. + HistoryIter EndOfRange = getEndOfRange(HI, HE); + if (EndOfRange == HE) // If Begin is the last instruction in History then its value is valid // until the end of the function. SLabel = FunctionEndSym; else { - const MachineInstr *End = HI[1]; + const MachineInstr *End = *EndOfRange; DEBUG(dbgs() << "DotDebugLoc Pair:\n" << "\t" << *Begin << "\t" << *End << "\n"); if (End->isDebugValue()) @@ -1306,25 +1364,39 @@ // End is a normal instruction clobbering the range. SLabel = getLabelAfterInsn(End); assert(SLabel && "Forgot label after clobber instruction"); - ++HI; + // Mark the end as processed. + *EndOfRange = 0; } } - // The value is valid until the next DBG_VALUE or clobber. - LexicalScope *FnScope = LScopes.getCurrentFunctionScope(); - DwarfCompileUnit *TheCU = SPMap.lookup(FnScope->getScopeNode()); - DebugLocEntry Loc = getDebugLocEntry(Asm, FLabel, SLabel, Begin, TheCU); - if (DebugLoc.empty() || !DebugLoc.back().Merge(Loc)) - DebugLoc.push_back(std::move(Loc)); + if (IsNonOverlappingPiece && + FLabel == DebugLoc.back().getBeginSym() && + SLabel == DebugLoc.back().getEndSym() + ) { + // If this is a piece of the same variable with a + // non-overlapping range, add it to the previous Loc. + DEBUG(DIVariable Prev(DebugLoc.back().getValues().back().getVariable()); + DIVariable Cur(Begin->getDebugVariable()); + assert((Prev.getName() == Cur.getName()) && + "pieces do not belong to the same variable"); + ); + DebugLoc.back().addValue(getDebugLocValue(Begin)); + } else { + // The value is valid until the next DBG_VALUE or clobber. + DebugLocEntry Loc(FLabel, SLabel, getDebugLocValue(Begin), TheCU); + if (DebugLoc.empty() || !DebugLoc.back().Merge(Loc)) + DebugLoc.push_back(std::move(Loc)); + } + IsNonOverlappingPiece = EndOfRange != HI+1; } } // Collect info for variables that were optimized out. - LexicalScope *FnScope = LScopes.getCurrentFunctionScope(); DIArray Variables = DISubprogram(FnScope->getScopeNode()).getVariables(); for (unsigned i = 0, e = Variables.getNumElements(); i != e; ++i) { DIVariable DV(Variables.getElement(i)); - if (!DV || !DV.isVariable() || !Processed.insert(DV)) + if (!DV || !DV.isVariable() || + !Processed.insert(getEntireVariable(DV))) continue; if (LexicalScope *Scope = LScopes.findLexicalScope(DV.getContext())) addScopeVariable(Scope, new DbgVariable(DV, NULL, this)); @@ -1493,22 +1565,40 @@ assert(MI->getNumOperands() > 1 && "Invalid machine instruction!"); // Keep track of user variables. - const MDNode *Var = MI->getDebugVariable(); + DIVariable Var = MI->getDebugVariable(); // Variable is in a register, we need to check for clobbers. if (isDbgValueInDefinedReg(MI)) LiveUserVar[MI->getOperand(0).getReg()] = Var; - // Check the history of this variable. - SmallVectorImpl &History = DbgValues[Var]; - if (History.empty()) { - UserVariables.push_back(Var); + // Check the history of this variable. A variable may be fragmented + // into multiple pieces, but they still share one history. + DIVariable EntireVar = getEntireVariable(Var); + SmallVectorImpl &History = DbgValues[EntireVar]; + + bool AtFnBegin = false; + if (History.empty()) + AtFnBegin = true; + else { + const MachineInstr *Prev = History.back(); + if (Prev->isDebugValue() && + Var.isVariablePiece() && + LabelsBeforeInsn[Prev] == FunctionBeginSym && + !piecesOverlap(Prev->getDebugVariable(), Var)) + AtFnBegin = true; + } + + if (AtFnBegin) { // The first mention of a function argument gets the FunctionBeginSym // label, so arguments are visible when breaking at function entry. DIVariable DV(Var); if (DV.isVariable() && DV.getTag() == dwarf::DW_TAG_arg_variable && getDISubprogram(DV.getContext()).describes(MF->getFunction())) LabelsBeforeInsn[MI] = FunctionBeginSym; + } + + if (History.empty()) { + UserVariables.push_back(EntireVar); } else { // We have seen this variable before. Try to coalesce DBG_VALUEs. const MachineInstr *Prev = History.back(); @@ -1567,7 +1657,8 @@ LiveUserVar[Reg] = 0; // Was MD last defined by a DBG_VALUE referring to Reg? - DbgValueHistoryMap::iterator HistI = DbgValues.find(Var); + DIVariable EntireVar = getEntireVariable(DIVariable(Var)); + DbgValueHistoryMap::iterator HistI = DbgValues.find(EntireVar); if (HistI == DbgValues.end()) continue; SmallVectorImpl &History = HistI->second; @@ -2327,21 +2418,72 @@ Holder.emitStrings(Asm->getObjFileLowering().getDwarfStrSection()); } +/// Emits an optimal (=sorted) sequence of DW_OP_pieces. +void emitDWARFPieces(AsmPrinter *Asm, ByteStreamer &Streamer, + const DITypeIdentifierMap &Map, + ArrayRef Values) { + typedef DebugLocEntry::Value Piece; + SmallVector Pieces(Values.begin(), Values.end()); + assert(std::all_of(Pieces.begin(), Pieces.end(), [](Piece &P) { + return DIVariable(P.getVariable()).isVariablePiece(); + })); + + // Sort the pieces so they can be emitted using DW_OP_piece. + std::sort(Pieces.begin(), Pieces.end(), [](const Piece &A, const Piece &B) { + DIVariable VarA(A.getVariable()); + DIVariable VarB(B.getVariable()); + return VarA.getPieceOffset() < VarB.getPieceOffset(); + }); + + bool Consecutive = true; + unsigned Offset = 0; + for (auto Piece : Pieces) { + DIVariable Var(Piece.getVariable()); + unsigned PieceOffset = Var.getPieceOffset(); + unsigned PieceSize = Var.getPieceSize(); + if (PieceOffset != Offset) + Consecutive = false; + + Offset += PieceSize; + + // Assuming 8 bits per byte. + assert(!Var.isIndirect() && "indirect address for piece"); +#ifndef NDEBUG + unsigned VarSize = Var.getSizeInBits(Map); + assert(PieceSize+PieceOffset <= VarSize/8 + && "piece is larger than or outside of variable"); + assert(PieceSize*8 != VarSize + && "piece covers entire variable"); +#endif + if (Piece.isLocation()) + Asm->EmitDwarfRegOpPiece(Streamer, + Piece.getLoc(), + PieceSize*8, + Consecutive ? 0 : PieceOffset*8); + } +}; + void DwarfDebug::emitDebugLocEntry(ByteStreamer &Streamer, const DebugLocEntry &Entry) { - DIVariable DV(Entry.getVariable()); - if (Entry.isInt()) { + const DebugLocEntry::Value Value = Entry.getValues()[0]; + DIVariable DV(Value.getVariable()); + if (DV.isVariablePiece()) + // Emit all pieces that belong to the same variable and range. + return emitDWARFPieces(Asm, Streamer, TypeIdentifierMap, Entry.getValues()); + + // Regular entry. + if (Value.isInt()) { DIBasicType BTy(resolve(DV.getType())); if (BTy.Verify() && (BTy.getEncoding() == dwarf::DW_ATE_signed || BTy.getEncoding() == dwarf::DW_ATE_signed_char)) { Streamer.EmitInt8(dwarf::DW_OP_consts, "DW_OP_consts"); - Streamer.EmitSLEB128(Entry.getInt()); + Streamer.EmitSLEB128(Value.getInt()); } else { Streamer.EmitInt8(dwarf::DW_OP_constu, "DW_OP_constu"); - Streamer.EmitULEB128(Entry.getInt()); + Streamer.EmitULEB128(Value.getInt()); } - } else if (Entry.isLocation()) { - MachineLocation Loc = Entry.getLoc(); + } else if (Value.isLocation()) { + MachineLocation Loc = Value.getLoc(); if (!DV.hasComplexAddress()) // Regular entry. Asm->EmitDwarfRegOp(Streamer, Loc, DV.isIndirect()); Index: lib/CodeGen/AsmPrinter/DwarfUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -597,6 +597,13 @@ } else if (Element == DIBuilder::OpDeref) { if (!Location.isReg()) addUInt(Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_deref); + } else if (Element == DIBuilder::OpPiece) { + DIVariable Piece = DV.getVariable(); + assert(Piece.getPieceSize() > 0 && "empty piece"); + addUInt(Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_bit_piece); + addUInt(Loc, dwarf::DW_FORM_data1, Piece.getPieceSize()*8); + addUInt(Loc, dwarf::DW_FORM_data1, Piece.getPieceOffset()*8); + i += 2; } else llvm_unreachable("unknown DIBuilder Opcode"); } Index: lib/IR/DIBuilder.cpp =================================================================== --- lib/IR/DIBuilder.cpp +++ lib/IR/DIBuilder.cpp @@ -1050,6 +1050,22 @@ return DIVariable(MDNode::get(VMContext, Elts)); } +/// createVariablePiece - Create a descriptor to describe one part +/// of aggregate variable that is fragmented across multiple Values. +DIVariable DIBuilder::createVariablePiece(DIVariable Variable, + unsigned OffsetInBytes, + unsigned SizeInBytes) { + assert(SizeInBytes > 0); + SmallVector Elts; + for (unsigned i = 0; i < Variable->getNumOperands(); ++i) + Elts.push_back(Variable->getOperand(i)); + + Elts.push_back(ConstantInt::get(Type::getInt32Ty(VMContext), OpPiece)); + Elts.push_back(ConstantInt::get(Type::getInt32Ty(VMContext), OffsetInBytes)); + Elts.push_back(ConstantInt::get(Type::getInt32Ty(VMContext), SizeInBytes)); + return DIVariable(MDNode::get(VMContext, Elts)); +} + /// createFunction - Create a new descriptor for the specified function. /// FIXME: this is added for dragonegg. Once we update dragonegg /// to call resolve function, this will be removed. Index: lib/IR/DebugInfo.cpp =================================================================== --- lib/IR/DebugInfo.cpp +++ lib/IR/DebugInfo.cpp @@ -12,13 +12,14 @@ // //===----------------------------------------------------------------------===// -#include "llvm/IR/DebugInfo.h" #include "LLVMContextImpl.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" @@ -145,6 +146,36 @@ /// getInlinedAt - If this variable is inlined then return inline location. MDNode *DIVariable::getInlinedAt() const { return getNodeField(DbgNode, 7); } +bool DIVariable::isVariablePiece() const { + return hasComplexAddress() && getAddrElement(0) == DIBuilder::OpPiece; +} + +uint64_t DIVariable::getPieceOffset() const { + assert(isVariablePiece()); + return getAddrElement(1); +} + +uint64_t DIVariable::getPieceSize() const { + assert(isVariablePiece()); + return getAddrElement(2); +} + +/// Return the size reported by the variable's type. +unsigned DIVariable::getSizeInBits(const DITypeIdentifierMap &Map) { + DIType Ty = getType().resolve(Map); + // Follow derived types until we reach a type that + // reports back a size. + while (Ty.isDerivedType() && !Ty.isCompositeType()) { + DIDerivedType DT(&*Ty); + Ty = DT.getTypeDerivedFrom().resolve(Map); + } + assert(Ty.getSizeInBits() && "type with size 0"); + return Ty.getSizeInBits(); +} + + + + //===----------------------------------------------------------------------===// // Predicates //===----------------------------------------------------------------------===// @@ -910,6 +941,20 @@ return DIVariable(MDNode::get(VMContext, Elts)); } + +/// getEntireVariable - Remove OpPiece exprs from the variable. +DIVariable llvm::getEntireVariable(DIVariable DV) { + if (!DV.isVariablePiece()) + return DV; + + SmallVector Elts; + for (unsigned i = 0; i < 8; ++i) + Elts.push_back(DV->getOperand(i)); + + return DIVariable(MDNode::get(DV->getContext(), Elts)); +} + + /// getDISubprogram - Find subprogram that is enclosing this scope. DISubprogram llvm::getDISubprogram(const MDNode *Scope) { DIDescriptor D(Scope); @@ -1393,6 +1438,10 @@ OS << " [" << Res << ']'; OS << " [line " << getLineNumber() << ']'; + + if (isVariablePiece()) + OS << " [piece, size " << getPieceSize() + << ", offset " << getPieceOffset() << ']'; } void DIObjCProperty::printInternal(raw_ostream &OS) const { Index: test/DebugInfo/X86/pieces-1.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/pieces-1.ll @@ -0,0 +1,81 @@ +; RUN: llc %t.ll -filetype=obj -o %t.o +; RUN: llvm-dwarfdump -debug-dump=loc %t.o | FileCheck --check-prefix=CHECK-DWARF %s +; +; rdar://problem/15928306 +; +; Test that we can emit debug info for aggregate values that are split +; up across multiple registers by SROA. +; +; // Compile with -O1. +; typedef struct { long int a; int b;} S; +; +; int foo(S s) { +; return s.b; +; } +; +; +; CHECK-DWARF: .debug_loc contents: +; +; 0x0000000000000000 - 0x0000000000000008: rdi, piece 0x00000008 +; CHECK-DWARF: Beginning address offset: 0x0000000000000000 +; CHECK-DWARF: Ending address offset: [[END:.*]] +; CHECK-DWARF: Location description: 55 93 08 +; +; 0x0000000000000000 - 0x0000000000000006: rsi, bit-piece 32 64 +; CHECK-DWARF: Beginning address offset: 0x0000000000000000 +; CHECK-DWARF: Ending address offset: [[LTMP3:.*]] +; CHECK-DWARF: Location description: 54 9d 20 40 +; +; 0x0000000000000006 - 0x0000000000000008: rax, bit-piece 32 64 +; CHECK-DWARF: Beginning address offset: [[LTMP3:.*]] +; CHECK-DWARF: Ending address offset: [[END]] +; CHECK-DWARF: Location description: 50 9d 20 40 +; +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.9.0" + +; Function Attrs: nounwind ssp uwtable +define i32 @foo(i64 %s.coerce0, i32 %s.coerce1) #0 { +entry: + call void @llvm.dbg.value(metadata !{i64 %s.coerce0}, i64 0, metadata !20), !dbg !21 + call void @llvm.dbg.value(metadata !{i32 %s.coerce1}, i64 0, metadata !22), !dbg !21 + ret i32 %s.coerce1, !dbg !23 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata) #1 + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata) #1 + +attributes #0 = { nounwind ssp uwtable "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!17, !18} +!llvm.ident = !{!19} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5 ", i1 true, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""} +!1 = metadata !{metadata !"pieces.c", metadata !""} +!2 = metadata !{} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !1, metadata !5, metadata !"foo", metadata !"foo", metadata !"", i32 3, metadata !6, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 true, i32 (i64, i32)* @foo, null, null, metadata !15, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [foo] +!5 = metadata !{i32 786473, metadata !1} ; [ DW_TAG_file_type ] [/pieces.c] +!6 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !7, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!7 = metadata !{metadata !8, metadata !9} +!8 = metadata !{i32 786468, null, null, metadata !"int", i32 0, i64 32, i64 32, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] [int] [line 0, size 32, align 32, offset 0, enc DW_ATE_signed] +!9 = metadata !{i32 786454, metadata !1, null, metadata !"S", i32 1, i64 0, i64 0, i64 0, i32 0, metadata !10} ; [ DW_TAG_typedef ] [S] [line 1, size 0, align 0, offset 0] [from ] +!10 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 1, i64 128, i64 64, i32 0, i32 0, null, metadata !11, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 1, size 128, align 64, offset 0] [def] [from ] +!11 = metadata !{metadata !12, metadata !14} +!12 = metadata !{i32 786445, metadata !1, metadata !10, metadata !"a", i32 1, i64 64, i64 64, i64 0, i32 0, metadata !13} ; [ DW_TAG_member ] [a] [line 1, size 64, align 64, offset 0] [from long int] +!13 = metadata !{i32 786468, null, null, metadata !"long int", i32 0, i64 64, i64 64, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] [long int] [line 0, size 64, align 64, offset 0, enc DW_ATE_signed] +!14 = metadata !{i32 786445, metadata !1, metadata !10, metadata !"b", i32 1, i64 32, i64 32, i64 64, i32 0, metadata !8} ; [ DW_TAG_member ] [b] [line 1, size 32, align 32, offset 64] [from int] +!15 = metadata !{metadata !16} +!16 = metadata !{i32 786689, metadata !4, metadata !"s", metadata !5, i32 16777219, metadata !9, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [s] [line 3] +!17 = metadata !{i32 2, metadata !"Dwarf Version", i32 4} +!18 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!19 = metadata !{metadata !"clang version 3.5 "} +!20 = metadata !{i32 786689, metadata !4, metadata !"s", metadata !5, i32 16777219, metadata !9, i32 0, i32 0, i32 3, i32 0, i32 8} ; [ DW_TAG_arg_variable ] [s] [line 3] [piece, size 8, offset 0] +!21 = metadata !{i32 3, i32 0, metadata !4, null} +!22 = metadata !{i32 786689, metadata !4, metadata !"s", metadata !5, i32 16777219, metadata !9, i32 0, i32 0, i32 3, i32 8, i32 4} ; [ DW_TAG_arg_variable ] [s] [line 3] [piece, size 4, offset 8] +!23 = metadata !{i32 4, i32 0, metadata !4, null}