Index: docs/SourceLevelDebugging.rst =================================================================== --- docs/SourceLevelDebugging.rst +++ docs/SourceLevelDebugging.rst @@ -570,6 +570,7 @@ metadata, ;; 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 @@ -428,7 +428,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(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(const MachineLocation &MLoc, bool Indirect) const; Index: include/llvm/CodeGen/MachineInstr.h =================================================================== --- include/llvm/CodeGen/MachineInstr.h +++ include/llvm/CodeGen/MachineInstr.h @@ -23,6 +23,7 @@ #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" #include "llvm/CodeGen/MachineOperand.h" +#include "llvm/DebugInfo.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/InlineAsm.h" #include "llvm/MC/MCInstrDesc.h" @@ -242,6 +243,14 @@ /// DebugLoc getDebugLoc() const { return debugLoc; } + /// getDebugVariable() - Retrun the debug variable referenced by + /// this DBG_VALUE instruction. + DIVariable getDebugVariable() const { + assert( isDebugValue() ); + const MDNode *Var = getOperand(getNumOperands() - 1).getMetadata(); + return DIVariable(Var); + } + /// emitError - Emit an error referring to the source location of this /// instruction. This should only be used for inline assembly that is somehow /// impossible to compile. Other errors should have been handled much Index: include/llvm/CodeGen/SelectionDAG.h =================================================================== --- include/llvm/CodeGen/SelectionDAG.h +++ include/llvm/CodeGen/SelectionDAG.h @@ -20,6 +20,7 @@ #include "llvm/ADT/ilist.h" #include "llvm/CodeGen/DAGCombine.h" #include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/DebugInfo.h" #include "llvm/Support/RecyclingAllocator.h" #include "llvm/Target/TargetMachine.h" #include @@ -205,6 +206,9 @@ /// DbgInfo - Tracks dbg_value information through SDISel. SDDbgInfo *DbgInfo; + /// Holds the map for resolving DITypes in the current module. + DITypeIdentifierMapCache TypeIdentifierMapCache; + public: /// DAGUpdateListener - Clients of various APIs that cause global effects on /// the DAG can optionally implement this interface. This allows the clients @@ -280,6 +284,9 @@ const TargetSelectionDAGInfo &getSelectionDAGInfo() const { return TSI; } LLVMContext *getContext() const {return Context; } + /// Return the DITypeIdentifierMap for the current module. + const DITypeIdentifierMap& getTypeIdentifierMap(); + /// viewGraph - Pop up a GraphViz/gv window with the DAG rendered using 'dot'. /// void viewGraph(const std::string &Title); Index: include/llvm/DIBuilder.h =================================================================== --- include/llvm/DIBuilder.h +++ include/llvm/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. @@ -540,6 +540,16 @@ DIType 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/DebugInfo.h =================================================================== --- include/llvm/DebugInfo.h +++ include/llvm/DebugInfo.h @@ -685,6 +685,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; }; @@ -800,9 +811,23 @@ /// cleanseInlinedVariable - Remove inlined scope from the variable. DIVariable cleanseInlinedVariable(MDNode *DV, LLVMContext &VMContext); +/// getEntireVariable - Remove OpPiece exprs from the variable. +DIVariable getEntireVariable(const MDNode *DV, LLVMContext &VMContext); + /// Construct DITypeIdentifierMap by going through retained types of each CU. DITypeIdentifierMap generateDITypeIdentifierMap(const NamedMDNode *CU_Nodes); +/// \brief Caches the last Module for which a DITypeIdentifierMap was +/// constructed. +class DITypeIdentifierMapCache { + DITypeIdentifierMap Map; + const Module *LastModule; +public: + DITypeIdentifierMapCache() : LastModule(0) {} + /// \brief Return the DITypeIdentifierMap for the module M. + const DITypeIdentifierMap& get(const Module *M); +}; + /// Strip debug info in the module if it exists. /// To do this, we remove all calls to the debugger intrinsics and any named /// metadata for debugging. We also remove debug locations for instructions. Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -910,34 +910,53 @@ /// - a small value occupying only part of a register or /// - a small register representing only part of a value. static void emitDwarfOpPiece(const AsmPrinter &AP, - unsigned Size, unsigned Offset) { - assert(Size > 0); - if (Offset > 0) { + unsigned SizeInBits, + unsigned OffsetInBits) { + assert(SizeInBits > 8); + if (OffsetInBits > 0) { AP.OutStreamer.AddComment("DW_OP_bit_piece"); AP.EmitInt8(dwarf::DW_OP_bit_piece); - AP.OutStreamer.AddComment(Twine(Size)); - AP.EmitULEB128(Size); - AP.OutStreamer.AddComment(Twine(Offset)); - AP.EmitULEB128(Offset); + AP.OutStreamer.AddComment(Twine(SizeInBits)); + AP.EmitULEB128(SizeInBits); + AP.OutStreamer.AddComment(Twine(OffsetInBits)); + AP.EmitULEB128(OffsetInBits); } else { AP.OutStreamer.AddComment("DW_OP_piece"); AP.EmitInt8(dwarf::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. AP.OutStreamer.AddComment(Twine(ByteSize)); AP.EmitULEB128(ByteSize); } } +/// Emit a shift-right dwarf expression. +static void emitDwarfOpShr(const AsmPrinter &AP, + unsigned ShiftBy) { + AP.OutStreamer.AddComment("DW_OP_constu"); + AP.EmitInt8(dwarf::DW_OP_constu); + AP.EmitULEB128(ShiftBy); + AP.OutStreamer.AddComment("DW_OP_shr"); + AP.EmitInt8(dwarf::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(const AsmPrinter &AP, - const MachineLocation &MLoc) { +void AsmPrinter::EmitDwarfRegOpPiece(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(*this, Reg); + emitDwarfOpPiece(*this, 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) { @@ -946,9 +965,19 @@ unsigned Idx = TRI->getSubRegIndex(*SR, MLoc.getReg()); unsigned Size = TRI->getSubRegIdxSize(Idx); unsigned Offset = TRI->getSubRegIdxOffset(Idx); - AP.OutStreamer.AddComment("super-register"); - emitDwarfRegOp(AP, Reg); - emitDwarfOpPiece(AP, Size, Offset); + OutStreamer.AddComment("super-register"); + emitDwarfRegOp(*this, Reg); + if (PieceOffsetInBits == Offset) { + emitDwarfOpPiece(*this, 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(*this, Size, PieceOffsetInBits); + if (Offset) + emitDwarfOpShr(*this, Offset); + } return; } } @@ -958,7 +987,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 @@ -979,9 +1008,9 @@ // 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"); - emitDwarfRegOp(AP, Reg); - emitDwarfOpPiece(AP, Size, Offset == CurPos ? 0 : Offset); + OutStreamer.AddComment("sub-register"); + emitDwarfRegOp(*this, Reg); + emitDwarfOpPiece(*this, Size, Offset == CurPos ? 0 : Offset); CurPos = Offset+Size; // Mark it as emitted. @@ -989,10 +1018,10 @@ } } - if (CurPos == 0) { + if (CurPos == PieceOffsetInBits) { // FIXME: We have no reasonable way of handling errors in here. - AP.OutStreamer.AddComment("nop (could not find a dwarf register number)"); - AP.EmitInt8(dwarf::DW_OP_nop); + OutStreamer.AddComment("nop (could not find a dwarf register number)"); + EmitInt8(dwarf::DW_OP_nop); } } @@ -1013,8 +1042,7 @@ } // Attempt to find a valid super- or sub-register. - if (!Indirect && !MLoc.isIndirect()) - return EmitDwarfRegOpPiece(*this, MLoc); + return EmitDwarfRegOpPiece(MLoc, 0, 0); } if (MLoc.isIndirect()) Index: lib/CodeGen/AsmPrinter/DwarfDebug.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.h +++ lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -171,7 +171,8 @@ StringRef getName() const { return Var.getName(); } DbgVariable *getAbstractVariable() const { return AbsVar; } const MachineInstr *getMInsn() const { return MInsn; } - void setMInsn(const MachineInstr *M) { MInsn = M; } + /// Set the machine instruction and update the Var accordingly. + void setMInsn(const MachineInstr *M); int getFrameIndex() const { return FrameIndex; } void setFrameIndex(int FI) { FrameIndex = FI; } // Translate tag to proper Dwarf tag. @@ -587,6 +588,9 @@ /// \brief Emit visible names into a debug str section. void emitDebugStr(); + /// \brief Return the size of the variable's type. + unsigned getVariableSizeInBits(DIVariable DV); + /// \brief Emit visible names into a debug loc section. void emitDebugLoc(); Index: lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -117,6 +117,12 @@ namespace llvm { +void DbgVariable::setMInsn(const MachineInstr *M) { + MInsn = M; + // The DBG_VALUE may have a more specific complex expression. + Var = MInsn->getDebugVariable(); +} + /// resolve - Look in the DwarfDebug map for the MDNode that /// corresponds to the reference. template T DbgVariable::resolve(DIRef Ref) const { @@ -1253,7 +1259,7 @@ const MCSymbol *FLabel, const MCSymbol *SLabel, const MachineInstr *MI) { - const MDNode *Var = MI->getOperand(MI->getNumOperands() - 1).getMetadata(); + const MDNode *Var = MI->getDebugVariable(); assert(MI->getNumOperands() == 3); if (MI->getOperand(0).isReg()) { @@ -1276,6 +1282,35 @@ 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. +static HistoryIter +nextOverlappingHistoryEntry(HistoryIter DbgVal, HistoryIter End) { + DIVariable DV0 = (*DbgVal)->getDebugVariable(); + if (!DV0.isVariablePiece()) + return DbgVal+1; + + for (HistoryIter I=DbgVal+1; I != End; ++I) { + if (!*I) continue; + if (!(*I)->isDebugValue()) return I; + + // Clobber or overlap? + 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) { @@ -1296,6 +1331,12 @@ 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); @@ -1311,7 +1352,7 @@ if (!Scope) continue; - Processed.insert(DV); + Processed.insert(getEntireVariable(DV, DV->getContext())); assert(MInsn->isDebugValue() && "History must begin with debug value"); DbgVariable *AbsVar = findAbstractVariable(DV, MInsn->getDebugLoc()); DbgVariable *RegVar = new DbgVariable(DV, AbsVar, this); @@ -1330,11 +1371,15 @@ // Handle multiple DBG_VALUE instructions describing one variable. RegVar->setDotDebugLocOffset(DotDebugLocEntries.size()); - for (SmallVectorImpl::const_iterator - HI = History.begin(), - HE = History.end(); - HI != HE; ++HI) { + // Build the location list. + bool ShareFLabel = false; + const MCSymbol *SLabel, *FLabel = 0; + 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. @@ -1343,15 +1388,17 @@ continue; // Compute the range for a register location. - const MCSymbol *FLabel = getLabelBeforeInsn(Begin); - const MCSymbol *SLabel = 0; + if (!ShareFLabel) + FLabel = getLabelBeforeInsn(Begin); + SLabel = 0; - if (HI + 1 == HE) + HistoryIter EndOfRange = nextOverlappingHistoryEntry(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()) @@ -1360,13 +1407,15 @@ // 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. DotDebugLocEntries.push_back( getDebugLocEntry(Asm, FLabel, SLabel, Begin)); + ShareFLabel = EndOfRange != HI+1; } DotDebugLocEntries.push_back(DotDebugLocEntry()); } @@ -1376,7 +1425,8 @@ 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, DV->getContext()))) continue; if (LexicalScope *Scope = LScopes.findLexicalScope(DV.getContext())) addScopeVariable(Scope, new DbgVariable(DV, NULL, this)); @@ -1560,17 +1610,19 @@ assert(MI->getNumOperands() > 1 && "Invalid machine instruction!"); // Keep track of user variables. - const MDNode *Var = - MI->getOperand(MI->getNumOperands() - 1).getMetadata(); + 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]; + // Check the history of this variable. A variable may be fragmented + // into multiple pieces, but they still share one history. + DIVariable EntireVar = getEntireVariable(Var, Var->getContext()); + SmallVectorImpl &History = DbgValues[EntireVar]; + if (History.empty()) { - UserVariables.push_back(Var); + UserVariables.push_back(EntireVar); // The first mention of a function argument gets the FunctionBeginSym // label, so arguments are visible when breaking at function entry. DIVariable DV(Var); @@ -1590,6 +1642,12 @@ History.pop_back(); } + if (EntireVar.isVariablePiece()) { + DEBUG(dbgs() << "Adding piece " << *Var << " at " << *MI <<"\n"); + History.push_back(MI); + continue; + } + // Terminate old register assignments that don't reach MI; MachineFunction::const_iterator PrevMBB = Prev->getParent(); if (PrevMBB != I && (!AtBlockEntry || std::next(PrevMBB) != I) && @@ -1637,7 +1695,8 @@ LiveUserVar[Reg] = 0; // Was MD last defined by a DBG_VALUE referring to Reg? - DbgValueHistoryMap::iterator HistI = DbgValues.find(Var); + DIVariable EntireVar = getEntireVariable(Var, Var->getContext()); + DbgValueHistoryMap::iterator HistI = DbgValues.find(EntireVar); if (HistI == DbgValues.end()) continue; SmallVectorImpl &History = HistI->second; @@ -2563,6 +2622,7 @@ Holder.emitStrings(Asm->getObjFileLowering().getDwarfStrSection()); } + // Emit locations into the debug loc section. void DwarfDebug::emitDebugLoc() { if (DotDebugLocEntries.empty()) @@ -2622,8 +2682,44 @@ Asm->EmitDwarfRegOp(Loc, DV.isIndirect()); else { // Complex address entry. + if (DV.isVariablePiece()) { + bool Consecutive = true; + unsigned Offset = 0; + // Collect all the pieces belonging to this variable. + for (; I!=E; ++I) { + DIVariable Var(I->getVariable()); + if (Offset > 0 && + !(Var.isVariable() && + Var.isVariablePiece() && + Var.getName() == DV.getName() && + I->getBeginSym() == Entry.getBeginSym() && + I->getEndSym() == Entry.getEndSym())) { + --I; + break; + } + + if (Var.getPieceOffset() != Offset) + Consecutive = false; + + Offset += Var.getPieceSize(); + + assert(!Var.isIndirect() && "indirect address for piece"); +#ifndef NDEBUG + unsigned VarSize = DV.getSizeInBits(TypeIdentifierMap); + assert(Var.getPieceSize()+Var.getPieceOffset() <= VarSize/8 + && "piece is larger than or outside of variable"); + assert(Var.getPieceSize()*8 != VarSize + && "piece covers entire variable"); +#endif + // Assuming 8 bits per byte. + Asm->EmitDwarfRegOpPiece(I->getLoc(), + Var.getPieceSize()*8, + Var.getPieceOffset()*8); + } + } else { unsigned N = DV.getNumAddrElements(); unsigned i = 0; + if (N >= 2 && DV.getAddrElement(0) == DIBuilder::OpPlus) { if (Loc.getOffset()) { i = 2; @@ -2656,6 +2752,7 @@ } else llvm_unreachable("unknown Opcode found in complex address"); } + } // FIXME: weird indentation here to make the diff more readable. } } // else ... ignore constant fp. There is not any good way to Index: lib/CodeGen/AsmPrinter/DwarfUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -550,6 +550,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/CodeGen/SelectionDAG/InstrEmitter.cpp =================================================================== --- lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -671,6 +671,8 @@ DenseMap::iterator I = VRBaseMap.find(Op); if (I==VRBaseMap.end()) MIB.addReg(0U); // undef + else if (TargetRegisterInfo::isPhysicalRegister(I->second)) + MIB.addReg(I->second, RegState::Debug); else AddOperand(MIB, Op, (*MIB).getNumOperands(), &II, VRBaseMap, /*IsDebug=*/true, /*IsClone=*/false, /*IsCloned=*/false); Index: lib/CodeGen/SelectionDAG/LegalizeTypes.cpp =================================================================== --- lib/CodeGen/SelectionDAG/LegalizeTypes.cpp +++ lib/CodeGen/SelectionDAG/LegalizeTypes.cpp @@ -14,7 +14,11 @@ //===----------------------------------------------------------------------===// #include "LegalizeTypes.h" +#include "SDNodeDbgValue.h" #include "llvm/ADT/SetVector.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/DIBuilder.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/DataLayout.h" #include "llvm/Support/CommandLine.h" @@ -775,6 +779,41 @@ Hi = Entry.second; } +/// TransferDbgValues - Transfer SDDbgValues. +static void TransferDbgValues(SelectionDAG &DAG, DIBuilder &DIB, + const DITypeIdentifierMap &TypeIdentifierMap, + SDValue From, SDValue To, + unsigned OffsetInBits) { + SDNode *FromNode = From.getNode(); + SDNode *ToNode = To.getNode(); + assert(FromNode != ToNode); + ArrayRef DVs = DAG.GetDbgValues(FromNode); + for (ArrayRef::iterator I = DVs.begin(), E = DVs.end(); + I != E; ++I) { + SDDbgValue *Dbg = *I; + + if (Dbg->getKind() == SDDbgValue::SDNODE) { + // Assuming 8 bits / byte. + DIVariable Var(Dbg->getMDPtr()); + + unsigned VarSize = Var.getSizeInBits(TypeIdentifierMap); + unsigned PieceSizeInBits = To.getValueSizeInBits(); + assert(OffsetInBits % 8 == 0); + assert(PieceSizeInBits % 8 == 0); + assert(PieceSizeInBits+OffsetInBits <= VarSize + && "Piece is larger than or outside of variable"); + DIVariable Piece = (PieceSizeInBits == VarSize) + ? Var + : DIB.createVariablePiece(Var, OffsetInBits/8, PieceSizeInBits/8); + SDDbgValue *Clone = DAG.getDbgValue(&*Piece, ToNode, To.getResNo(), + Dbg->getOffset(), Dbg->getDebugLoc(), + Dbg->getOrder()); + Dbg->setIsInvalidated(); + DAG.AddDbgValue(Clone, ToNode, false); + } + } +} + void DAGTypeLegalizer::SetExpandedInteger(SDValue Op, SDValue Lo, SDValue Hi) { assert(Lo.getValueType() == @@ -785,6 +824,13 @@ AnalyzeNewValue(Lo); AnalyzeNewValue(Hi); + // Transfer debug values. + const Module *M = DAG.getMachineFunction().getMMI().getModule(); + const DITypeIdentifierMap &Map = DAG.getTypeIdentifierMap(); + DIBuilder DIB(*const_cast(M)); + TransferDbgValues(DAG, DIB, Map, Op, Lo, 0); + TransferDbgValues(DAG, DIB, Map, Op, Hi, Lo.getValueSizeInBits()); + // Remember that this is the result of the node. std::pair &Entry = ExpandedIntegers[Op]; assert(Entry.first.getNode() == 0 && "Node already expanded"); Index: lib/CodeGen/SelectionDAG/SelectionDAG.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -541,6 +541,11 @@ // SelectionDAG Class //===----------------------------------------------------------------------===// +// Return the DITypeIdentifierMap for the current module. +const DITypeIdentifierMap& SelectionDAG::getTypeIdentifierMap() { + return TypeIdentifierMapCache.get(MF->getMMI().getModule()); +} + /// doNotCSE - Return true if CSE should not be performed for this node. static bool doNotCSE(SDNode *N) { if (N->getValueType(0) == MVT::Glue) @@ -6050,7 +6055,7 @@ I != E; ++I) { SDDbgValue *Dbg = *I; if (Dbg->getKind() == SDDbgValue::SDNODE) { - SDDbgValue *Clone = getDbgValue(Dbg->getMDPtr(), ToNode, To.getResNo(), + SDDbgValue *Clone = getDbgValue(Dbg->getMDPtr(), ToNode, Dbg->getResNo(), Dbg->getOffset(), Dbg->getDebugLoc(), Dbg->getOrder()); ClonedDVs.push_back(Clone); Index: lib/IR/DIBuilder.cpp =================================================================== --- lib/IR/DIBuilder.cpp +++ lib/IR/DIBuilder.cpp @@ -1044,6 +1044,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 @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/Analysis/ValueTracking.h" +#include "llvm/DIBuilder.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instructions.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(); + // 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 //===----------------------------------------------------------------------===// @@ -906,6 +937,21 @@ return DIVariable(MDNode::get(VMContext, Elts)); } + +/// getEntireVariable - Remove OpPiece exprs from the variable. +DIVariable llvm::getEntireVariable(const MDNode *MD, LLVMContext &VMContext) { + DIVariable DV(MD); + if (!DV.isVariablePiece()) + return DV; + + SmallVector Elts; + for (unsigned i = 0; i < 8; ++i) + Elts.push_back(DV->getOperand(i)); + + return DIVariable(MDNode::get(VMContext, Elts)); +} + + /// getDISubprogram - Find subprogram that is enclosing this scope. DISubprogram llvm::getDISubprogram(const MDNode *Scope) { DIDescriptor D(Scope); @@ -964,6 +1010,18 @@ return Map; } +/// \brief Return the DITypeIdentifierMap for the module M. +const DITypeIdentifierMap& DITypeIdentifierMapCache::get(const Module *M) { + // Update the type identifier map. + if (M != LastModule) { + if (NamedMDNode *CU_Nodes = M->getNamedMetadata("llvm.dbg.cu")) + Map = generateDITypeIdentifierMap(CU_Nodes); + LastModule = M; + } + return Map; +} + + //===----------------------------------------------------------------------===// // DebugInfoFinder implementations. //===----------------------------------------------------------------------===// @@ -1401,6 +1459,10 @@ OS << " [" << Res << ']'; OS << " [line " << getLineNumber() << ']'; + + if (isVariablePiece()) + OS << " [piece, size " << getPieceSize() + << ", offset " << getPieceOffset() << ']'; } void DIObjCProperty::printInternal(raw_ostream &OS) const { Index: lib/Transforms/Scalar/SROA.cpp =================================================================== --- lib/Transforms/Scalar/SROA.cpp +++ lib/Transforms/Scalar/SROA.cpp @@ -25,6 +25,7 @@ #define DEBUG_TYPE "sroa" #include "llvm/Transforms/Scalar.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" @@ -969,6 +970,14 @@ /// currently in the promotable queue. SetVector > SpeculatableSelects; + /// Debug intrinsics do not show up like regular uses in the + /// IR. This side-table holds the missing use edges. + DenseMap DbgDeclares; + + /// Holds a map from DITypeRef to the actual MDNode. + DITypeIdentifierMapCache TypeIdentifierMapCache; + Optional TypeIdentifierMap; + public: SROA(bool RequiresDomTree = true) : FunctionPass(ID), RequiresDomTree(RequiresDomTree), @@ -2035,6 +2044,9 @@ // Utility IR builder, whose name prefix is setup for each visited use, and // the insertion point is set to point to the user. IRBuilderTy IRB; + DenseMap& DbgDeclares; + const DITypeIdentifierMap& TypeIdentifierMap; + DIBuilder DIB; public: AllocaSliceRewriter(const DataLayout &DL, AllocaSlices &S, SROA &Pass, @@ -2043,7 +2055,9 @@ uint64_t NewAllocaEndOffset, bool IsVectorPromotable, bool IsIntegerPromotable, SmallPtrSetImpl &PHIUsers, - SmallPtrSetImpl &SelectUsers) + SmallPtrSetImpl &SelectUsers, + DenseMap& DbgDeclares, + const DITypeIdentifierMap& TypeIdentifierMap) : DL(DL), S(S), Pass(Pass), OldAI(OldAI), NewAI(NewAI), NewAllocaBeginOffset(NewAllocaBeginOffset), NewAllocaEndOffset(NewAllocaEndOffset), @@ -2058,7 +2072,10 @@ : 0), BeginOffset(), EndOffset(), IsSplittable(), IsSplit(), OldUse(), OldPtr(), PHIUsers(PHIUsers), SelectUsers(SelectUsers), - IRB(NewAI.getContext(), ConstantFolder()) { + IRB(NewAI.getContext(), ConstantFolder()), + DbgDeclares(DbgDeclares), + TypeIdentifierMap(TypeIdentifierMap), + DIB(*NewAI.getParent()->getParent()->getParent()) { if (VecTy) { assert((DL.getTypeSizeInBits(ElementTy) % 8) == 0 && "Only multiple-of-8 sized vector elements are viable"); @@ -2092,6 +2109,27 @@ IRB.SetCurrentDebugLocation(OldUserI->getDebugLoc()); IRB.SetNamePrefix(Twine(NewAI.getName()) + "." + Twine(BeginOffset) + "."); + if (&NewAI != &OldAI) { + // Migrate debug information to the new alloca. + if (DbgDeclareInst *DbgDecl = DbgDeclares.lookup(&OldAI)) { + DIVariable Var(DbgDecl->getVariable()); + // If the new slice is smaller than the entire variable (this + // may happen even if this slice is not a split), create + // a variable piece to describe the alloca. This works fine + // even if Var already was a piece. + unsigned VarSize = Var.getSizeInBits(TypeIdentifierMap) / 8; + assert(BeginOffset+SliceSize <= VarSize && + "slice is larger than or outside of variable"); + DIVariable Piece = (SliceSize == VarSize) + ? Var + : DIB.createVariablePiece(Var, BeginOffset, SliceSize); + Instruction *NewDDI = DIB.insertDeclare(&NewAI, Piece, OldPtr); + NewDDI->setDebugLoc(DbgDecl->getDebugLoc()); + DbgDeclares.insert(std::make_pair(&NewAI,cast(NewDDI))); + Pass.DeadInsts.insert(DbgDecl); + } + } + CanSROA &= visit(cast(OldUse->getUser())); if (VecTy || IntTy) assert(CanSROA); @@ -3180,7 +3218,8 @@ AllocaSliceRewriter Rewriter(*DL, S, *this, AI, *NewAI, BeginOffset, EndOffset, IsVectorPromotable, - IsIntegerPromotable, PHIUsers, SelectUsers); + IsIntegerPromotable, PHIUsers, SelectUsers, + DbgDeclares, *TypeIdentifierMap); bool Promotable = true; for (ArrayRef::const_iterator SUI = SplitUses.begin(), SUE = SplitUses.end(); @@ -3616,6 +3655,10 @@ DEBUG(dbgs() << " Skipping SROA -- no target data!\n"); return false; } + + // Update the type identifier map. + TypeIdentifierMap = TypeIdentifierMapCache.get(F.getParent()); + DL = &DLP->getDataLayout(); DominatorTreeWrapperPass *DTWP = getAnalysisIfAvailable(); @@ -3623,9 +3666,12 @@ BasicBlock &EntryBB = F.getEntryBlock(); for (BasicBlock::iterator I = EntryBB.begin(), E = std::prev(EntryBB.end()); - I != E; ++I) + I != E; ++I) { if (AllocaInst *AI = dyn_cast(I)) Worklist.insert(AI); + else if (DbgDeclareInst *DDI = dyn_cast(I)) + DbgDeclares.insert(std::make_pair(DDI->getAddress(), DDI)); + } bool Changed = false; // A set of deleted alloca instruction pointers which should be removed from Index: test/DebugInfo/X86/dbg-large-unsigned-const.ll =================================================================== --- test/DebugInfo/X86/dbg-large-unsigned-const.ll +++ test/DebugInfo/X86/dbg-large-unsigned-const.ll @@ -1,5 +1,9 @@ -; RUN: llc -filetype=obj %s -o /dev/null -; Hanle large unsigned constant values. +; Handle large unsigned constant values. +; RUN: llc -filetype=obj %s -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck %s +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_const_value [DW_FORM_sdata] (9223372036854775807) +; target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128-n8:16:32" target triple = "i386-apple-macosx10.7.0" Index: test/DebugInfo/X86/pieces-1.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/pieces-1.ll @@ -0,0 +1,90 @@ +; RUN: opt -sroa -verify -S -asm-verbose -o %t.ll %s +; RUN: cat %t.ll | FileCheck --check-prefix=CHECK-OPT %s +; 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; +; } +; +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.9.0" + +%struct.S = type { i64, i32 } + +; Function Attrs: nounwind readnone ssp uwtable +define i32 @foo(i64 %s.coerce0, i32 %s.coerce1) #0 { +entry: + %s = alloca %struct.S, align 8 + %0 = bitcast %struct.S* %s to { i64, i32 }* + %1 = getelementptr { i64, i32 }* %0, i32 0, i32 0 + store i64 %s.coerce0, i64* %1 + %2 = getelementptr { i64, i32 }* %0, i32 0, i32 1 + store i32 %s.coerce1, i32* %2 + call void @llvm.dbg.declare(metadata !{%struct.S* %s}, metadata !16), !dbg !23 + %b = getelementptr inbounds %struct.S* %s, i32 0, i32 1, !dbg !23 + %3 = load i32* %b, align 4, !dbg !21, !tbaa !24 + ret i32 %3, !dbg !24 +} + +; CHECK-OPT: define i32 @foo(i64 %s.coerce0, i32 %s.coerce1) #0 { +; CHECK-OPT: call void @llvm.dbg.value(metadata !{i64 %s.coerce0}, i64 0, metadata ![[PIECE1:.*]]) +; CHECK-OPT: call void @llvm.dbg.value(metadata !{i32 %s.coerce1}, i64 0, metadata ![[PIECE2:.*]]) +; CHECK-OPT: ret i32 %s.coerce1 +; Make sure the dbg.declare for the old alloca is gone. +; CHECK-OPT-NONE: undef +; CHECK-OPT: ![[PIECE1]] = {{.*}} i32 3, i32 0, i32 8} ; [ DW_TAG_arg_variable ] [s] [line 3] +; CHECK-OPT: ![[PIECE2]] = {{.*}} i32 3, i32 8, i32 4} ; [ DW_TAG_arg_variable ] [s] [line 3] +; CHECK-OPT-NONE: undef + +; rdi, piece 0x00000008 +; CHECK-DWARF: Location description: 55 93 08 +; rsi, bit-piece 32 64 +; CHECK-DWARF: Location description: 54 9d 20 40 + +; 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 !""} ; [ DW_TAG_compile_unit ] [pieces.c] [DW_LANG_C99] +!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] +!21 = 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] +!22 = metadata !{%struct.S* undef} +!23 = metadata !{i32 3, i32 0, metadata !4, null} +!24 = metadata !{i32 4, i32 0, metadata !4, null} Index: test/DebugInfo/X86/sdagsplit-1.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/sdagsplit-1.ll @@ -0,0 +1,70 @@ +; RUN: llc %s -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 large values that are split +; up across multiple registers by the SelectionDAG type legalizer. +; +; // Compile with -O1 -m32. +; long long foo (long long a, long long b, long long c) +; { +; long long res = c+1; +; if ( a == b ) +; return res; +; return 0; +; } +target datalayout = "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128" +target triple = "i386-apple-macosx10.9.0" + +; eax, piece 0x00000004 +; CHECK-DWARF: Location description: 50 93 04 +; edx, bit-piece 32 32 +; CHECK-DWARF: Location description: 52 9d 20 20 + + +; Function Attrs: nounwind readnone ssp +define i64 @foo(i64 %a, i64 %b, i64 %c) #0 { +entry: + tail call void @llvm.dbg.value(metadata !{i64 %a}, i64 0, metadata !10), !dbg !17 + tail call void @llvm.dbg.value(metadata !{i64 %b}, i64 0, metadata !11), !dbg !17 + tail call void @llvm.dbg.value(metadata !{i64 %c}, i64 0, metadata !12), !dbg !17 + tail call void @llvm.dbg.value(metadata !{i64 %add}, i64 0, metadata !13), !dbg !18 + %cmp = icmp eq i64 %a, %b, !dbg !19 + %add = add nsw i64 %c, 1, !dbg !18 + %retval.0 = select i1 %cmp, i64 %add, i64 0, !dbg !19 + ret i64 %retval.0, !dbg !21 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata) #1 + +attributes #0 = { nounwind readnone ssp } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!14, !15} +!llvm.ident = !{!16} + +!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 !""} ; [ DW_TAG_compile_unit ] [sdagsplit-1.c] [DW_LANG_C99] +!1 = metadata !{metadata !"sdagsplit-1.c", metadata !""} +!2 = metadata !{} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !1, metadata !5, metadata !"foo", metadata !"foo", metadata !"", i32 149, metadata !6, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 true, i64 (i64, i64, i64)* @foo, null, null, metadata !9, i32 150} ; [ DW_TAG_subprogram ] [line 149] [def] [scope 150] [foo] +!5 = metadata !{i32 786473, metadata !1} ; [ DW_TAG_file_type ] [sdagsplit-1.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 !8, metadata !8, metadata !8} +!8 = metadata !{i32 786468, null, null, metadata !"long long int", i32 0, i64 64, i64 32, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] [long long int] [line 0, size 64, align 32, offset 0, enc DW_ATE_signed] +!9 = metadata !{metadata !10, metadata !11, metadata !12, metadata !13} +!10 = metadata !{i32 786689, metadata !4, metadata !"a", metadata !5, i32 16777365, metadata !8, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [a] [line 149] +!11 = metadata !{i32 786689, metadata !4, metadata !"b", metadata !5, i32 33554581, metadata !8, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [b] [line 149] +!12 = metadata !{i32 786689, metadata !4, metadata !"c", metadata !5, i32 50331797, metadata !8, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [c] [line 149] +!13 = metadata !{i32 786688, metadata !4, metadata !"res", metadata !5, i32 151, metadata !8, i32 0, i32 0} ; [ DW_TAG_auto_variable ] [res] [line 151] +!14 = metadata !{i32 2, metadata !"Dwarf Version", i32 2} +!15 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!16 = metadata !{metadata !"clang version 3.5 "} +!17 = metadata !{i32 149, i32 0, metadata !4, null} +!18 = metadata !{i32 151, i32 0, metadata !4, null} +!19 = metadata !{i32 152, i32 0, metadata !20, null} +!20 = metadata !{i32 786443, metadata !1, metadata !4, i32 152, i32 0, i32 0} ; [ DW_TAG_lexical_block ] [sdagsplit-1.c] +!21 = metadata !{i32 155, i32 0, metadata !4, null} Index: test/DebugInfo/X86/sdagsplit-2.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/sdagsplit-2.ll @@ -0,0 +1,74 @@ +; RUN: llc %s -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck --check-prefix=CHECK-DWARF %s +; +; rdar://problem/15928306 +; +; Test that we can emit debug info for large values that are split +; up across multiple registers by the SelectionDAG type legalizer. +; +; // Compile with -O1 -m32. +; long long foo (long long a, long long b, long long c) +; { +; long long res = c+1; +; if ( a == b ) +; return res; +; return res+b; +; } +target datalayout = "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128" +target triple = "i386-apple-macosx10.9.0" + +; CHECK-DWARF: "res" +; ecx, bit-piece 32 32 +; CHECK-DWARF: 51 9d 20 20 + + +; ModuleID = '/Volumes/Data/radar/15928306/iains.c' +target datalayout = "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128" +target triple = "i386-apple-macosx10.9.0" + +; Function Attrs: nounwind readnone ssp +define i64 @foo(i64 %a, i64 %b, i64 %c) #0 { +entry: + tail call void @llvm.dbg.value(metadata !{i64 %a}, i64 0, metadata !10), !dbg !17 + tail call void @llvm.dbg.value(metadata !{i64 %b}, i64 0, metadata !11), !dbg !17 + tail call void @llvm.dbg.value(metadata !{i64 %c}, i64 0, metadata !12), !dbg !17 + %add = add nsw i64 %c, 1, !dbg !18 + tail call void @llvm.dbg.value(metadata !{i64 %add}, i64 0, metadata !13), !dbg !18 + %cmp = icmp eq i64 %a, %b, !dbg !19 + %add1 = select i1 %cmp, i64 0, i64 %b, !dbg !19 + %retval.0 = add nsw i64 %add, %add1, !dbg !19 + ret i64 %retval.0, !dbg !21 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata) #1 + +attributes #0 = { nounwind readnone ssp } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!14, !15} +!llvm.ident = !{!16} + +!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 !""} ; [ DW_TAG_compile_unit ] [sdagsplit-2.c] [DW_LANG_C99] +!1 = metadata !{metadata !"sdagsplit-2.c", metadata !""} +!2 = metadata !{} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !1, metadata !5, metadata !"foo", metadata !"foo", metadata !"", i32 149, metadata !6, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 true, i64 (i64, i64, i64)* @foo, null, null, metadata !9, i32 150} ; [ DW_TAG_subprogram ] [line 149] [def] [scope 150] [foo] +!5 = metadata !{i32 786473, metadata !1} ; [ DW_TAG_file_type ] [sdagsplit-2.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 !8, metadata !8, metadata !8} +!8 = metadata !{i32 786468, null, null, metadata !"long long int", i32 0, i64 64, i64 32, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] [long long int] [line 0, size 64, align 32, offset 0, enc DW_ATE_signed] +!9 = metadata !{metadata !10, metadata !11, metadata !12, metadata !13} +!10 = metadata !{i32 786689, metadata !4, metadata !"a", metadata !5, i32 16777365, metadata !8, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [a] [line 149] +!11 = metadata !{i32 786689, metadata !4, metadata !"b", metadata !5, i32 33554581, metadata !8, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [b] [line 149] +!12 = metadata !{i32 786689, metadata !4, metadata !"c", metadata !5, i32 50331797, metadata !8, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [c] [line 149] +!13 = metadata !{i32 786688, metadata !4, metadata !"res", metadata !5, i32 151, metadata !8, i32 0, i32 0} ; [ DW_TAG_auto_variable ] [res] [line 151] +!14 = metadata !{i32 2, metadata !"Dwarf Version", i32 2} +!15 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!16 = metadata !{metadata !"clang version 3.5 "} +!17 = metadata !{i32 149, i32 0, metadata !4, null} +!18 = metadata !{i32 151, i32 0, metadata !4, null} +!19 = metadata !{i32 152, i32 0, metadata !20, null} +!20 = metadata !{i32 786443, metadata !1, metadata !4, i32 152, i32 0, i32 0} ; [ DW_TAG_lexical_block ] [sdagsplit-2.c] +!21 = metadata !{i32 155, i32 0, metadata !4, null} Index: test/DebugInfo/X86/sroasplit-1.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/sroasplit-1.ll @@ -0,0 +1,103 @@ +; RUN: opt -sroa -verify -S -asm-verbose -o %t.ll %s +; RUN: cat %t.ll | FileCheck --check-prefix=CHECK-OPT %s +; 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 partial emit debug info for aggregates repeatedly +; split up by SROA. +; +; // Compile with -O1 +; typedef struct { +; int a; +; long int b; +; } Inner; +; +; typedef struct { +; Inner inner[2]; +; } Outer; +; +; int foo(Outer outer) { +; Inner i1 = outer.inner[1]; +; return i1.a; +; } +; + +; Verify that SROA creates a variable piece when splitting i1. +; CHECK-OPT: %[[I1:.*]] = alloca [12 x i8], align 4 +; CHECK-OPT: call void @llvm.dbg.declare(metadata !{[12 x i8]* %[[I1]]}, metadata ![[PIECE:[0-9]+]]) +; CHECK-OPT: ![[PIECE]] = {{.*}} ; [ DW_TAG_auto_variable ] [i1] [line 11] [piece, size 12, offset 0] + +; eax, piece 0x00000004 +; CHECK-DWARF: Location description: 50 93 04 + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.9.0" + +%struct.Outer = type { [2 x %struct.Inner] } +%struct.Inner = type { i32, i64 } + +; Function Attrs: nounwind ssp uwtable +define i32 @foo(%struct.Outer* byval align 8 %outer) #0 { +entry: + %i1 = alloca %struct.Inner, align 8 + call void @llvm.dbg.declare(metadata !{%struct.Outer* %outer}, metadata !25), !dbg !26 + call void @llvm.dbg.declare(metadata !{%struct.Inner* %i1}, metadata !27), !dbg !28 + %inner = getelementptr inbounds %struct.Outer* %outer, i32 0, i32 0, !dbg !28 + %arrayidx = getelementptr inbounds [2 x %struct.Inner]* %inner, i32 0, i64 1, !dbg !28 + %0 = bitcast %struct.Inner* %i1 to i8*, !dbg !28 + %1 = bitcast %struct.Inner* %arrayidx to i8*, !dbg !28 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %0, i8* %1, i64 16, i32 8, i1 false), !dbg !28 + %a = getelementptr inbounds %struct.Inner* %i1, i32 0, i32 0, !dbg !29 + %2 = load i32* %a, align 4, !dbg !29 + ret i32 %2, !dbg !29 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata) #1 + +; Function Attrs: nounwind +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1) #2 + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata) #1 + +attributes #0 = { nounwind ssp uwtable } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!22, !23} +!llvm.ident = !{!24} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5.0 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !"", i32 1} ; [ DW_TAG_compile_unit ] [sroasplit-1.c] [DW_LANG_C99] +!1 = metadata !{metadata !"sroasplit-1.c", metadata !""} +!2 = metadata !{} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !1, metadata !5, metadata !"foo", metadata !"foo", metadata !"", i32 10, metadata !6, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 (%struct.Outer*)* @foo, null, null, metadata !2, i32 10} ; [ DW_TAG_subprogram ] [line 10] [def] [foo] +!5 = metadata !{i32 786473, metadata !1} ; [ DW_TAG_file_type ] [sroasplit-1.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 !"Outer", i32 8, i64 0, i64 0, i64 0, i32 0, metadata !10} ; [ DW_TAG_typedef ] [Outer] [line 8, size 0, align 0, offset 0] [from ] +!10 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 6, i64 256, i64 64, i32 0, i32 0, null, metadata !11, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 6, size 256, align 64, offset 0] [def] [from ] +!11 = metadata !{metadata !12} +!12 = metadata !{i32 786445, metadata !1, metadata !10, metadata !"inner", i32 7, i64 256, i64 64, i64 0, i32 0, metadata !13} ; [ DW_TAG_member ] [inner] [line 7, size 256, align 64, offset 0] [from ] +!13 = metadata !{i32 786433, null, null, metadata !"", i32 0, i64 256, i64 64, i32 0, i32 0, metadata !14, metadata !20, i32 0, null, null, null} ; [ DW_TAG_array_type ] [line 0, size 256, align 64, offset 0] [from Inner] +!14 = metadata !{i32 786454, metadata !1, null, metadata !"Inner", i32 4, i64 0, i64 0, i64 0, i32 0, metadata !15} ; [ DW_TAG_typedef ] [Inner] [line 4, size 0, align 0, offset 0] [from ] +!15 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 1, i64 128, i64 64, i32 0, i32 0, null, metadata !16, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 1, size 128, align 64, offset 0] [def] [from ] +!16 = metadata !{metadata !17, metadata !18} +!17 = metadata !{i32 786445, metadata !1, metadata !15, metadata !"a", i32 2, i64 32, i64 32, i64 0, i32 0, metadata !8} ; [ DW_TAG_member ] [a] [line 2, size 32, align 32, offset 0] [from int] +!18 = metadata !{i32 786445, metadata !1, metadata !15, metadata !"b", i32 3, i64 64, i64 64, i64 64, i32 0, metadata !19} ; [ DW_TAG_member ] [b] [line 3, size 64, align 64, offset 64] [from long int] +!19 = 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] +!20 = metadata !{metadata !21} +!21 = metadata !{i32 786465, i64 0, i64 2} ; [ DW_TAG_subrange_type ] [0, 1] +!22 = metadata !{i32 2, metadata !"Dwarf Version", i32 2} +!23 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!24 = metadata !{metadata !"clang version 3.5.0 "} +!25 = metadata !{i32 786689, metadata !4, metadata !"outer", metadata !5, i32 16777226, metadata !9, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [outer] [line 10] +!26 = metadata !{i32 10, i32 0, metadata !4, null} +!27 = metadata !{i32 786688, metadata !4, metadata !"i1", metadata !5, i32 11, metadata !14, i32 0, i32 0} ; [ DW_TAG_auto_variable ] [i1] [line 11] +!28 = metadata !{i32 11, i32 0, metadata !4, null} +!29 = metadata !{i32 12, i32 0, metadata !4, null} Index: test/DebugInfo/X86/sroasplit-2.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/sroasplit-2.ll @@ -0,0 +1,131 @@ +; RUN: opt -sroa -verify -S -asm-verbose -o %t.ll %s +; RUN: cat %t.ll | FileCheck --check-prefix=CHECK-OPT %s +; RUN: llc %t.ll -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck --check-prefix=CHECK-DWARF %s +; +; rdar://problem/15928306 +; +; Test that we can partial emit debug info for aggregates repeatedly +; split up by SROA. +; +; // Compile with -O1 +; typedef struct { +; int a; +; int b; +; } Inner; +; +; typedef struct { +; Inner inner[2]; +; } Outer; +; +; int foo(Outer outer) { +; Inner i1 = outer.inner[1]; +; return i1.a; +; } +; + +; Verify that SROA creates a variable piece when splitting i1. +; CHECK-OPT: call void @llvm.dbg.value(metadata !{i64 %outer.coerce0}, i64 0, metadata ![[O1:.*]]), +; CHECK-OPT: call void @llvm.dbg.value(metadata !{i64 %outer.coerce1}, i64 0, metadata ![[O2:.*]]), +; CHECK-OPT: call void @llvm.dbg.value({{.*}}, i64 0, metadata ![[I1:.*]]), +; CHECK-OPT-DAG: ![[O2]] = {{.*}} [ DW_TAG_arg_variable ] [outer] [line 10] [piece, size 4, offset 12] +; CHECK-OPT-DAG: ![[O1]] = {{.*}} [ DW_TAG_arg_variable ] [outer] [line 10] [piece, size 8, offset 0] +; CHECK-OPT-DAG: ![[I1]] = {{.*}} [ DW_TAG_auto_variable ] [i1] [line 11] [piece, size 4, offset 0] + +; CHECK-DWARF: DW_TAG_formal_parameter +; CHECK-DWARF: "outer" +; CHECK-DWARF: DW_AT_location [DW_FORM_data4] ([[LOC:.*]]) +; CHECK-DWARF: DW_TAG_variable +; CHECK-DWARF: "i1" +; rsi, piece 0x00000004, bit-piece 32 0 +; CHECK-DWARF: DW_AT_location [DW_FORM_block1] (<0x06> 54 93 04 9d 20 00 ) +; CHECK-DWARF: .debug_loc +; CHECK-DWARF: [[LOC]]: +; CHECK-DWARF: Beginning address offset: 0x0000000000000000 +; CHECK-DWARF: Ending address offset: 0x0000000000000008 +; rdi, piece 0x00000008 +; CHECK-DWARF: Location description: 55 93 08 + +; CHECK-DWARF: Beginning address offset: 0x0000000000000000 +; CHECK-DWARF: Ending address offset: 0x0000000000000004 +; rsi, bit-piece 32 96 +; CHECK-DWARF: Location description: 54 9d 20 60 + +; CHECK-DWARF: Beginning address offset: 0x0000000000000004 +; CHECK-DWARF: Ending address offset: 0x0000000000000008 +; rsi, bit-piece 32 96 +; CHECK-DWARF: Location description: 54 9d 20 60 + + +; ModuleID = 'sroasplit-2.c' +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.9.0" + +%struct.Outer = type { [2 x %struct.Inner] } +%struct.Inner = type { i32, i32 } + +; Function Attrs: nounwind ssp uwtable +define i32 @foo(i64 %outer.coerce0, i64 %outer.coerce1) #0 { +entry: + %outer = alloca %struct.Outer, align 8 + %i1 = alloca %struct.Inner, align 4 + %0 = bitcast %struct.Outer* %outer to { i64, i64 }* + %1 = getelementptr { i64, i64 }* %0, i32 0, i32 0 + store i64 %outer.coerce0, i64* %1 + %2 = getelementptr { i64, i64 }* %0, i32 0, i32 1 + store i64 %outer.coerce1, i64* %2 + call void @llvm.dbg.declare(metadata !{%struct.Outer* %outer}, metadata !24), !dbg !25 + call void @llvm.dbg.declare(metadata !{%struct.Inner* %i1}, metadata !26), !dbg !27 + %inner = getelementptr inbounds %struct.Outer* %outer, i32 0, i32 0, !dbg !27 + %arrayidx = getelementptr inbounds [2 x %struct.Inner]* %inner, i32 0, i64 1, !dbg !27 + %3 = bitcast %struct.Inner* %i1 to i8*, !dbg !27 + %4 = bitcast %struct.Inner* %arrayidx to i8*, !dbg !27 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %3, i8* %4, i64 8, i32 4, i1 false), !dbg !27 + %a = getelementptr inbounds %struct.Inner* %i1, i32 0, i32 0, !dbg !28 + %5 = load i32* %a, align 4, !dbg !28 + ret i32 %5, !dbg !28 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata) #1 + +; Function Attrs: nounwind +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1) #2 + +attributes #0 = { nounwind ssp uwtable } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!21, !22} +!llvm.ident = !{!23} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5.0 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !"", i32 1} ; [ DW_TAG_compile_unit ] [sroasplit-2.c] [DW_LANG_C99] +!1 = metadata !{metadata !"sroasplit-2.c", metadata !""} +!2 = metadata !{} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !1, metadata !5, metadata !"foo", metadata !"foo", metadata !"", i32 10, metadata !6, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 (i64, i64)* @foo, null, null, metadata !2, i32 10} ; [ DW_TAG_subprogram ] [line 10] [def] [foo] +!5 = metadata !{i32 786473, metadata !1} ; [ DW_TAG_file_type ] [sroasplit-2.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 !"Outer", i32 8, i64 0, i64 0, i64 0, i32 0, metadata !10} ; [ DW_TAG_typedef ] [Outer] [line 8, size 0, align 0, offset 0] [from ] +!10 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 6, i64 128, i64 32, i32 0, i32 0, null, metadata !11, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 6, size 128, align 32, offset 0] [def] [from ] +!11 = metadata !{metadata !12} +!12 = metadata !{i32 786445, metadata !1, metadata !10, metadata !"inner", i32 7, i64 128, i64 32, i64 0, i32 0, metadata !13} ; [ DW_TAG_member ] [inner] [line 7, size 128, align 32, offset 0] [from ] +!13 = metadata !{i32 786433, null, null, metadata !"", i32 0, i64 128, i64 32, i32 0, i32 0, metadata !14, metadata !19, i32 0, null, null, null} ; [ DW_TAG_array_type ] [line 0, size 128, align 32, offset 0] [from Inner] +!14 = metadata !{i32 786454, metadata !1, null, metadata !"Inner", i32 4, i64 0, i64 0, i64 0, i32 0, metadata !15} ; [ DW_TAG_typedef ] [Inner] [line 4, size 0, align 0, offset 0] [from ] +!15 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 1, i64 64, i64 32, i32 0, i32 0, null, metadata !16, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 1, size 64, align 32, offset 0] [def] [from ] +!16 = metadata !{metadata !17, metadata !18} +!17 = metadata !{i32 786445, metadata !1, metadata !15, metadata !"a", i32 2, i64 32, i64 32, i64 0, i32 0, metadata !8} ; [ DW_TAG_member ] [a] [line 2, size 32, align 32, offset 0] [from int] +!18 = metadata !{i32 786445, metadata !1, metadata !15, metadata !"b", i32 3, i64 32, i64 32, i64 32, i32 0, metadata !8} ; [ DW_TAG_member ] [b] [line 3, size 32, align 32, offset 32] [from int] +!19 = metadata !{metadata !20} +!20 = metadata !{i32 786465, i64 0, i64 2} ; [ DW_TAG_subrange_type ] [0, 1] +!21 = metadata !{i32 2, metadata !"Dwarf Version", i32 2} +!22 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!23 = metadata !{metadata !"clang version 3.5.0 "} +!24 = metadata !{i32 786689, metadata !4, metadata !"outer", metadata !5, i32 16777226, metadata !9, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [outer] [line 10] +!25 = metadata !{i32 10, i32 0, metadata !4, null} +!26 = metadata !{i32 786688, metadata !4, metadata !"i1", metadata !5, i32 11, metadata !14, i32 0, i32 0} ; [ DW_TAG_auto_variable ] [i1] [line 11] +!27 = metadata !{i32 11, i32 0, metadata !4, null} +!28 = metadata !{i32 12, i32 0, metadata !4, null}