From 316138a83dae60a04938e92f259eb69ab8fd84fb Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Fri, 29 Jan 2016 18:16:43 +0000 Subject: [PATCH] [codeview] Begin to add support for inlined call sites Summary: There are three parts to inlined call frames: 1. The inlinee line subsection 2. The inline site symbol record 3. The function ids referenced by both This change starts by emitting function ids (3) for all subprograms and emitting the base inline site symbol record (2). The actual line numbers in (2) use an encoded format that will come next, along with the inlinee line subsection. Reviewers: majnemer Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D16333 llvm-svn: 259217 --- include/llvm/MC/MCObjectFileInfo.h | 5 + lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 139 +++++++++++++++++++- lib/CodeGen/AsmPrinter/CodeViewDebug.h | 30 ++++- lib/MC/MCObjectFileInfo.cpp | 13 +- test/DebugInfo/COFF/inlining.ll | 160 +++++++++++++++++++++++ 5 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 test/DebugInfo/COFF/inlining.ll diff --git a/include/llvm/MC/MCObjectFileInfo.h b/include/llvm/MC/MCObjectFileInfo.h index 8a3a6af3bf7..f8346f22a23 100644 --- a/include/llvm/MC/MCObjectFileInfo.h +++ b/include/llvm/MC/MCObjectFileInfo.h @@ -127,6 +127,7 @@ protected: MCSection *DwarfGnuPubTypesSection; MCSection *COFFDebugSymbolsSection; + MCSection *COFFDebugTypesSection; /// Extra TLS Variable Data section. /// @@ -274,6 +275,10 @@ public: MCSection *getCOFFDebugSymbolsSection() const { return COFFDebugSymbolsSection; } + MCSection *getCOFFDebugTypesSection() const { + return COFFDebugTypesSection; + } + MCSection *getTLSExtraDataSection() const { return TLSExtraDataSection; } const MCSection *getTLSDataSection() const { return TLSDataSection; } diff --git a/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index b503f1dc31e..e3c48296f34 100644 --- a/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -15,6 +15,8 @@ #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/Line.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/COFF.h" @@ -87,6 +89,17 @@ unsigned CodeViewDebug::maybeRecordFile(const DIFile *F) { return Insertion.first->second; } +CodeViewDebug::InlineSite &CodeViewDebug::getInlineSite(const DILocation *Loc) { + const DILocation *InlinedAt = Loc->getInlinedAt(); + auto Insertion = CurFn->InlineSites.insert({InlinedAt, InlineSite()}); + if (Insertion.second) { + InlineSite &Site = Insertion.first->second; + Site.SiteFuncId = NextFuncId++; + Site.Inlinee = Loc->getScope()->getSubprogram(); + } + return Insertion.first->second; +} + void CodeViewDebug::maybeRecordLocation(DebugLoc DL, const MachineFunction *MF) { // Skip this instruction if it has the same location as the previous one. @@ -115,7 +128,28 @@ void CodeViewDebug::maybeRecordLocation(DebugLoc DL, else FileId = CurFn->LastFileId = maybeRecordFile(DL->getFile()); CurFn->LastLoc = DL; - Asm->OutStreamer->EmitCVLocDirective(CurFn->FuncId, FileId, DL.getLine(), + + unsigned FuncId = CurFn->FuncId; + if (const DILocation *Loc = DL->getInlinedAt()) { + // If this location was actually inlined from somewhere else, give it the ID + // of the inline call site. + FuncId = getInlineSite(DL.get()).SiteFuncId; + // Ensure we have links in the tree of inline call sites. + const DILocation *ChildLoc = nullptr; + while (Loc->getInlinedAt()) { + InlineSite &Site = getInlineSite(Loc); + if (ChildLoc) { + // Record the child inline site if not already present. + auto B = Site.ChildSites.begin(), E = Site.ChildSites.end(); + if (std::find(B, E, Loc) != E) + break; + Site.ChildSites.push_back(Loc); + } + ChildLoc = Loc; + } + } + + Asm->OutStreamer->EmitCVLocDirective(FuncId, FileId, DL.getLine(), DL.getCol(), /*PrologueEnd=*/false, /*IsStmt=*/false, DL->getFilename()); } @@ -139,6 +173,8 @@ void CodeViewDebug::endModule() { if (FnDebugInfo.empty()) return; + emitTypeInformation(); + // FIXME: For functions that are comdat, we should emit separate .debug$S // sections that are comdat associative with the main function instead of // having one big .debug$S section. @@ -167,6 +203,60 @@ void CodeViewDebug::endModule() { clear(); } +template static void emitRecord(MCStreamer &OS, const T &Rec) { + OS.EmitBytes(StringRef(reinterpret_cast(&Rec), sizeof(Rec))); +} + +void CodeViewDebug::emitTypeInformation() { + // Start the .debug$T section with 0x4. + Asm->OutStreamer->SwitchSection( + Asm->getObjFileLowering().getCOFFDebugTypesSection()); + Asm->EmitInt32(COFF::DEBUG_SECTION_MAGIC); + + NamedMDNode *CU_Nodes = + Asm->MMI->getModule()->getNamedMetadata("llvm.dbg.cu"); + if (!CU_Nodes) + return; + + // This type info currently only holds function ids for use with inline call + // frame info. All functions are assigned a simple 'void ()' type. Emit that + // type here. + TypeIndex ArgListIdx = getNextTypeIndex(); + Asm->EmitInt16(2 + sizeof(ArgList)); + Asm->EmitInt16(LF_ARGLIST); + Asm->EmitInt32(0); + + TypeIndex VoidProcIdx = getNextTypeIndex(); + Asm->EmitInt16(2 + sizeof(ProcedureType)); + Asm->EmitInt16(LF_PROCEDURE); + ProcedureType Proc{}; // Zero initialize. + Proc.ReturnType = TypeIndex::Void(); + Proc.CallConv = CallingConvention::NearC; + Proc.Options = FunctionOptions::None; + Proc.NumParameters = 0; + Proc.ArgListType = ArgListIdx; + emitRecord(*Asm->OutStreamer, Proc); + + for (MDNode *N : CU_Nodes->operands()) { + auto *CUNode = cast(N); + for (auto *SP : CUNode->getSubprograms()) { + StringRef DisplayName = SP->getDisplayName(); + Asm->EmitInt16(2 + sizeof(FuncId) + DisplayName.size() + 1); + Asm->EmitInt16(LF_FUNC_ID); + + FuncId Func{}; // Zero initialize. + Func.ParentScope = TypeIndex(); + Func.FunctionType = VoidProcIdx; + emitRecord(*Asm->OutStreamer, Func); + Asm->OutStreamer->EmitBytes(DisplayName); + Asm->EmitInt8(0); + + TypeIndex FuncIdIdx = getNextTypeIndex(); + SubprogramToFuncId.insert(std::make_pair(SP, FuncIdIdx)); + } + } +} + static void EmitLabelDiff(MCStreamer &Streamer, const MCSymbol *From, const MCSymbol *To, unsigned int Size = 4) { @@ -179,6 +269,44 @@ static void EmitLabelDiff(MCStreamer &Streamer, Streamer.EmitValue(AddrDelta, Size); } +void CodeViewDebug::emitInlinedCallSite(const FunctionInfo &FI, + const DILocation *InlinedAt, + const InlineSite &Site) { + MCStreamer &OS = *Asm->OutStreamer; + + MCSymbol *InlineBegin = Asm->MMI->getContext().createTempSymbol(), + *InlineEnd = Asm->MMI->getContext().createTempSymbol(); + + assert(SubprogramToFuncId.count(Site.Inlinee)); + TypeIndex InlineeIdx = SubprogramToFuncId[Site.Inlinee]; + + // SymbolRecord + EmitLabelDiff(OS, InlineBegin, InlineEnd, 2); // RecordLength + OS.EmitLabel(InlineBegin); + Asm->EmitInt16(SymbolRecordKind::S_INLINESITE); // RecordKind + + InlineSiteSym SiteBytes{}; + SiteBytes.Inlinee = InlineeIdx; + Asm->OutStreamer->EmitBytes( + StringRef(reinterpret_cast(&SiteBytes), sizeof(SiteBytes))); + + // FIXME: annotations + + OS.EmitLabel(InlineEnd); + + // Recurse on child inlined call sites before closing the scope. + for (const DILocation *ChildSite : Site.ChildSites) { + auto I = FI.InlineSites.find(ChildSite); + assert(I != FI.InlineSites.end() && + "child site not in function inline site map"); + emitInlinedCallSite(FI, ChildSite, I->second); + } + + // Close the scope. + Asm->EmitInt16(2); // RecordLength + Asm->EmitInt16(SymbolRecordKind::S_INLINESITE_END); // RecordKind +} + void CodeViewDebug::emitDebugInfoForFunction(const Function *GV, FunctionInfo &FI) { // For each function there is a separate subsection @@ -224,6 +352,15 @@ void CodeViewDebug::emitDebugInfoForFunction(const Function *GV, Asm->EmitInt8(0); Asm->OutStreamer->EmitLabel(ProcSegmentEnd); + // Emit inlined call site information. Only emit functions inlined directly + // into the parent function. We'll emit the other sites recursively as part + // of their parent inline site. + for (auto &KV : FI.InlineSites) { + const DILocation *InlinedAt = KV.first; + if (!InlinedAt->getInlinedAt()) + emitInlinedCallSite(FI, InlinedAt, KV.second); + } + // We're done with this function. Asm->EmitInt16(0x0002); Asm->EmitInt16(unsigned(SymbolRecordKind::S_PROC_ID_END)); diff --git a/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/lib/CodeGen/AsmPrinter/CodeViewDebug.h index 77b5ed39c1f..4883905e907 100644 --- a/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ b/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -22,6 +22,7 @@ #include "llvm/CodeGen/LexicalScopes.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugLoc.h" #include "llvm/MC/MCStreamer.h" @@ -33,19 +34,31 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public AsmPrinterHandler { AsmPrinter *Asm; DebugLoc PrevInstLoc; + struct InlineSite { + TinyPtrVector ChildSites; + const DISubprogram *Inlinee = nullptr; + unsigned SiteFuncId = 0; + }; + // For each function, store a vector of labels to its instructions, as well as // to the end of the function. struct FunctionInfo { + /// Map from inlined call site to inlined instructions and child inlined + /// call sites. Listed in program order. + MapVector InlineSites; + DebugLoc LastLoc; MCSymbol *End = nullptr; unsigned FuncId = 0; - unsigned LastFileId; + unsigned LastFileId = 0; bool HaveLineInfo = false; }; FunctionInfo *CurFn; unsigned NextFuncId = 0; + InlineSite &getInlineSite(const DILocation *Loc); + /// Remember some debug info about each function. Keep it in a stable order to /// emit at the end of the TU. MapVector FnDebugInfo; @@ -53,6 +66,16 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public AsmPrinterHandler { /// Map from DIFile to .cv_file id. DenseMap FileIdMap; + DenseMap SubprogramToFuncId; + + unsigned TypeCount = 0; + + /// Gets the next type index and increments the count of types streamed so + /// far. + codeview::TypeIndex getNextTypeIndex() { + return codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex + TypeCount++); + } + typedef std::map FileToFilepathMapTy; FileToFilepathMapTy FileToFilepathMap; StringRef getFullFilepath(const DIFile *S); @@ -68,8 +91,13 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public AsmPrinterHandler { FileToFilepathMap.clear(); } + void emitTypeInformation(); + void emitDebugInfoForFunction(const Function *GV, FunctionInfo &FI); + void emitInlinedCallSite(const FunctionInfo &FI, const DILocation *InlinedAt, + const InlineSite &Site); + public: CodeViewDebug(AsmPrinter *Asm); diff --git a/lib/MC/MCObjectFileInfo.cpp b/lib/MC/MCObjectFileInfo.cpp index 0c86fa81c1b..05eadeb44a5 100644 --- a/lib/MC/MCObjectFileInfo.cpp +++ b/lib/MC/MCObjectFileInfo.cpp @@ -191,6 +191,7 @@ void MCObjectFileInfo::initMachOMCObjectFileInfo(Triple T) { SectionKind::getReadOnlyWithRel()); COFFDebugSymbolsSection = nullptr; + COFFDebugTypesSection = nullptr; if (useCompactUnwind(T)) { CompactUnwindSection = @@ -484,6 +485,7 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(Triple T) { ELF::SHF_ALLOC); COFFDebugSymbolsSection = nullptr; + COFFDebugTypesSection = nullptr; // Debug Info Sections. DwarfAbbrevSection = Ctx->getELFSection(".debug_abbrev", ELF::SHT_PROGBITS, 0, @@ -623,9 +625,14 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(Triple T) { // Debug info. COFFDebugSymbolsSection = - Ctx->getCOFFSection(".debug$S", COFF::IMAGE_SCN_MEM_DISCARDABLE | - COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | - COFF::IMAGE_SCN_MEM_READ, + Ctx->getCOFFSection(".debug$S", (COFF::IMAGE_SCN_MEM_DISCARDABLE | + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ), + SectionKind::getMetadata()); + COFFDebugTypesSection = + Ctx->getCOFFSection(".debug$T", (COFF::IMAGE_SCN_MEM_DISCARDABLE | + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ), SectionKind::getMetadata()); DwarfAbbrevSection = Ctx->getCOFFSection( diff --git a/test/DebugInfo/COFF/inlining.ll b/test/DebugInfo/COFF/inlining.ll new file mode 100644 index 00000000000..08e26c789a2 --- /dev/null +++ b/test/DebugInfo/COFF/inlining.ll @@ -0,0 +1,160 @@ +; RUN: llc -mcpu=core2 -mtriple=i686-pc-win32 < %s | FileCheck %s --check-prefix=ASM +; RUN: llc -mcpu=core2 -mtriple=i686-pc-win32 < %s -filetype=obj | llvm-readobj -codeview | FileCheck %s --check-prefix=OBJ + +; This LL file was generated by running 'clang -O1 -g -gcodeview' on the +; following code: +; 1: extern volatile int x; +; 2: static inline void foo() { +; 3: int y = 1; +; 4: x += (int)&y; +; 5: x += 2; +; 6: x += 3; +; 7: } +; 8: static inline void bar() { +; 9: x += 4; +; 10: foo(); +; 11: x += 5; +; 12: } +; 13: void baz() { +; 14: x += 6; +; 15: bar(); +; 16: x += 7; +; 17: } + +; ASM: .cv_loc 0 1 13 0 is_stmt 0 # t.cpp:13:0 +; ASM: .cv_loc 0 1 14 5 # t.cpp:14:5 +; ASM: addl $6, "?x@@3HC" +; ASM: .cv_loc 1 1 9 5 # t.cpp:9:5 +; ASM: addl $4, "?x@@3HC" +; ASM: .cv_loc 2 1 3 7 # t.cpp:3:7 +; ASM: .cv_loc 2 1 4 5 # t.cpp:4:5 +; ASM: addl {{.*}}, "?x@@3HC" +; ASM: .cv_loc 2 1 5 5 # t.cpp:5:5 +; ASM: addl $2, "?x@@3HC" +; ASM: .cv_loc 2 1 6 5 # t.cpp:6:5 +; ASM: addl $3, "?x@@3HC" +; ASM: .cv_loc 1 1 11 5 # t.cpp:11:5 +; ASM: addl $5, "?x@@3HC" +; ASM: .cv_loc 0 1 16 5 # t.cpp:16:5 +; ASM: addl $7, "?x@@3HC" +; ASM: .cv_loc 0 1 17 1 # t.cpp:17:1 + +; OBJ: ProcStart { +; OBJ: PtrParent: 0x0 +; OBJ: PtrEnd: 0x0 +; OBJ: PtrNext: 0x0 +; OBJ: CodeSize: 0x3D +; OBJ: DbgStart: 0x0 +; OBJ: DbgEnd: 0x0 +; OBJ: FunctionType: 0x0 +; OBJ: CodeOffset: ?baz@@YAXXZ+0x0 +; OBJ: Segment: 0x0 +; OBJ: Flags [ (0x0) +; OBJ: ] +; OBJ: DisplayName: baz +; OBJ: LinkageName: ?baz@@YAXXZ +; OBJ: } +; OBJ: InlineSite { +; OBJ: PtrParent: 0x0 +; OBJ: PtrEnd: 0x0 +; OBJ: Inlinee: bar (0x1003) +; OBJ: BinaryAnnotations [ +; OBJ: ] +; OBJ: } +; OBJ: InlineSite { +; OBJ: PtrParent: 0x0 +; OBJ: PtrEnd: 0x0 +; OBJ: Inlinee: foo (0x1004) +; OBJ: BinaryAnnotations [ +; OBJ: ] +; OBJ: } +; OBJ: InlineSiteEnd { +; OBJ: } +; OBJ: InlineSiteEnd { +; OBJ: } +; OBJ: ProcEnd + +; ModuleID = 't.cpp' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc18.0.0" + +@"\01?x@@3HC" = external global i32, align 4 + +; Function Attrs: norecurse nounwind uwtable +define void @"\01?baz@@YAXXZ"() #0 !dbg !4 { +entry: + %y.i.i = alloca i32, align 4 + %0 = load volatile i32, i32* @"\01?x@@3HC", align 4, !dbg !12, !tbaa !13 + %add = add nsw i32 %0, 6, !dbg !12 + store volatile i32 %add, i32* @"\01?x@@3HC", align 4, !dbg !12, !tbaa !13 + %1 = load volatile i32, i32* @"\01?x@@3HC", align 4, !dbg !17, !tbaa !13 + %add.i = add nsw i32 %1, 4, !dbg !17 + store volatile i32 %add.i, i32* @"\01?x@@3HC", align 4, !dbg !17, !tbaa !13 + %2 = bitcast i32* %y.i.i to i8*, !dbg !19 + call void @llvm.lifetime.start(i64 4, i8* %2) #2, !dbg !19 + store i32 1, i32* %y.i.i, align 4, !dbg !21, !tbaa !13 + %3 = ptrtoint i32* %y.i.i to i64, !dbg !22 + %4 = trunc i64 %3 to i32, !dbg !22 + %5 = load volatile i32, i32* @"\01?x@@3HC", align 4, !dbg !23, !tbaa !13 + %add.i.i = add nsw i32 %5, %4, !dbg !23 + store volatile i32 %add.i.i, i32* @"\01?x@@3HC", align 4, !dbg !23, !tbaa !13 + %6 = load volatile i32, i32* @"\01?x@@3HC", align 4, !dbg !24, !tbaa !13 + %add1.i.i = add nsw i32 %6, 2, !dbg !24 + store volatile i32 %add1.i.i, i32* @"\01?x@@3HC", align 4, !dbg !24, !tbaa !13 + %7 = load volatile i32, i32* @"\01?x@@3HC", align 4, !dbg !25, !tbaa !13 + %add2.i.i = add nsw i32 %7, 3, !dbg !25 + store volatile i32 %add2.i.i, i32* @"\01?x@@3HC", align 4, !dbg !25, !tbaa !13 + call void @llvm.lifetime.end(i64 4, i8* %2) #2, !dbg !26 + %8 = load volatile i32, i32* @"\01?x@@3HC", align 4, !dbg !27, !tbaa !13 + %add1.i = add nsw i32 %8, 5, !dbg !27 + store volatile i32 %add1.i, i32* @"\01?x@@3HC", align 4, !dbg !27, !tbaa !13 + %9 = load volatile i32, i32* @"\01?x@@3HC", align 4, !dbg !28, !tbaa !13 + %add1 = add nsw i32 %9, 7, !dbg !28 + store volatile i32 %add1, i32* @"\01?x@@3HC", align 4, !dbg !28, !tbaa !13 + ret void, !dbg !29 +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.start(i64, i8* nocapture) #1 + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.end(i64, i8* nocapture) #1 + +attributes #0 = { norecurse nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind } +attributes #2 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.9.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: 2, enums: !2, subprograms: !3) +!1 = !DIFile(filename: "t.cpp", directory: "D:\5Csrc\5Cllvm\5Cbuild") +!2 = !{} +!3 = !{!4, !6, !7} +!4 = distinct !DISubprogram(name: "baz", scope: !1, file: !1, line: 13, type: !5, isLocal: false, isDefinition: true, scopeLine: 13, flags: DIFlagPrototyped, isOptimized: true, variables: !2) +!5 = !DISubroutineType(types: !2) +!6 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 8, type: !5, isLocal: true, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped, isOptimized: true, variables: !2) +!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !5, isLocal: true, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, variables: !2) +!8 = !{i32 2, !"CodeView", i32 1} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"PIC Level", i32 2} +!11 = !{!"clang version 3.9.0 "} +!12 = !DILocation(line: 14, column: 5, scope: !4) +!13 = !{!14, !14, i64 0} +!14 = !{!"int", !15, i64 0} +!15 = !{!"omnipotent char", !16, i64 0} +!16 = !{!"Simple C/C++ TBAA"} +!17 = !DILocation(line: 9, column: 5, scope: !6, inlinedAt: !18) +!18 = distinct !DILocation(line: 15, column: 3, scope: !4) +!19 = !DILocation(line: 3, column: 3, scope: !7, inlinedAt: !20) +!20 = distinct !DILocation(line: 10, column: 3, scope: !6, inlinedAt: !18) +!21 = !DILocation(line: 3, column: 7, scope: !7, inlinedAt: !20) +!22 = !DILocation(line: 4, column: 8, scope: !7, inlinedAt: !20) +!23 = !DILocation(line: 4, column: 5, scope: !7, inlinedAt: !20) +!24 = !DILocation(line: 5, column: 5, scope: !7, inlinedAt: !20) +!25 = !DILocation(line: 6, column: 5, scope: !7, inlinedAt: !20) +!26 = !DILocation(line: 7, column: 1, scope: !7, inlinedAt: !20) +!27 = !DILocation(line: 11, column: 5, scope: !6, inlinedAt: !18) +!28 = !DILocation(line: 16, column: 5, scope: !4) +!29 = !DILocation(line: 17, column: 1, scope: !4)