From: Eric Christopher Date: Fri, 26 Jul 2013 17:02:41 +0000 (+0000) Subject: Add preliminary support for hashing DIEs and breaking them into X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=3dee575b8dfe9c3d89f581e51dd11ddd08c43cd6;p=oota-llvm.git Add preliminary support for hashing DIEs and breaking them into type units. Initially this support is used in the computation of an ODR checker for C++. For now we're attaching it to the DIE, but in the future it will be attached to the type unit. This also starts breaking out types into the separation for type units, but without actually splitting the DIEs. In preparation for hashing the DIEs this adds a DIEString type that contains a StringRef with the string contained at the label. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@187213 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/AsmPrinter/DIE.cpp b/lib/CodeGen/AsmPrinter/DIE.cpp index d6caef7e5b8..0b154123315 100644 --- a/lib/CodeGen/AsmPrinter/DIE.cpp +++ b/lib/CodeGen/AsmPrinter/DIE.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/MD5.h" using namespace llvm; //===----------------------------------------------------------------------===// @@ -322,6 +323,29 @@ void DIEDelta::print(raw_ostream &O) const { } #endif +//===----------------------------------------------------------------------===// +// DIEString Implementation +//===----------------------------------------------------------------------===// + +/// EmitValue - Emit string value. +/// +void DIEString::EmitValue(AsmPrinter *AP, unsigned Form) const { + Access->EmitValue(AP, Form); +} + +/// SizeOf - Determine size of delta value in bytes. +/// +unsigned DIEString::SizeOf(AsmPrinter *AP, unsigned Form) const { + return Access->SizeOf(AP, Form); +} + +#ifndef NDEBUG +void DIEString::print(raw_ostream &O) const { + O << "String: " << Str << "\tSymbol: "; + Access->print(O); +} +#endif + //===----------------------------------------------------------------------===// // DIEEntry Implementation //===----------------------------------------------------------------------===// diff --git a/lib/CodeGen/AsmPrinter/DIE.h b/lib/CodeGen/AsmPrinter/DIE.h index 3b04e206254..e2f49d64bee 100644 --- a/lib/CodeGen/AsmPrinter/DIE.h +++ b/lib/CodeGen/AsmPrinter/DIE.h @@ -345,6 +345,36 @@ namespace llvm { #endif }; + //===--------------------------------------------------------------------===// + /// DIEString - A container for string values. + /// + class DIEString : public DIEValue { + const DIEValue *Access; + const StringRef Str; + + public: + DIEString(const DIEValue *Acc, const StringRef S) + : DIEValue(isString), Access(Acc), Str(S) {} + + /// getString - Grab the string out of the object. + StringRef getString() const { return Str; } + + /// EmitValue - Emit delta value. + /// + virtual void EmitValue(AsmPrinter *AP, unsigned Form) const; + + /// SizeOf - Determine size of delta value in bytes. + /// + virtual unsigned SizeOf(AsmPrinter *AP, unsigned Form) const; + + // Implement isa/cast/dyncast. + static bool classof(const DIEValue *D) { return D->getType() == isString; } + + #ifndef NDEBUG + virtual void print(raw_ostream &O) const; + #endif + }; + //===--------------------------------------------------------------------===// /// DIEEntry - A pointer to another debug information entry. An instance of /// this class can also be used as a proxy for a debug information entry not diff --git a/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index 37eedc27e5a..e912076a65b 100644 --- a/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -132,21 +132,24 @@ void CompileUnit::addSInt(DIE *Die, unsigned Attribute, /// into another table which gets us the static offset into the string /// table. void CompileUnit::addString(DIE *Die, unsigned Attribute, StringRef String) { + DIEValue *Value; + unsigned Form; if (!DD->useSplitDwarf()) { MCSymbol *Symb = DU->getStringPoolEntry(String); - DIEValue *Value; if (Asm->needsRelocationsForDwarfStringPool()) Value = new (DIEValueAllocator) DIELabel(Symb); else { MCSymbol *StringPool = DU->getStringPoolSym(); Value = new (DIEValueAllocator) DIEDelta(Symb, StringPool); } - Die->addValue(Attribute, dwarf::DW_FORM_strp, Value); + Form = dwarf::DW_FORM_strp; } else { unsigned idx = DU->getStringPoolIndex(String); - DIEValue *Value = new (DIEValueAllocator) DIEInteger(idx); - Die->addValue(Attribute, dwarf::DW_FORM_GNU_str_index, Value); + Value = new (DIEValueAllocator) DIEInteger(idx); + Form = dwarf::DW_FORM_GNU_str_index; } + DIEValue *Str = new (DIEValueAllocator) DIEString(Value, String); + Die->addValue(Attribute, Form, Str); } /// addLocalString - Add a string attribute data and value. This is guaranteed @@ -878,6 +881,39 @@ void CompileUnit::constructTypeDIE(DIE &Buffer, DIDerivedType DTy) { addSourceLine(&Buffer, DTy); } +/// Return true if the type is appropriately scoped to be contained inside +/// its own type unit. +static bool isTypeUnitScoped(DIType Ty) { + DIScope Parent = Ty.getContext(); + while (Parent) { + // Don't generate a hash for anything scoped inside a function. + if (Parent.isSubprogram()) + return false; + Parent = Parent.getContext(); + } + return true; +} + +/// Return true if the type should be split out into a type unit. +static bool shouldCreateTypeUnit(DICompositeType CTy) { + unsigned Tag = CTy.getTag(); + + switch (Tag) { + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_class_type: + // If this is a class, structure, union, or enumeration type + // that is not a declaration, is a type definition, and not scoped + // inside a function then separate this out as a type unit. + if (CTy.isForwardDecl() || !isTypeUnitScoped(CTy)) + return 0; + return 1; + default: + return 0; + } +} + /// constructTypeDIE - Construct type DIE from DICompositeType. void CompileUnit::constructTypeDIE(DIE &Buffer, DICompositeType CTy) { // Get core information. @@ -1075,6 +1111,10 @@ void CompileUnit::constructTypeDIE(DIE &Buffer, DICompositeType CTy) { addUInt(&Buffer, dwarf::DW_AT_APPLE_runtime_class, dwarf::DW_FORM_data1, RLang); } + // If this is a type applicable to a type unit it then add it to the + // list of types we'll compute a hash for later. + if (shouldCreateTypeUnit(CTy)) + DD->addTypeUnitType(&Buffer); } /// getOrCreateTemplateTypeParameterDIE - Find existing DIE or create new DIE diff --git a/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 8dc44b7fdc4..fae1ece086f 100644 --- a/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/MD5.h" #include "llvm/Support/Path.h" #include "llvm/Support/Timer.h" #include "llvm/Support/ValueHandle.h" @@ -60,6 +61,11 @@ GenerateDwarfPubNamesSection("generate-dwarf-pubnames", cl::Hidden, cl::init(false), cl::desc("Generate DWARF pubnames section")); +static cl::opt +GenerateODRHash("generate-odr-hash", cl::Hidden, + cl::desc("Add an ODR hash to external type DIEs."), + cl::init(false)); + namespace { enum DefaultOnOff { Default, @@ -956,6 +962,135 @@ void DwarfDebug::collectDeadVariables() { DeleteContainerSeconds(DeadFnScopeMap); } +// Type Signature computation code. +typedef ArrayRef HashValue; + +/// \brief Grabs the string in whichever attribute is passed in and returns +/// a reference to it. +static StringRef getDIEStringAttr(DIE *Die, unsigned Attr) { + const SmallVectorImpl &Values = Die->getValues(); + const DIEAbbrev &Abbrevs = Die->getAbbrev(); + + // Iterate through all the attributes until we find the one we're + // looking for, if we can't find it return an empty string. + for (size_t i = 0; i < Values.size(); ++i) { + if (Abbrevs.getData()[i].getAttribute() == Attr) { + DIEValue *V = Values[i]; + assert(isa(V) && "String requested. Not a string."); + DIEString *S = cast(V); + return S->getString(); + } + } + return StringRef(""); +} + +/// \brief Adds the string in \p Str to the hash in \p Hash. This also hashes +/// a trailing NULL with the string. +static void addStringToHash(MD5 &Hash, StringRef Str) { + DEBUG(dbgs() << "Adding string " << Str << " to hash.\n"); + HashValue SVal((const uint8_t *)Str.data(), Str.size()); + const uint8_t NB = '\0'; + HashValue NBVal((const uint8_t *)&NB, 1); + Hash.update(SVal); + Hash.update(NBVal); +} + +/// \brief Adds the character string in \p Str to the hash in \p Hash. This does +/// not hash a trailing NULL on the character. +static void addLetterToHash(MD5 &Hash, StringRef Str) { + DEBUG(dbgs() << "Adding letter " << Str << " to hash.\n"); + assert(Str.size() == 1 && "Trying to add a too large letter?"); + HashValue SVal((const uint8_t *)Str.data(), Str.size()); + Hash.update(SVal); +} + +// FIXME: These are copied and only slightly modified out of LEB128.h. + +/// \brief Adds the unsigned in \p N to the hash in \p Hash. This also encodes +/// the unsigned as a ULEB128. +static void addULEB128ToHash(MD5 &Hash, uint64_t Value) { + DEBUG(dbgs() << "Adding ULEB128 " << Value << " to hash.\n"); + do { + uint8_t Byte = Value & 0x7f; + Value >>= 7; + if (Value != 0) + Byte |= 0x80; // Mark this byte to show that more bytes will follow. + Hash.update(Byte); + } while (Value != 0); +} + +/// \brief Including \p Parent adds the context of Parent to \p Hash. +static void addParentContextToHash(MD5 &Hash, DIE *Parent) { + unsigned Tag = Parent->getTag(); + + DEBUG(dbgs() << "Adding parent context to hash...\n"); + + // For each surrounding type or namespace... + if (Tag != dwarf::DW_TAG_namespace && Tag != dwarf::DW_TAG_class_type && + Tag != dwarf::DW_TAG_structure_type) + return; + + // ... beginning with the outermost such construct... + if (Parent->getParent() != NULL) + addParentContextToHash(Hash, Parent->getParent()); + + // Append the letter "C" to the sequence. + addLetterToHash(Hash, "C"); + + // Followed by the DWARF tag of the construct. + addULEB128ToHash(Hash, Parent->getTag()); + + // Then the name, taken from the DW_AT_name attribute. + StringRef Name = getDIEStringAttr(Parent, dwarf::DW_AT_name); + if (!Name.empty()) + addStringToHash(Hash, Name); +} + +/// This is based on the type signature computation given in section 7.27 of the +/// DWARF4 standard. It is the md5 hash of a flattened description of the DIE. +static void addDIEODRSignature(MD5 &Hash, CompileUnit *CU, DIE *Die) { + + // Add the contexts to the hash. + DIE *Parent = Die->getParent(); + if (Parent) + addParentContextToHash(Hash, Parent); + + // Add the current DIE information. + + // Add the DWARF tag of the DIE. + addULEB128ToHash(Hash, Die->getTag()); + + // Add the name of the type to the hash. + addStringToHash(Hash, getDIEStringAttr(Die, dwarf::DW_AT_name)); + + // Now get the result. + MD5::MD5Result Result; + Hash.final(Result); + + // ... take the least significant 8 bytes and store those as the attribute. + uint64_t Signature; + memcpy(&Signature, &Result[8], 8); + + // FIXME: This should be added onto the type unit, not the type, but this + // works as an intermediate stage. + CU->addUInt(Die, dwarf::DW_AT_GNU_odr_signature, dwarf::DW_FORM_data8, + Signature); +} + +/// Return true if the current DIE is contained within an anonymous namespace. +static bool isContainedInAnonNamespace(DIE *Die) { + DIE *Parent = Die->getParent(); + + while (Parent) { + if (Die->getTag() == dwarf::DW_TAG_namespace && + getDIEStringAttr(Die, dwarf::DW_AT_name) == "") + return true; + Parent = Parent->getParent(); + } + + return false; +} + void DwarfDebug::finalizeModuleInfo() { // Collect info for variables that were optimized out. collectDeadVariables(); @@ -971,6 +1106,20 @@ void DwarfDebug::finalizeModuleInfo() { TheCU->constructContainingTypeDIEs(); } + // For types that we'd like to move to type units or ODR check go ahead + // and either move the types out or add the ODR attribute now. + // FIXME: Do type splitting. + for (unsigned i = 0, e = TypeUnits.size(); i != e; ++i) { + MD5 Hash; + DIE *Die = TypeUnits[i]; + // If we're in C++ and we want to generate the hash then go ahead and do + // that now. + if (GenerateODRHash && + CUMap.begin()->second->getLanguage() == dwarf::DW_LANG_C_plus_plus && + !isContainedInAnonNamespace(Die)) + addDIEODRSignature(Hash, CUMap.begin()->second, Die); + } + // Compute DIE offsets and sizes. InfoHolder.computeSizeAndOffsets(); if (useSplitDwarf()) diff --git a/lib/CodeGen/AsmPrinter/DwarfDebug.h b/lib/CodeGen/AsmPrinter/DwarfDebug.h index deb2e606899..e14f9b135b3 100644 --- a/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -428,6 +428,9 @@ class DwarfDebug { ImportedEntityMap; ImportedEntityMap ScopesWithImportedEntities; + // Holder for types that are going to be extracted out into a type unit. + std::vector TypeUnits; + // DWARF5 Experimental Options bool HasDwarfAccelTables; bool HasSplitDwarf; @@ -651,6 +654,10 @@ public: /// \brief Process end of an instruction. void endInstruction(const MachineInstr *MI); + /// \brief Add a DIE to the set of types that we're going to pull into + /// type units. + void addTypeUnitType(DIE *Die) { TypeUnits.push_back(Die); } + /// \brief Look up the source id with the given directory and source file /// names. If none currently exists, create a new id and insert it in the /// SourceIds map. diff --git a/test/DebugInfo/generate-odr-hash.ll b/test/DebugInfo/generate-odr-hash.ll new file mode 100644 index 00000000000..0b35e3f9d5b --- /dev/null +++ b/test/DebugInfo/generate-odr-hash.ll @@ -0,0 +1,116 @@ +; REQUIRES: object-emission + +; RUN: llc %s -o %t -filetype=obj -O0 -generate-odr-hash +; RUN: llvm-dwarfdump -debug-dump=info %t | FileCheck %s +; +; Generated from: +; +; struct bar {}; +; struct bar b; +; void foo(void) { +; struct baz {}; +; baz b; +; } +; namespace echidna { +; namespace capybara { +; namespace mongoose { +; class fluffy { +; int a; +; int b; +; }; +; fluffy animal; +; } +; } +; } +; namespace { +; struct walrus {}; +; } +; walrus w; + +; Check that we generate a hash for bar, that it is a particular value and +; that we don't generate a hash for baz or walrus. +; CHECK: DW_TAG_structure_type +; CHECK-NEXT: debug_str{{.*}}"bar" +; CHECK: DW_AT_GNU_odr_signature [DW_FORM_data8] (0x200520c0d5b90eff) +; CHECK: DW_TAG_namespace +; CHECK-NEXT: debug_str{{.*}}"echidna" +; CHECK: DW_TAG_namespace +; CHECK-NEXT: debug_str{{.*}}"capybara" +; CHECK: DW_TAG_namespace +; CHECK-NEXT: debug_str{{.*}}"mongoose" +; CHECK: DW_TAG_class_type +; CHECK-NEXT: debug_str{{.*}}"fluffy" +; CHECK: DW_AT_GNU_odr_signature [DW_FORM_data8] (0x9a0124d5a0c21c52) +; CHECK: DW_TAG_structure_type +; CHECK-NEXT: debug_str{{.*}}"baz" +; CHECK-NOT: DW_AT_GNU_odr_signature +; FIXME: we may want to generate debug info for walrus, but still no hash. +; CHECK-NOT: debug_str{{.*}}"walrus" + + +%struct.bar = type { i8 } +%"class.echidna::capybara::mongoose::fluffy" = type { i32, i32 } +%struct.baz = type { i8 } + +@b = global %struct.bar zeroinitializer, align 1 +@_ZN7echidna8capybara8mongoose6animalE = global %"class.echidna::capybara::mongoose::fluffy" zeroinitializer, align 4 + +; Function Attrs: nounwind uwtable +define void @_Z3foov() #0 { +entry: + %b = alloca %struct.baz, align 1 + call void @llvm.dbg.declare(metadata !{%struct.baz* %b}, metadata !32), !dbg !40 + ret void, !dbg !41 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata) #1 + +attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!31} + +!0 = metadata !{i32 786449, metadata !1, i32 4, metadata !"clang version 3.4 (trunk 187152) (llvm/trunk 187150)", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !8, metadata !2, metadata !""} ; [ DW_TAG_compile_unit ] [/usr/local/google/home/echristo/tmp/bar.cpp] [DW_LANG_C_plus_plus] +!1 = metadata !{metadata !"bar.cpp", metadata !"/usr/local/google/home/echristo/tmp"} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !1, metadata !5, metadata !"foo", metadata !"foo", metadata !"_Z3foov", i32 6, metadata !6, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, void ()* @_Z3foov, null, null, metadata !2, i32 6} ; [ DW_TAG_subprogram ] [line 6] [def] [foo] +!5 = metadata !{i32 786473, metadata !1} ; [ DW_TAG_file_type ] [/usr/local/google/home/echristo/tmp/bar.cpp] +!6 = metadata !{i32 786453, i32 0, i32 0, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !7, i32 0, i32 0} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!7 = metadata !{null} +!8 = metadata !{metadata !9, metadata !17} +!9 = metadata !{i32 786484, i32 0, null, metadata !"b", metadata !"b", metadata !"", metadata !5, i32 4, metadata !10, i32 0, i32 1, %struct.bar* @b, null} ; [ DW_TAG_variable ] [b] [line 4] [def] +!10 = metadata !{i32 786451, metadata !1, null, metadata !"bar", i32 1, i64 8, i64 8, i32 0, i32 0, null, metadata !11, i32 0, null, null} ; [ DW_TAG_structure_type ] [bar] [line 1, size 8, align 8, offset 0] [def] [from ] +!11 = metadata !{metadata !12} +!12 = metadata !{i32 786478, metadata !1, metadata !10, metadata !"bar", metadata !"bar", metadata !"", i32 1, metadata !13, i1 false, i1 false, i32 0, i32 0, null, i32 320, i1 false, null, null, i32 0, metadata !16, i32 1} ; [ DW_TAG_subprogram ] [line 1] [bar] +!13 = metadata !{i32 786453, i32 0, i32 0, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !14, i32 0, i32 0} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!14 = metadata !{null, metadata !15} +!15 = metadata !{i32 786447, i32 0, i32 0, metadata !"", i32 0, i64 64, i64 64, i64 0, i32 1088, metadata !10} ; [ DW_TAG_pointer_type ] [line 0, size 64, align 64, offset 0] [artificial] [from bar] +!16 = metadata !{i32 786468} +!17 = metadata !{i32 786484, i32 0, metadata !18, metadata !"animal", metadata !"animal", metadata !"_ZN7echidna8capybara8mongoose6animalE", metadata !5, i32 20, metadata !21, i32 0, i32 1, %"class.echidna::capybara::mongoose::fluffy"* @_ZN7echidna8capybara8mongoose6animalE, null} ; [ DW_TAG_variable ] [animal] [line 20] [def] +!18 = metadata !{i32 786489, metadata !1, metadata !19, metadata !"mongoose", i32 14} ; [ DW_TAG_namespace ] [mongoose] [line 14] +!19 = metadata !{i32 786489, metadata !1, metadata !20, metadata !"capybara", i32 13} ; [ DW_TAG_namespace ] [capybara] [line 13] +!20 = metadata !{i32 786489, metadata !1, null, metadata !"echidna", i32 12} ; [ DW_TAG_namespace ] [echidna] [line 12] +!21 = metadata !{i32 786434, metadata !1, metadata !18, metadata !"fluffy", i32 15, i64 64, i64 32, i32 0, i32 0, null, metadata !22, i32 0, null, null} ; [ DW_TAG_class_type ] [fluffy] [line 15, size 64, align 32, offset 0] [def] [from ] +!22 = metadata !{metadata !23, metadata !25, metadata !26} +!23 = metadata !{i32 786445, metadata !1, metadata !21, metadata !"a", i32 16, i64 32, i64 32, i64 0, i32 1, metadata !24} ; [ DW_TAG_member ] [a] [line 16, size 32, align 32, offset 0] [private] [from int] +!24 = 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] +!25 = metadata !{i32 786445, metadata !1, metadata !21, metadata !"b", i32 17, i64 32, i64 32, i64 32, i32 1, metadata !24} ; [ DW_TAG_member ] [b] [line 17, size 32, align 32, offset 32] [private] [from int] +!26 = metadata !{i32 786478, metadata !1, metadata !21, metadata !"fluffy", metadata !"fluffy", metadata !"", i32 15, metadata !27, i1 false, i1 false, i32 0, i32 0, null, i32 320, i1 false, null, null, i32 0, metadata !30, i32 15} ; [ DW_TAG_subprogram ] [line 15] [fluffy] +!27 = metadata !{i32 786453, i32 0, i32 0, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !28, i32 0, i32 0} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!28 = metadata !{null, metadata !29} +!29 = metadata !{i32 786447, i32 0, i32 0, metadata !"", i32 0, i64 64, i64 64, i64 0, i32 1088, metadata !21} ; [ DW_TAG_pointer_type ] [line 0, size 64, align 64, offset 0] [artificial] [from fluffy] +!30 = metadata !{i32 786468} +!31 = metadata !{i32 2, metadata !"Dwarf Version", i32 3} +!32 = metadata !{i32 786688, metadata !4, metadata !"b", metadata !5, i32 9, metadata !33, i32 0, i32 0} ; [ DW_TAG_auto_variable ] [b] [line 9] +!33 = metadata !{i32 786451, metadata !1, metadata !4, metadata !"baz", i32 7, i64 8, i64 8, i32 0, i32 0, null, metadata !34, i32 0, null, null} ; [ DW_TAG_structure_type ] [baz] [line 7, size 8, align 8, offset 0] [def] [from ] +!34 = metadata !{metadata !35} +!35 = metadata !{i32 786478, metadata !1, metadata !33, metadata !"baz", metadata !"baz", metadata !"", i32 7, metadata !36, i1 false, i1 false, i32 0, i32 0, null, i32 320, i1 false, null, null, i32 0, metadata !39, i32 7} ; [ DW_TAG_subprogram ] [line 7] [baz] +!36 = metadata !{i32 786453, i32 0, i32 0, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !37, i32 0, i32 0} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!37 = metadata !{null, metadata !38} +!38 = metadata !{i32 786447, i32 0, i32 0, metadata !"", i32 0, i64 64, i64 64, i64 0, i32 1088, metadata !33} ; [ DW_TAG_pointer_type ] [line 0, size 64, align 64, offset 0] [artificial] [from baz] +!39 = metadata !{i32 786468} +!40 = metadata !{i32 9, i32 0, metadata !4, null} +!41 = metadata !{i32 10, i32 0, metadata !4, null}