Index: llvm/trunk/include/llvm/CodeGen/MachineInstr.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/MachineInstr.h +++ llvm/trunk/include/llvm/CodeGen/MachineInstr.h @@ -292,6 +292,10 @@ /// this DBG_VALUE instruction. const DIExpression *getDebugExpression() const; + /// Return the debug label referenced by + /// this DBG_LABEL instruction. + const DILabel *getDebugLabel() const; + /// 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 @@ -831,6 +835,8 @@ bool isPosition() const { return isLabel() || isCFIInstruction(); } bool isDebugValue() const { return getOpcode() == TargetOpcode::DBG_VALUE; } + bool isDebugLabel() const { return getOpcode() == TargetOpcode::DBG_LABEL; } + bool isDebugInstr() const { return isDebugValue() || isDebugLabel(); } /// A DBG_VALUE is indirect iff the first operand is a register and /// the second operand is an immediate. @@ -907,6 +913,7 @@ case TargetOpcode::EH_LABEL: case TargetOpcode::GC_LABEL: case TargetOpcode::DBG_VALUE: + case TargetOpcode::DBG_LABEL: case TargetOpcode::LIFETIME_START: case TargetOpcode::LIFETIME_END: return true; Index: llvm/trunk/include/llvm/CodeGen/SelectionDAG.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/SelectionDAG.h +++ llvm/trunk/include/llvm/CodeGen/SelectionDAG.h @@ -73,6 +73,7 @@ class MCSymbol; class OptimizationRemarkEmitter; class SDDbgValue; +class SDDbgLabel; class SelectionDAG; class SelectionDAGTargetInfo; class TargetLibraryInfo; @@ -148,6 +149,7 @@ BumpPtrAllocator Alloc; SmallVector DbgValues; SmallVector ByvalParmDbgValues; + SmallVector DbgLabels; using DbgValMapType = DenseMap>; DbgValMapType DbgValMap; @@ -164,6 +166,10 @@ DbgValMap[Node].push_back(V); } + void add(SDDbgLabel *L) { + DbgLabels.push_back(L); + } + /// Invalidate all DbgValues attached to the node and remove /// it from the Node-to-DbgValues map. void erase(const SDNode *Node); @@ -172,13 +178,14 @@ DbgValMap.clear(); DbgValues.clear(); ByvalParmDbgValues.clear(); + DbgLabels.clear(); Alloc.Reset(); } BumpPtrAllocator &getAlloc() { return Alloc; } bool empty() const { - return DbgValues.empty() && ByvalParmDbgValues.empty(); + return DbgValues.empty() && ByvalParmDbgValues.empty() && DbgLabels.empty(); } ArrayRef getSDDbgValues(const SDNode *Node) { @@ -189,11 +196,14 @@ } using DbgIterator = SmallVectorImpl::iterator; + using DbgLabelIterator = SmallVectorImpl::iterator; DbgIterator DbgBegin() { return DbgValues.begin(); } DbgIterator DbgEnd() { return DbgValues.end(); } DbgIterator ByvalParmDbgBegin() { return ByvalParmDbgValues.begin(); } DbgIterator ByvalParmDbgEnd() { return ByvalParmDbgValues.end(); } + DbgLabelIterator DbgLabelBegin() { return DbgLabels.begin(); } + DbgLabelIterator DbgLabelEnd() { return DbgLabels.end(); } }; void checkForCycles(const SelectionDAG *DAG, bool force = false); @@ -255,7 +265,7 @@ /// Pool allocation for misc. objects that are created once per SelectionDAG. BumpPtrAllocator Allocator; - /// Tracks dbg_value information through SDISel. + /// Tracks dbg_value and dbg_label information through SDISel. SDDbgInfo *DbgInfo; uint16_t NextPersistentId = 0; @@ -1247,6 +1257,9 @@ unsigned VReg, bool IsIndirect, const DebugLoc &DL, unsigned O); + /// Creates a SDDbgLabel node. + SDDbgLabel *getDbgLabel(DILabel *Label, const DebugLoc &DL, unsigned O); + /// Transfer debug values from one node to another, while optionally /// generating fragment expressions for split-up values. If \p InvalidateDbg /// is set, debug values are invalidated after they are transferred. @@ -1328,6 +1341,9 @@ /// value is produced by SD. void AddDbgValue(SDDbgValue *DB, SDNode *SD, bool isParameter); + /// Add a dbg_label SDNode. + void AddDbgLabel(SDDbgLabel *DB); + /// Get the debug values which reference the given SDNode. ArrayRef GetDbgValues(const SDNode* SD) { return DbgInfo->getSDDbgValues(SD); @@ -1349,6 +1365,13 @@ return DbgInfo->ByvalParmDbgEnd(); } + SDDbgInfo::DbgLabelIterator DbgLabelBegin() { + return DbgInfo->DbgLabelBegin(); + } + SDDbgInfo::DbgLabelIterator DbgLabelEnd() { + return DbgInfo->DbgLabelEnd(); + } + /// To be invoked on an SDNode that is slated to be erased. This /// function mirrors \c llvm::salvageDebugInfo. void salvageDebugInfo(SDNode &N); Index: llvm/trunk/include/llvm/Support/TargetOpcodes.def =================================================================== --- llvm/trunk/include/llvm/Support/TargetOpcodes.def +++ llvm/trunk/include/llvm/Support/TargetOpcodes.def @@ -77,6 +77,9 @@ /// DBG_VALUE - a mapping of the llvm.dbg.value intrinsic HANDLE_TARGET_OPCODE(DBG_VALUE) +/// DBG_LABEL - a mapping of the llvm.dbg.label intrinsic +HANDLE_TARGET_OPCODE(DBG_LABEL) + /// REG_SEQUENCE - This variadic instruction is used to form a register that /// represents a consecutive sequence of sub-registers. It's used as a /// register coalescing / allocation aid and must be eliminated before code Index: llvm/trunk/include/llvm/Target/Target.td =================================================================== --- llvm/trunk/include/llvm/Target/Target.td +++ llvm/trunk/include/llvm/Target/Target.td @@ -997,6 +997,12 @@ let AsmString = "DBG_VALUE"; let hasSideEffects = 0; } +def DBG_LABEL : StandardPseudoInstruction { + let OutOperandList = (outs); + let InOperandList = (ins unknown:$label); + let AsmString = "DBG_LABEL"; + let hasSideEffects = 0; +} def REG_SEQUENCE : StandardPseudoInstruction { let OutOperandList = (outs unknown:$dst); let InOperandList = (ins unknown:$supersrc, variable_ops); Index: llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -911,6 +911,30 @@ return true; } +/// This method handles the target-independent form of DBG_LABEL, returning +/// true if it was able to do so. A false return means the target will need +/// to handle MI in EmitInstruction. +static bool emitDebugLabelComment(const MachineInstr *MI, AsmPrinter &AP) { + if (MI->getNumOperands() != 1) + return false; + + SmallString<128> Str; + raw_svector_ostream OS(Str); + OS << "DEBUG_LABEL: "; + + const DILabel *V = MI->getDebugLabel(); + if (auto *SP = dyn_cast(V->getScope())) { + StringRef Name = SP->getName(); + if (!Name.empty()) + OS << Name << ":"; + } + OS << V->getName(); + + // NOTE: Want this comment at start of line, don't emit with AddComment. + AP.OutStreamer->emitRawComment(OS.str()); + return true; +} + AsmPrinter::CFIMoveType AsmPrinter::needsCFIMoves() const { if (MAI->getExceptionHandlingType() == ExceptionHandling::DwarfCFI && MF->getFunction().needsUnwindTableEntry()) @@ -1074,6 +1098,12 @@ EmitInstruction(&MI); } break; + case TargetOpcode::DBG_LABEL: + if (isVerbose()) { + if (!emitDebugLabelComment(&MI, *this)) + EmitInstruction(&MI); + } + break; case TargetOpcode::IMPLICIT_DEF: if (isVerbose()) emitImplicitDef(&MI); break; Index: llvm/trunk/lib/CodeGen/MachineInstr.cpp =================================================================== --- llvm/trunk/lib/CodeGen/MachineInstr.cpp +++ llvm/trunk/lib/CodeGen/MachineInstr.cpp @@ -613,6 +613,11 @@ return -1; } +const DILabel *MachineInstr::getDebugLabel() const { + assert(isDebugLabel() && "not a DBG_LABEL"); + return cast(getOperand(0).getMetadata()); +} + const DILocalVariable *MachineInstr::getDebugVariable() const { assert(isDebugValue() && "not a DBG_VALUE"); return cast(getOperand(2).getMetadata()); @@ -1383,6 +1388,17 @@ MO.print(OS, MST, TypeToPrint, /*PrintDef=*/true, IsStandalone, ShouldPrintRegisterTies, TiedOperandIdx, TRI, IntrinsicInfo); } + } else if (isDebugLabel() && MO.isMetadata()) { + // Pretty print DBG_LABEL instructions. + auto *DIL = dyn_cast(MO.getMetadata()); + if (DIL && !DIL->getName().empty()) + OS << "\"" << DIL->getName() << '\"'; + else { + LLT TypeToPrint = MRI ? getTypeToPrint(i, PrintedTypes, *MRI) : LLT{}; + unsigned TiedOperandIdx = getTiedOperandIdx(i); + MO.print(OS, MST, TypeToPrint, /*PrintDef=*/true, IsStandalone, + ShouldPrintRegisterTies, TiedOperandIdx, TRI, IntrinsicInfo); + } } else if (i == AsmDescOp && MO.isImm()) { // Pretty print the inline asm operand descriptor. OS << '$' << AsmOpCount++; Index: llvm/trunk/lib/CodeGen/PatchableFunction.cpp =================================================================== --- llvm/trunk/lib/CodeGen/PatchableFunction.cpp +++ llvm/trunk/lib/CodeGen/PatchableFunction.cpp @@ -49,6 +49,7 @@ case TargetOpcode::EH_LABEL: case TargetOpcode::GC_LABEL: case TargetOpcode::DBG_VALUE: + case TargetOpcode::DBG_LABEL: return true; } } Index: llvm/trunk/lib/CodeGen/SelectionDAG/InstrEmitter.h =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/InstrEmitter.h +++ llvm/trunk/lib/CodeGen/SelectionDAG/InstrEmitter.h @@ -113,6 +113,9 @@ MachineInstr *EmitDbgValue(SDDbgValue *SD, DenseMap &VRBaseMap); + /// Generate machine instruction for a dbg_label node. + MachineInstr *EmitDbgLabel(SDDbgLabel *SD); + /// EmitNode - Generate machine code for a node and needed dependencies. /// void EmitNode(SDNode *Node, bool IsClone, bool IsCloned, Index: llvm/trunk/lib/CodeGen/SelectionDAG/InstrEmitter.cpp =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ llvm/trunk/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -753,6 +753,20 @@ return &*MIB; } +MachineInstr * +InstrEmitter::EmitDbgLabel(SDDbgLabel *SD) { + MDNode *Label = SD->getLabel(); + DebugLoc DL = SD->getDebugLoc(); + assert(cast(Label)->isValidLocationForIntrinsic(DL) && + "Expected inlined-at fields to agree"); + + const MCInstrDesc &II = TII->get(TargetOpcode::DBG_LABEL); + MachineInstrBuilder MIB = BuildMI(*MF, DL, II); + MIB.addMetadata(Label); + + return &*MIB; +} + /// EmitMachineNode - Generate machine code for a target-specific node and /// needed dependencies. /// Index: llvm/trunk/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h +++ llvm/trunk/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h @@ -128,6 +128,28 @@ bool isInvalidated() const { return Invalid; } }; +/// Holds the information from a dbg_label node through SDISel. +/// We do not use SDValue here to avoid including its header. +class SDDbgLabel { + MDNode *Label; + DebugLoc DL; + unsigned Order; + +public: + SDDbgLabel(MDNode *Label, DebugLoc dl, unsigned O) + : Label(Label), DL(std::move(dl)), Order(O) {} + + /// Returns the MDNode pointer for the label. + MDNode *getLabel() const { return Label; } + + /// Returns the DebugLoc. + DebugLoc getDebugLoc() const { return DL; } + + /// Returns the SDNodeOrder. This is the order of the preceding node in the + /// input. + unsigned getOrder() const { return Order; } +}; + } // end llvm namespace #endif Index: llvm/trunk/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp +++ llvm/trunk/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp @@ -911,6 +911,39 @@ MachineBasicBlock *InsertBB = Emitter.getBlock(); MachineBasicBlock::iterator Pos = InsertBB->getFirstTerminator(); InsertBB->insert(Pos, DbgMIs.begin(), DbgMIs.end()); + + SDDbgInfo::DbgLabelIterator DLI = DAG->DbgLabelBegin(); + SDDbgInfo::DbgLabelIterator DLE = DAG->DbgLabelEnd(); + // Now emit the rest according to source order. + LastOrder = 0; + for (const auto &InstrOrder : Orders) { + unsigned Order = InstrOrder.first; + MachineInstr *MI = InstrOrder.second; + if (!MI) + continue; + + // Insert all SDDbgLabel's whose order(s) are before "Order". + for (; DLI != DLE && + (*DLI)->getOrder() >= LastOrder && (*DLI)->getOrder() < Order; + ++DLI) { + MachineInstr *DbgMI = Emitter.EmitDbgLabel(*DLI); + if (DbgMI) { + if (!LastOrder) + // Insert to start of the BB (after PHIs). + BB->insert(BBBegin, DbgMI); + else { + // Insert at the instruction, which may be in a different + // block, if the block was split by a custom inserter. + MachineBasicBlock::iterator Pos = MI; + MI->getParent()->insert(Pos, DbgMI); + } + } + } + if (DLI == DLE) + break; + + LastOrder = Order; + } } InsertPos = Emitter.getInsertPos(); Index: llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAG.cpp =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -7420,6 +7420,14 @@ AddDbgValue(Dbg, Dbg->getSDNode(), false); } +/// Creates a SDDbgLabel node. +SDDbgLabel *SelectionDAG::getDbgLabel(DILabel *Label, + const DebugLoc &DL, unsigned O) { + assert(cast(Label)->isValidLocationForIntrinsic(DL) && + "Expected inlined-at fields to agree"); + return new (DbgInfo->getAlloc()) SDDbgLabel(Label, DL, O); +} + namespace { /// RAUWUpdateListener - Helper for ReplaceAllUsesWith - When the node @@ -7905,6 +7913,10 @@ DbgInfo->add(DB, SD, isParameter); } +void SelectionDAG::AddDbgLabel(SDDbgLabel *DB) { + DbgInfo->add(DB); +} + SDValue SelectionDAG::makeEquivalentMemoryOrdering(LoadSDNode *OldLoad, SDValue NewMemOp) { assert(isa(NewMemOp.getNode()) && "Expected a memop node"); Index: llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5230,6 +5230,16 @@ } return nullptr; } + case Intrinsic::dbg_label: { + const DbgLabelInst &DI = cast(I); + DILabel *Label = DI.getLabel(); + assert(Label && "Missing label"); + + SDDbgLabel *SDV; + SDV = DAG.getDbgLabel(Label, dl, SDNodeOrder); + DAG.AddDbgLabel(SDV); + return nullptr; + } case Intrinsic::dbg_value: { const DbgValueInst &DI = cast(I); assert(DI.getVariable() && "Missing variable"); Index: llvm/trunk/test/DebugInfo/Generic/debug-label-mi.ll =================================================================== --- llvm/trunk/test/DebugInfo/Generic/debug-label-mi.ll +++ llvm/trunk/test/DebugInfo/Generic/debug-label-mi.ll @@ -0,0 +1,58 @@ +; Test DBG_LABEL MachineInstr for label debugging. +; RUN: llc -fast-isel=false -debug-only=isel %s -o /dev/null 2> %t.debug +; RUN: cat %t.debug | FileCheck %s --check-prefix=CHECKMI +; +; CHECKMI: DBG_LABEL "top", debug-location !9 +; CHECKMI: DBG_LABEL "done", debug-location !11 +; +; RUN: llc -fast-isel=false %s -o - | FileCheck %s --check-prefix=CHECKASM +; +; CHECKASM: #DEBUG_LABEL: foo:top +; CHECKASM: #DEBUG_LABEL: foo:done + +source_filename = "debug-label-mi.c" + +; Function Attrs: noinline nounwind optnone +define i32 @foo(i32 signext %a, i32 signext %b) #0 !dbg !4 { +entry: + %a.addr = alloca i32, align 4 + %b.addr = alloca i32, align 4 + %sum = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + store i32 %b, i32* %b.addr, align 4 + br label %top + +top: ; preds = %entry + call void @llvm.dbg.label(metadata !8), !dbg !9 + %0 = load i32, i32* %a.addr, align 4 + %1 = load i32, i32* %b.addr, align 4 + %add = add nsw i32 %0, %1 + store i32 %add, i32* %sum, align 4 + br label %done + +done: ; preds = %top + call void @llvm.dbg.label(metadata !10), !dbg !11 + %2 = load i32, i32* %sum, align 4 + ret i32 %2 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.label(metadata) + +attributes #0 = { noinline nounwind optnone uwtable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: false, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "debug-label-mi.c", directory: "./") +!2 = !{} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: false, unit: !0, retainedNodes: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{!7, !7, !7} +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !DILabel(scope: !4, name: "top", file: !1, line: 4) +!9 = !DILocation(line: 4, column: 1, scope: !4) +!10 = !DILabel(scope: !4, name: "done", file: !1, line: 7) +!11 = !DILocation(line: 7, column: 1, scope: !4) Index: llvm/trunk/test/DebugInfo/Generic/debug-label-opt.ll =================================================================== --- llvm/trunk/test/DebugInfo/Generic/debug-label-opt.ll +++ llvm/trunk/test/DebugInfo/Generic/debug-label-opt.ll @@ -0,0 +1,73 @@ +; Test DBG_LABEL MachineInstr under optimization. +; The test case is generated by clang with -O2 is on. +; RUN: llc -fast-isel=false -debug-only=isel %s -o /dev/null 2> %t.debug +; RUN: cat %t.debug | FileCheck %s --check-prefix=CHECKMI +; +; CHECKMI: DBG_LABEL "end_sum", debug-location !18 +; CHECKMI: DBG_LABEL "end", debug-location !20 +source_filename = "debug-label-opt.c" + +define i32 @foo(i32* nocapture readonly %a, i32 %n) local_unnamed_addr !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i32* %a, metadata !13, metadata !DIExpression()), !dbg !6 + call void @llvm.dbg.value(metadata i32 %n, metadata !14, metadata !DIExpression()), !dbg !6 + call void @llvm.dbg.value(metadata i32 0, metadata !15, metadata !DIExpression()), !dbg !6 + switch i32 %n, label %end_sum [ + i32 2, label %end_sum.sink.split + i32 3, label %if.then3 + ], !dbg !6 + +if.then3: ; preds = %entry + %arrayidx4 = getelementptr inbounds i32, i32* %a, i64 1, !dbg !6 + br label %end_sum.sink.split, !dbg !6 + +end_sum.sink.split: ; preds = %entry, %if.then3 + %a.sink = phi i32* [ %arrayidx4, %if.then3 ], [ %a, %entry ] + %.sink = phi i64 [ 2, %if.then3 ], [ 1, %entry ] + %0 = load i32, i32* %a.sink, align 4 + %arrayidx1 = getelementptr inbounds i32, i32* %a, i64 %.sink + %1 = load i32, i32* %arrayidx1, align 4 + %add = add nsw i32 %1, %0 + br label %end_sum, !dbg !6 + +end_sum: ; preds = %end_sum.sink.split, %entry + %sum.0 = phi i32 [ 0, %entry ], [ %add, %end_sum.sink.split ] + call void @llvm.dbg.value(metadata i32 %sum.0, metadata !15, metadata !DIExpression()), !dbg !6 + call void @llvm.dbg.label(metadata !16), !dbg !18 + %2 = load i32, i32* %a, align 4, !dbg !6 + %mul = mul nsw i32 %2, %sum.0, !dbg !19 + call void @llvm.dbg.value(metadata i32 %mul, metadata !15, metadata !DIExpression()), !dbg !6 + call void @llvm.dbg.label(metadata !17), !dbg !20 + ret i32 %mul, !dbg !6 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.label(metadata) + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "debug-label-opt.c", directory: "./") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !DILocation(line: 1, column: 14, scope: !7) +!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !12) +!8 = !DISubroutineType(types: !9) +!9 = !{!10, !11, !10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) +!12 = !{!13, !14, !15, !16, !17} +!13 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 1, type: !11) +!14 = !DILocalVariable(name: "n", arg: 2, scope: !7, file: !1, line: 1, type: !10) +!15 = !DILocalVariable(name: "sum", scope: !7, file: !1, line: 3, type: !10) +!16 = !DILabel(scope: !7, name: "end_sum", file: !1, line: 10) +!17 = !DILabel(scope: !7, name: "end", file: !1, line: 13) +!18 = !DILocation(line: 10, column: 1, scope: !7) +!19 = !DILocation(line: 11, column: 7, scope: !7) +!20 = !DILocation(line: 13, column: 1, scope: !7)