diff --git a/llvm/include/llvm/MC/CAS/MCCASObjectV1.def b/llvm/include/llvm/MC/CAS/MCCASObjectV1.def index 4c483886b913e..18a633fdc77e8 100644 --- a/llvm/include/llvm/MC/CAS/MCCASObjectV1.def +++ b/llvm/include/llvm/MC/CAS/MCCASObjectV1.def @@ -18,7 +18,6 @@ CASV1_SIMPLE_DATA_REF(DataInCodeRef, mc:data_in_code) CASV1_SIMPLE_DATA_REF(CStringRef, mc:cstring) CASV1_SIMPLE_DATA_REF(MergedFragmentRef, mc:merged_fragment) CASV1_SIMPLE_DATA_REF(DebugStrRef, mc:debug_string) -CASV1_SIMPLE_DATA_REF(DebugInfoCURef, mc:debug_info_cu) CASV1_SIMPLE_DATA_REF(DebugLineRef, mc:debug_line) CASV1_SIMPLE_DATA_REF(DebugAbbrevRef, mc:debug_abbrev) CASV1_SIMPLE_DATA_REF(DebugAbbrevOffsetsRef, mc:debug_abbrev_offsets) @@ -33,6 +32,7 @@ CASV1_SIMPLE_GROUP_REF(GroupRef, mc:group) CASV1_SIMPLE_GROUP_REF(SectionRef, mc:section) CASV1_SIMPLE_GROUP_REF(DebugAbbrevSectionRef, mc:debug_abbrev_section) CASV1_SIMPLE_GROUP_REF(DebugLineSectionRef, mc:debug_line_section) +CASV1_SIMPLE_GROUP_REF(DebugStringSectionRef, mc:debug_string_section) CASV1_SIMPLE_GROUP_REF(AtomRef, mc:atom) CASV1_SIMPLE_GROUP_REF(SymbolTableRef, mc:symbol_table) diff --git a/llvm/include/llvm/MC/CAS/MCCASObjectV1.h b/llvm/include/llvm/MC/CAS/MCCASObjectV1.h index b391a02659c77..1e05bcbc79fea 100644 --- a/llvm/include/llvm/MC/CAS/MCCASObjectV1.h +++ b/llvm/include/llvm/MC/CAS/MCCASObjectV1.h @@ -145,6 +145,13 @@ class MCObjectProxy : public cas::ObjectProxy { MCObjectProxy() = delete; + static Error encodeReferences(ArrayRef Refs, + SmallVectorImpl &Data, + SmallVectorImpl &IDs); + + static Expected> + decodeReferences(const MCObjectProxy &Node, StringRef &Remaining); + protected: MCObjectProxy(const MCSchema &Schema, const cas::ObjectProxy &Node) : cas::ObjectProxy(Node), Schema(&Schema) {} @@ -402,6 +409,34 @@ class MCAssemblerRef : public SpecificRef { MCAssemblerRef(SpecificRefT Ref) : SpecificRefT(Ref) {} }; +class DebugInfoCURef : public SpecificRef { + using SpecificRefT = SpecificRef; + friend class SpecificRef; + +public: + static constexpr StringLiteral KindString = "mc:debug_info_cu"; + static Expected create(MCCASBuilder &MB, StringRef Data, + ArrayRef Refs); + static Expected get(Expected Ref); + static Expected get(const MCSchema &Schema, + cas::ObjectRef ID) { + return get(Schema.get(ID)); + } + static Optional Cast(MCObjectProxy Ref) { + auto Specific = SpecificRefT::Cast(Ref); + if (!Specific) + return None; + return DebugInfoCURef(*Specific); + } + Expected materialize(raw_ostream &OS) const { + OS << getData(); + return getData().size(); + } + +private: + explicit DebugInfoCURef(SpecificRefT Ref) : SpecificRefT(Ref) {} +}; + struct DwarfSectionsCache { MCSection *DebugInfo; MCSection *Line; @@ -497,6 +532,15 @@ class MCCASBuilder { return AtomAddends; } + Optional + getObjectRefFromStringMap(unsigned StringOffset) const { + auto StringRefIt = + DebugStringSectionContents::MapOfStringRefs.find(StringOffset); + if (StringRefIt != DebugStringSectionContents::MapOfStringRefs.end()) + return StringRefIt->getSecond(); + return None; + } + // Scratch space SmallString<8> FragmentData; raw_svector_ostream FragmentOS; @@ -510,7 +554,7 @@ class MCCASBuilder { // Helper functions. Error createStringSection(StringRef S, - std::function CreateFn); + std::function CreateFn); // If a DWARF Line Section exists, create a DebugLineRef CAS object per // function contribution to the line table. @@ -522,7 +566,8 @@ class MCCASBuilder { /// A pair of vectors with the CAS objects is returned. /// The CAS objects appear in the same order as in the object file. /// If the section doesn't exist, an empty container is returned. - Expected splitDebugInfoAndAbbrevSections(); + Expected + splitDebugInfoAndAbbrevSections(ArrayRef DebugStringRefs); /// If CURefs is non-empty, create a SectionRef CAS object with edges to all /// CURefs. Otherwise, no objects are created and `success` is returned. @@ -543,6 +588,13 @@ class MCCASBuilder { splitAbbrevSection(ArrayRef AbbrevOffsets, ArrayRef FullAbbrevData); + struct DebugStringSectionContents { + SmallVector DebugStringRefs; + static DenseMap MapOfStringRefs; + }; + + Expected createDebugStringRefs(); + struct CUSplit { SmallVector> SplitCUData; SmallVector AbbrevOffsets; @@ -555,7 +607,7 @@ class MCCASBuilder { // If a DWARF String section exists, create a DebugStrRef CAS object per // string in the section. - Error createDebugStrSection(); + Error createDebugStrSection(ArrayRef DebugStringRefs); /// If there is any padding between one section and the next, create a /// PaddingRef CAS object to represent the bytes of Padding between the two @@ -632,10 +684,11 @@ class DebugInfoSectionRef : public SpecificRef { return None; return DebugInfoSectionRef(*Specific); } - Expected materialize(MCCASReader &Reader, - ArrayRef AbbrevSectionContents, - ArrayRef SecOffsetVals, - raw_ostream *Stream = nullptr) const; + + Expected + materialize(MCCASReader &Reader, ArrayRef AbbrevSectionContents, ArrayRef SecOffsetVals, + DenseMap &MapOfStringOffsets, + raw_ostream *Stream = nullptr) const; private: explicit DebugInfoSectionRef(SpecificRefT Ref) : SpecificRefT(Ref) {} diff --git a/llvm/lib/MC/MCCASObjectV1.cpp b/llvm/lib/MC/MCCASObjectV1.cpp index 4d6d9107074cb..10db27401ad16 100644 --- a/llvm/lib/MC/MCCASObjectV1.cpp +++ b/llvm/lib/MC/MCCASObjectV1.cpp @@ -46,6 +46,7 @@ constexpr StringLiteral PaddingRef::KindString; constexpr StringLiteral MCFragmentName##Ref::KindString; #include "llvm/MC/CAS/MCCASObjectV1.def" constexpr StringLiteral DebugInfoSectionRef::KindString; +constexpr StringLiteral DebugInfoCURef::KindString; constexpr unsigned Dwarf4HeaderSize32Bit = 11; @@ -93,6 +94,10 @@ template <> struct llvm::DenseMapInfo { } }; +DenseMap + MCCASBuilder::DebugStringSectionContents::MapOfStringRefs = + DenseMap(); + /// A DWARFObject implementation that can be used to dwarfdump CAS-formatted /// debug info. class InMemoryCASDWARFObject : public DWARFObject { @@ -120,8 +125,8 @@ class InMemoryCASDWARFObject : public DWARFObject { struct PartitionedDebugInfoSection { SmallVector DebugInfoCURefData; SmallVector DistinctData; - constexpr static std::array FormsToPartition{ - llvm::dwarf::Form::DW_FORM_strp}; + SmallVector DebugStringRefs; + constexpr static std::array FormsToPartition{0}; }; /// Create a DwarfCompileUnit that represents the compile unit at \p CUOffset @@ -131,8 +136,8 @@ class InMemoryCASDWARFObject : public DWARFObject { /// deduplicate. Store both kinds of Forms in their own buffers per compile /// unit. Expected - partitionCUData(ArrayRef DebugInfoData, uint64_t AbbrevOffset, - uint64_t CUOffset, DWARFContext *Ctx); + partitionCUData(MCCASBuilder &Builder, ArrayRef DebugInfoData, uint64_t AbbrevOffset, + uint64_t CUOffset, DWARFContext *Ctx, ArrayRef DebugStringRefs); }; struct CUInfo { @@ -191,6 +196,7 @@ Error MCSchema::fillCache() { PaddingRef::KindString, MCAssemblerRef::KindString, DebugInfoSectionRef::KindString, + DebugInfoCURef::KindString, #define CASV1_SIMPLE_DATA_REF(RefName, IdentifierName) RefName::KindString, #define CASV1_SIMPLE_GROUP_REF(RefName, IdentifierName) RefName::KindString, #define MCFRAGMENT_NODE_REF(MCFragmentName, MCEnumName, MCEnumIdentifier) \ @@ -398,9 +404,9 @@ static Error decodeRelocationsAndAddends(MCCASReader &Reader, StringRef Data) { return Error::success(); } -static Error encodeReferences(ArrayRef Refs, - SmallVectorImpl &Data, - SmallVectorImpl &IDs) { +Error MCObjectProxy::encodeReferences(ArrayRef Refs, + SmallVectorImpl &Data, + SmallVectorImpl &IDs) { DenseMap RefMap; SmallVector CompactRefs; for (const auto &ID : Refs) { @@ -430,8 +436,9 @@ static Error encodeReferences(ArrayRef Refs, return Error::success(); } -static Expected> -decodeReferences(const MCObjectProxy &Node, StringRef &Remaining) { +Expected> +MCObjectProxy::decodeReferences(const MCObjectProxy &Node, + StringRef &Remaining) { Expected> MaybeRefs = loadReferences(Node); if (!MaybeRefs) return MaybeRefs.takeError(); @@ -460,6 +467,27 @@ decodeReferences(const MCObjectProxy &Node, StringRef &Remaining) { return CompactRefs; } +Expected DebugInfoCURef::create(MCCASBuilder &MB, + StringRef Name, + ArrayRef Refs) { + auto B = Builder::startNode(MB.Schema, KindString); + if (!B) + return B.takeError(); + + if (auto E = encodeReferences(Refs, B->Data, B->Refs)) + return std::move(E); + + B->Data.append(Name); + return get(B->build()); +} + +Expected DebugInfoCURef::get(Expected Ref) { + auto Specific = SpecificRefT::getSpecific(std::move(Ref)); + if (!Specific) + return Specific.takeError(); + return DebugInfoCURef(*Specific); +} + Expected GroupRef::create(MCCASBuilder &MB, ArrayRef Fragments) { Expected B = Builder::startNode(MB.Schema, KindString); @@ -507,7 +535,7 @@ Expected LoadedDebugAbbrevSection::load(DebugAbbrevSectionRef Section) { StringRef Remaining = Section.getData(); - auto Refs = decodeReferences(Section, Remaining); + auto Refs = MCObjectProxy::decodeReferences(Section, Remaining); if (!Refs) return Refs.takeError(); @@ -552,7 +580,7 @@ Expected LoadedDebugLineSection::load(DebugLineSectionRef Section) { StringRef Remaining = Section.getData(); - auto Refs = decodeReferences(Section, Remaining); + auto Refs = MCObjectProxy::decodeReferences(Section, Remaining); if (!Refs) return Refs.takeError(); @@ -576,9 +604,56 @@ LoadedDebugLineSection::load(DebugLineSectionRef Section) { return LoadedSection; } +struct LoadedDebugStringSection { + DenseMap MapOfStringOffsets; + + static Expected load(DebugStringSectionRef Section); + +private: + /// Extracts the information contained in \p Proxy, which must be either an + /// StrRef or a PaddingNode. + Error addNode(Expected Proxy, unsigned &StringOffset) { + if (!Proxy) + return Proxy.takeError(); + if (auto StrRef = DebugStrRef::Cast(*Proxy)) { + MapOfStringOffsets.try_emplace(StrRef->getRef(), StringOffset); + StringOffset += StrRef->getData().size() + 1; + } else + return createStringError(inconvertibleErrorCode(), + "Invalid node for Debug Info section"); + return Error::success(); + } +}; + +Expected +LoadedDebugStringSection::load(DebugStringSectionRef Section) { + + StringRef Remaining = Section.getData(); + auto Refs = MCObjectProxy::decodeReferences(Section, Remaining); + if (!Refs) + return Refs.takeError(); + + const MCSchema &Schema = Section.getSchema(); + cas::ObjectStore &CAS = Schema.CAS; + auto loadRef = [&](cas::ObjectRef Ref) { + return MCObjectProxy::get(Schema, CAS.getProxy(Ref)); + }; + + LoadedDebugStringSection LoadedSection; + // TODO: Add support for 64 bit dwarf format + unsigned StringOffset = 0; + for (auto Ref : *Refs) { + if (auto E = LoadedSection.addNode(loadRef(Ref), StringOffset)) + return std::move(E); + } + + return LoadedSection; +} + struct DebugInfoMaterializationSecs { Optional AbbrevSectionRef; Optional LineSectionRef; + Optional StringSectionRef; }; Expected @@ -592,6 +667,8 @@ findSectionRef(MCCASReader &Reader, ArrayRef Refs) { DebugRefs.AbbrevSectionRef = AbbrevSectionRef; else if (auto LineSectionRef = DebugLineSectionRef::Cast(*Node)) DebugRefs.LineSectionRef = LineSectionRef; + else if (auto StringSectionRef = DebugStringSectionRef::Cast(*Node)) + DebugRefs.StringSectionRef = StringSectionRef; } if (!DebugRefs.AbbrevSectionRef) return createStringError(inconvertibleErrorCode(), @@ -599,6 +676,9 @@ findSectionRef(MCCASReader &Reader, ArrayRef Refs) { if (!DebugRefs.LineSectionRef) return createStringError(inconvertibleErrorCode(), "Could not find a DebugLineSectionRef"); + if (!DebugRefs.StringSectionRef) + return createStringError(inconvertibleErrorCode(), + "Could not find a DebugStringSectionRef"); return DebugRefs; } @@ -616,13 +696,18 @@ materializeDebugInfoSection(MCCASReader &Reader, ArrayRef Refs, LoadedDebugAbbrevSection::load(*DebugRefs->AbbrevSectionRef); if (!LoadedAbbrev) return LoadedAbbrev.takeError(); + auto LoadedString = + LoadedDebugStringSection::load(*DebugRefs->StringSectionRef); + if (!LoadedString) + return LoadedString.takeError(); auto LoadedLine = LoadedDebugLineSection::load(*DebugRefs->LineSectionRef); if (!LoadedLine) return LoadedLine.takeError(); return DebugInfoRef.materialize(Reader, LoadedAbbrev->AbbrevContents, - LoadedLine->SecOffsetVals, &Reader.OS); + LoadedLine->SecOffsetVals, + LoadedString->MapOfStringOffsets, &Reader.OS); } Expected GroupRef::materialize(MCCASReader &Reader, @@ -746,11 +831,27 @@ DebugLineSectionRef::create(MCCASBuilder &MB, return get(B->build()); } +Expected +DebugStringSectionRef::create(MCCASBuilder &MB, + ArrayRef Fragments) { + Expected B = Builder::startNode(MB.Schema, KindString); + if (!B) + return B.takeError(); + + if (auto E = encodeReferences(Fragments, B->Data, B->Refs)) + return std::move(E); + + writeRelocationsAndAddends(MB.getSectionRelocs(), MB.getSectionAddends(), + B->Data); + return get(B->build()); +} + /// Class that represents a fully verified DebugInfoSectionRef object loaded /// from the CAS. struct LoadedDebugInfoSection { StringRef DistinctData; SmallVector AbbrevOffsets; + SmallVector> DebugStringRefs; SmallVector CUData; Optional Padding; StringRef RelocationData; @@ -797,7 +898,14 @@ struct LoadedDebugInfoSection { if (Padding.has_value()) return createStringError(inconvertibleErrorCode(), "Invalid CURef after PaddingRef"); - CUData.push_back(CURef->getData()); + + StringRef Remaining = CURef->getData(); + auto DebugStrRefs = MCObjectProxy::decodeReferences(*Proxy, Remaining); + if (!DebugStrRefs) + return DebugStrRefs.takeError(); + DebugStringRefs.resize(DebugStringRefs.size() + 1); + DebugStringRefs.back().append(*DebugStrRefs); + CUData.push_back(Remaining); } else if (auto PaddingNode = PaddingRef::Cast(*Proxy)) { if (Padding.has_value()) return createStringError(inconvertibleErrorCode(), @@ -1021,10 +1129,10 @@ static Expected getFormSize(dwarf::Form FormVal, dwarf::FormParams FP, static Error materializeCUDie(DWARFCompileUnit &DCU, MutableArrayRef SectionContents, - StringRef CUData, ArrayRef DistinctDataArrayRef, + StringRef CUData, ArrayRef DistinctDataArrayRef, DenseMap &MapOfStringOffsets, ArrayRef DebugStringRefsVec, ArrayRef SecOffsetVals, unsigned &SecOffsetIndex, uint64_t &CUOffset, uint64_t &DistinctDataOffset, - uint64_t &SectionOffset) { + uint64_t &SectionOffset, unsigned &DebugStringIndex) { // Copy Abbrev Tag. DWARFDataExtractor DWARFExtractor(CUData, DCU.isLittleEndian(), DCU.getAddressByteSize()); @@ -1074,7 +1182,9 @@ materializeCUDie(DWARFCompileUnit &DCU, MutableArrayRef SectionContents, if (!*FormSize) continue; bool DidOverflow = false; - if (FormInDistinctDataRef) { + if (FormInDistinctDataRef || + AbbrevDecl->getAttrByIndex(I) == dwarf::DW_AT_name || + AbbrevDecl->getAttrByIndex(I) == dwarf::DW_AT_linkage_name) { auto Value = SaturatingAdd(*FormSize, DistinctDataOffset, &DidOverflow); if (DidOverflow) return createStringError( @@ -1097,6 +1207,15 @@ materializeCUDie(DWARFCompileUnit &DCU, MutableArrayRef SectionContents, // sec_offsets. if (SecOffsetVals.size() > 1) SecOffsetIndex++; + } else if (Form == dwarf::DW_FORM_strp) { + auto StringOffsetIt = + MapOfStringOffsets.find(DebugStringRefsVec[DebugStringIndex]); + if (StringOffsetIt == MapOfStringOffsets.end()) + return createStringError(inconvertibleErrorCode(), + "Could not find DebugStrRef offset in map"); + // TODO: Add support for 64 bit DWARF + memcpy(&SectionContents[SectionOffset], &StringOffsetIt->second, 4); + DebugStringIndex++; } else { auto Value = SaturatingAdd(*FormSize, CUOffset, &DidOverflow); if (DidOverflow) @@ -1116,7 +1235,9 @@ materializeCUDie(DWARFCompileUnit &DCU, MutableArrayRef SectionContents, Expected DebugInfoSectionRef::materialize( MCCASReader &Reader, ArrayRef AbbrevSectionContents, - ArrayRef SecOffsetVals, raw_ostream *Stream) const { + ArrayRef SecOffsetVals, + DenseMap &MapOfStringOffsets, + raw_ostream *Stream) const { // Start a new section for relocations. Reader.Relocations.emplace_back(); auto LoadedSection = LoadedDebugInfoSection::load(*this); @@ -1149,7 +1270,11 @@ Expected DebugInfoSectionRef::materialize( Reader.getEndian() == support::little, 8)); uint64_t DistinctDataOffset = 0; unsigned SecOffsetIndex = 0; - for (auto CUData : CUContents) { + assert(CUContents.size() == LoadedSection->DebugStringRefs.size() && + "Number of Compile Units and number of DebugStringRef vectors must be " + "the same!"); + for (auto [CUData, DebugStringRefVec] : + llvm::zip(CUContents, LoadedSection->DebugStringRefs)) { uint64_t OffsetPtr = 0; DWARFUnitHeader Header; @@ -1203,12 +1328,14 @@ Expected DebugInfoSectionRef::materialize( CASObj.getStrSection(), CASObj.getStrOffsetsSection(), &CASObj.getAddrSection(), CASObj.getLocSection(), Reader.getEndian() == support::little, false, UV); + + unsigned DebugStringIndex = 0; while (SectionOffset < SectionData.size()) { auto Err = materializeCUDie( DCU, SectionData, toStringRef(CUData), - arrayRefFromStringRef(LoadedSection->DistinctData), + arrayRefFromStringRef(LoadedSection->DistinctData), MapOfStringOffsets, DebugStringRefVec, SecOffsetVals, SecOffsetIndex, CUOffset, DistinctDataOffset, - SectionOffset); + SectionOffset, DebugStringIndex); if (Err) return std::move(Err); } @@ -1306,6 +1433,39 @@ DebugAbbrevSectionRef::materialize(MCCASReader &Reader, Expected DebugLineSectionRef::materialize(MCCASReader &Reader, raw_ostream *Stream) const { +// Start a new section for relocations. + Reader.Relocations.emplace_back(); + SmallVector SectionContents; + raw_svector_ostream SectionStream(SectionContents); + + unsigned Size = 0; + StringRef Remaining = getData(); + auto Refs = decodeReferences(*this, Remaining); + if (!Refs) + return Refs.takeError(); + + for (auto ID : *Refs) { + auto FragmentSize = Reader.materializeSection(ID, &SectionStream); + if (!FragmentSize) + return FragmentSize.takeError(); + Size += *FragmentSize; + } + + if (auto E = decodeRelocationsAndAddends(Reader, Remaining)) + return std::move(E); + + if (auto E = applyAddends(Reader, SectionContents)) + return std::move(E); + + Reader.Addends.clear(); + Reader.OS << SectionContents; + + return Size; +} + +Expected +DebugStringSectionRef::materialize(MCCASReader &Reader, + raw_ostream *Stream) const { // Start a new section for relocations. Reader.Relocations.emplace_back(); SmallVector SectionContents; @@ -1841,15 +2001,17 @@ Error MCCASBuilder::createPaddingRef(const MCSection *Sec) { } Error MCCASBuilder::createStringSection( - StringRef S, std::function CreateFn) { + StringRef S, std::function CreateFn) { assert(S.endswith("\0") && "String sections are null terminated"); + unsigned DebugStringOffset = 0; if (!SplitStringSections) - return CreateFn(S); + return CreateFn(S, DebugStringOffset); while (!S.empty()) { auto SplitSym = S.split('\0'); - if (auto E = CreateFn(SplitSym.first)) + if (auto E = CreateFn(SplitSym.first, DebugStringOffset)) return E; + DebugStringOffset += SplitSym.first.size() + 1; S = SplitSym.second; } @@ -2062,7 +2224,7 @@ DebugAbbrevOffsetsRefAdaptor::encodeOffsets(ArrayRef Offsets) { return EncodedOffsets; } -static void partitionCUDie( +static void partitionCUDie(MCCASBuilder &Builder, InMemoryCASDWARFObject::PartitionedDebugInfoSection &PartitionedData, DWARFDie &CUDie, ArrayRef DebugInfoData, uint64_t &CUOffset, bool IsLittleEndian, uint8_t AddressSize) { @@ -2078,10 +2240,20 @@ static void partitionCUDie( for (const DWARFAttribute &AttrValue : CUDie.attributes()) { if (is_contained(InMemoryCASDWARFObject::PartitionedDebugInfoSection:: FormsToPartition, - AttrValue.Value.getForm())) + AttrValue.Value.getForm()) || + AttrValue.Attr == llvm::dwarf::DW_AT_name || + AttrValue.Attr == llvm::dwarf::DW_AT_linkage_name) { append_range(PartitionedData.DistinctData, DebugInfoData.slice(AttrValue.Offset, AttrValue.ByteSize)); - else if (AttrValue.Attr != llvm::dwarf::DW_AT_stmt_list) + } else if (AttrValue.Value.getForm() == llvm::dwarf::DW_FORM_strp) { + // TODO: Add support for 64-bit DWARF + uint32_t StringOffset; + memcpy(&StringOffset, &DebugInfoData[AttrValue.Offset], 4); + auto StringRef = Builder.getObjectRefFromStringMap(StringOffset); + if (!StringRef) + llvm_unreachable("Strp value not found in debug string section!"); + PartitionedData.DebugStringRefs.push_back(*StringRef); + } else if (AttrValue.Attr != llvm::dwarf::DW_AT_stmt_list) append_range(PartitionedData.DebugInfoCURefData, DebugInfoData.slice(AttrValue.Offset, AttrValue.ByteSize)); CUOffset += AttrValue.ByteSize; @@ -2089,7 +2261,7 @@ static void partitionCUDie( DWARFDie Child = CUDie.getFirstChild(); while (Child && Child.getAbbreviationDeclarationPtr()) { - partitionCUDie(PartitionedData, Child, DebugInfoData, CUOffset, + partitionCUDie(Builder, PartitionedData, Child, DebugInfoData, CUOffset, IsLittleEndian, AddressSize); Child = Child.getSibling(); } @@ -2100,9 +2272,9 @@ static void partitionCUDie( } Expected -InMemoryCASDWARFObject::partitionCUData(ArrayRef DebugInfoData, +InMemoryCASDWARFObject::partitionCUData(MCCASBuilder &Builder, ArrayRef DebugInfoData, uint64_t AbbrevOffset, - uint64_t CUOffset, DWARFContext *Ctx) { + uint64_t CUOffset, DWARFContext *Ctx, ArrayRef DebugStringRefs) { StringRef AbbrevSectionContribution = getAbbrevSection().drop_front(AbbrevOffset); @@ -2153,13 +2325,14 @@ InMemoryCASDWARFObject::partitionCUData(ArrayRef DebugInfoData, if (!C) return C.takeError(); - partitionCUDie(PartitionedData, CUDie, DebugInfoData, CUOffset, + partitionCUDie(Builder, PartitionedData, CUDie, DebugInfoData, CUOffset, IsLittleEndian, DCU.getAddressByteSize()); } return PartitionedData; } -Expected MCCASBuilder::splitDebugInfoAndAbbrevSections() { +Expected MCCASBuilder::splitDebugInfoAndAbbrevSections( + ArrayRef DebugStringRefs) { if (!DwarfSections.DebugInfo) return AbbrevAndDebugSplit{}; @@ -2199,13 +2372,14 @@ Expected MCCASBuilder::splitDebugInfoAndAbbrevSections() { llvm::zip(SplitInfo->SplitCUData, SplitInfo->AbbrevOffsets)) { uint64_t CUOffset = 0; auto PartitionedData = - CASObj.partitionCUData(CUData, AbbrevOffset, CUOffset, DWARFCtx); + CASObj.partitionCUData(*this, CUData, AbbrevOffset, CUOffset, DWARFCtx, DebugStringRefs); if (!PartitionedData) return PartitionedData.takeError(); DistinctData.append(PartitionedData->DistinctData.begin(), PartitionedData->DistinctData.end()); auto DbgInfoRef = DebugInfoCURef::create( - *this, toStringRef(PartitionedData->DebugInfoCURefData)); + *this, toStringRef(PartitionedData->DebugInfoCURefData), + PartitionedData->DebugStringRefs); if (!DbgInfoRef) return DbgInfoRef.takeError(); CURefs.push_back(*DbgInfoRef); @@ -2259,25 +2433,13 @@ Error MCCASBuilder::createLineSection() { return finalizeSection(); } -Error MCCASBuilder::createDebugStrSection() { - assert(DwarfSections.Str->getFragmentList().size() == 1 && - "One fragment in debug str section"); +Error MCCASBuilder::createDebugStrSection( + ArrayRef DebugStringRefs) { startSection(DwarfSections.Str); - - ArrayRef DebugStrData = - cast(*DwarfSections.Str->begin()).getContents(); - StringRef S(DebugStrData.data(), DebugStrData.size()); - if (auto E = createStringSection(S, [&](StringRef S) -> Error { - auto Sym = DebugStrRef::create(*this, S); - if (!Sym) - return Sym.takeError(); - addNode(*Sym); - return Error::success(); - })) - return E; - - return finalizeSection(); + for (auto DebugStringRef : DebugStringRefs) + addNode(DebugStringRef); + return finalizeSection(); } static void createAddendVector( @@ -2293,11 +2455,41 @@ static void createAddendVector( Addend.Size}); } +Expected +MCCASBuilder::createDebugStringRefs() { + if (!DwarfSections.Str || !DwarfSections.Str->getFragmentList().size()) + return DebugStringSectionContents{}; + + assert(DwarfSections.Str->getFragmentList().size() == 1 && + "One fragment in debug str section"); + + DebugStringSectionContents DebugStringContents; + ArrayRef DebugStrData = + cast(*DwarfSections.Str->begin()).getContents(); + StringRef S(DebugStrData.data(), DebugStrData.size()); + if (auto E = createStringSection( + S, [&](StringRef S, unsigned DebugStringOffset) -> Error { + auto Sym = DebugStrRef::create(*this, S); + if (!Sym) + return Sym.takeError(); + DebugStringContents.DebugStringRefs.push_back(*Sym); + DebugStringSectionContents::MapOfStringRefs.try_emplace( + DebugStringOffset, Sym->getRef()); + return Error::success(); + })) + return E; + return DebugStringContents; +} + Error MCCASBuilder::buildFragments() { startGroup(); + auto DebugStringContents = createDebugStringRefs(); + if (!DebugStringContents) + return DebugStringContents.takeError(); + Expected AbbrevAndCURefs = - splitDebugInfoAndAbbrevSections(); + splitDebugInfoAndAbbrevSections(DebugStringContents->DebugStringRefs); if (!AbbrevAndCURefs) return AbbrevAndCURefs.takeError(); @@ -2330,7 +2522,7 @@ Error MCCASBuilder::buildFragments() { // Handle Debug Str sections separately. if (&Sec == DwarfSections.Str) { - if (auto E = createDebugStrSection()) + if (auto E = createDebugStrSection(DebugStringContents->DebugStringRefs)) return E; continue; } @@ -2410,13 +2602,14 @@ Error MCCASBuilder::buildSymbolTable() { ObjectWriter.writeSymbolTable(Asm, Layout); StringRef S = ObjectWriter.getContent(); std::vector CStrings; - if (auto E = createStringSection(S, [&](StringRef S) -> Error { - auto Sym = CStringRef::create(*this, S); - if (!Sym) - return Sym.takeError(); - CStrings.push_back(Sym->getRef()); - return Error::success(); - })) + if (auto E = createStringSection( + S, [&](StringRef S, unsigned DebugStringOffset) -> Error { + auto Sym = CStringRef::create(*this, S); + if (!Sym) + return Sym.takeError(); + CStrings.push_back(Sym->getRef()); + return Error::success(); + })) return E; auto Ref = SymbolTableRef::create(*this, CStrings); @@ -2666,6 +2859,8 @@ Expected MCCASReader::materializeGroup(cas::ObjectRef ID) { // Group can have sections, symbol table strs. if (auto F = SectionRef::Cast(*Node)) return F->materialize(*this); + if (auto F = DebugStringSectionRef::Cast(*Node)) + return F->materialize(*this); if (auto F = CStringRef::Cast(*Node)) { auto Size = F->materialize(OS); if (!Size) diff --git a/llvm/tools/llvm-cas-dump/CASDWARFObject.cpp b/llvm/tools/llvm-cas-dump/CASDWARFObject.cpp index d6b4a1f691800..3d5833e755312 100644 --- a/llvm/tools/llvm-cas-dump/CASDWARFObject.cpp +++ b/llvm/tools/llvm-cas-dump/CASDWARFObject.cpp @@ -22,6 +22,40 @@ using namespace llvm; using namespace llvm::cas; using namespace llvm::mccasformats::v1; +std::unordered_map> + CASDWARFObject::MapOfLinkageNames = + std::unordered_map>(); + +// template <> struct DenseMapInfo { +// static inline std::string getEmptyKey() { +// return std::string( +// reinterpret_cast(~static_cast(0)), 0); +// } + +// static inline std::string getTombstoneKey() { +// return std::string( +// reinterpret_cast(~static_cast(1)), 0); +// } + +// static unsigned getHashValue(std::string Val); + +// static bool isEqual(std::string LHS, std::string RHS) { +// if (RHS.data() == getEmptyKey().data()) +// return LHS.data() == getEmptyKey().data(); +// if (RHS.data() == getTombstoneKey().data()) +// return LHS.data() == getTombstoneKey().data(); +// return LHS == RHS; +// } +// }; + +// unsigned DenseMapInfo::getHashValue(std::string Val) { +// assert(Val.data() != getEmptyKey().data() && +// "Cannot hash the empty key!"); +// assert(Val.data() != getTombstoneKey().data() && +// "Cannot hash the tombstone key!"); +// return (unsigned)(hash_value(Val)); +// } + namespace { /// Parse the MachO header to extract details such as endianness. /// Unfortunately object::MachOObjectfile() doesn't support parsing @@ -81,7 +115,8 @@ Error CASDWARFObject::discoverDebugInfoSection(MCObjectProxy MCObj, MCCASReader Reader(OS, Target, MCObj.getSchema()); auto Written = DbgInfoSecRef->materialize( Reader, arrayRefFromStringRef(getAbbrevSection()), - getSecOffsetVals(), &OS); + getSecOffsetVals(), + getMapOfStringOffsets(), &OS); if (!Written) return Written.takeError(); @@ -148,16 +183,94 @@ Error CASDWARFObject::discoverDwarfSections(MCObjectProxy MCObj) { if (DebugAbbrevRef::Cast(MCObj)) append_range(DebugAbbrevSection, MCObj.getData()); else if (DebugStrRef::Cast(MCObj)) { + MapOfStringOffsets.try_emplace(MCObj.getRef(), DebugStringSection.size()); DebugStringSection.append(Data.begin(), Data.end()); DebugStringSection.push_back(0); + } else if (DebugInfoCURef::Cast(MCObj)) + return Error::success(); + if (DebugAbbrevSectionRef::Cast(MCObj) || GroupRef::Cast(MCObj) || + SymbolTableRef::Cast(MCObj) || SectionRef::Cast(MCObj) || + DebugLineSectionRef::Cast(MCObj) || AtomRef::Cast(MCObj)) { + auto Refs = MCObjectProxy::decodeReferences(MCObj, Data); + if (!Refs) + return Refs.takeError(); + for (auto Ref : *Refs) { + if (Error E = discoverDwarfSections(Ref)) + return E; + } + return Error::success(); } return MCObj.forEachReference( [this](ObjectRef CASObj) { return discoverDwarfSections(CASObj); }); } +static Optional getLinkageName(DWARFDie &CUDie) { + if (CUDie.getTag() == dwarf::DW_TAG_subprogram) { + auto Decl = CUDie.findRecursively({dwarf::DW_AT_declaration}); + if (!Decl) { + StringRef LinkageName = CUDie.getLinkageName(); + if (LinkageName.size()) + return LinkageName; + } + } + DWARFDie Child = CUDie.getFirstChild(); + while (Child) { + auto Name = getLinkageName(Child); + Child = Child.getSibling(); + if (Name) + return Name; + } +} + +static void findStrpsInCompileUnit(DWARFDie &CUDie, raw_ostream &OS) { + for (auto Attr : CUDie.attributes()) { + if (Attr.Value.getForm() == llvm::dwarf::DW_FORM_strp) { + Attr.Value.dump(OS); + OS << "\n"; + } + } + DWARFDie Child = CUDie.getFirstChild(); + while (Child) { + findStrpsInCompileUnit(Child, OS); + Child = Child.getSibling(); + } +} + +// void CASDWARFObject::addLinkageNameAndObjectRefToMap(DWARFDie &CUDie, +// MCObjectProxy MCObj, +// bool &LinkageFound, +// DWARFUnit &U, +// DIDumpOptions &DumpOpts) +// { +// if (CUDie.getTag() == dwarf::DW_TAG_subprogram) { +// auto Decl = CUDie.findRecursively({dwarf::DW_AT_declaration}); +// if (!Decl) { +// StringRef LinkageName = CUDie.getLinkageName(); +// if (LinkageName.size()) { +// if (MapOfLinkageNames.find(LinkageName.data()) == +// MapOfLinkageNames.end()) +// MapOfLinkageNames.try_emplace(LinkageName.data(), +// std::unordered_set()); +// SmallVector DumpContents; +// raw_svector_ostream DumpStream(DumpContents); +// U.dump(DumpStream, DumpOpts); +// MapOfLinkageNames[LinkageName.data()].insert(DumpStream.str().data()); +// LinkageFound = true; +// } +// } +// } +// DWARFDie Child = CUDie.getFirstChild(); +// while (Child && !LinkageFound) { +// addLinkageNameAndObjectRefToMap(Child, MCObj, LinkageFound, U, DumpOpts); +// Child = Child.getSibling(); +// } +// } + Error CASDWARFObject::dump(raw_ostream &OS, int Indent, DWARFContext &DWARFCtx, - MCObjectProxy MCObj, bool ShowForm, bool Verbose) { - OS.indent(Indent); + MCObjectProxy MCObj, bool ShowForm, bool Verbose, + bool DumpSameLinkageDifferentCU) { + if (!DumpSameLinkageDifferentCU) + OS.indent(Indent); DIDumpOptions DumpOpts; DumpOpts.ShowChildren = true; DumpOpts.ShowForm = ShowForm; @@ -166,7 +279,7 @@ Error CASDWARFObject::dump(raw_ostream &OS, int Indent, DWARFContext &DWARFCtx, StringRef Data = MCObj.getData(); if (Data.empty()) return Err; - if (DebugStrRef::Cast(MCObj)) { + if (DebugStrRef::Cast(MCObj) && !DumpSameLinkageDifferentCU) { // Dump __debug_str data. assert(Data.data()[Data.size()] == 0); DataExtractor StrData(StringRef(Data.data(), Data.size() + 1), @@ -184,7 +297,7 @@ Error CASDWARFObject::dump(raw_ostream &OS, int Indent, DWARFContext &DWARFCtx, OS << "\"\n"; StrOffset = Offset; } - } else if (DebugLineRef::Cast(MCObj)) { + } else if (DebugLineRef::Cast(MCObj) && !DumpSameLinkageDifferentCU) { // Dump __debug_line data. uint64_t Address = 0; DWARFDataExtractor LineData(*this, {Data, Address}, isLittleEndian(), 0); @@ -215,7 +328,47 @@ Error CASDWARFObject::dump(raw_ostream &OS, int Indent, DWARFContext &DWARFCtx, &getLocSection(), getStrSection(), getStrOffsetsSection(), &getAddrSection(), getLocSection(), isLittleEndian(), false, UV); - U.dump(OS, DumpOpts); + if (DumpSameLinkageDifferentCU) { + if (DWARFDie CUDie = U.getUnitDIE(false)) { + auto Name = getLinkageName(CUDie); + if (!Name) + return Error::success(); + SmallVector StrpData; + raw_svector_ostream OS(StrpData); + findStrpsInCompileUnit(CUDie, OS); + if (MapOfLinkageNames.find(Name->data()) == MapOfLinkageNames.end()) + MapOfLinkageNames.try_emplace(Name->data(), + std::unordered_set()); + MapOfLinkageNames[Name->data()].insert(StrpData.data()); + } + + } else { + U.dump(OS, DumpOpts); + } } return Err; } + +Error CASDWARFObject::dumpSimilarCUs() { + for (auto KeyValue : MapOfLinkageNames) { + if (KeyValue.second.size() == 2) { + for (auto ID : KeyValue.second) { + llvm::outs() << KeyValue.first << " " << ID << "\n"; + break; + } + } + } + for (auto KeyValue : MapOfLinkageNames) { + if (KeyValue.second.size() == 2) { + int count = 0; + for (auto ID : KeyValue.second) { + if (count == 0) { + count++; + continue; + } + llvm::outs() << KeyValue.first << " " << ID << "\n"; + } + } + } + return Error::success(); +} diff --git a/llvm/tools/llvm-cas-dump/CASDWARFObject.h b/llvm/tools/llvm-cas-dump/CASDWARFObject.h index 7f9375596e657..cfcfe08075ec4 100644 --- a/llvm/tools/llvm-cas-dump/CASDWARFObject.h +++ b/llvm/tools/llvm-cas-dump/CASDWARFObject.h @@ -16,6 +16,8 @@ #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/DebugInfo/DWARF/DWARFObject.h" #include "llvm/MC/CAS/MCCASObjectV1.h" +#include +#include namespace llvm { @@ -32,6 +34,9 @@ class CASDWARFObject : public DWARFObject { SmallVector CUDataVec; SmallVector SecOffsetVals; unsigned LineTableOffset = 0; + DenseMap MapOfStringOffsets; + static std::unordered_map> + MapOfLinkageNames; unsigned CompileUnitIndex = 0; const mccasformats::v1::MCSchema &Schema; @@ -45,6 +50,11 @@ class CASDWARFObject : public DWARFObject { /// raw_ostream &OS); Error discoverDebugInfoSection(cas::ObjectRef CASObj, raw_ostream &OS); + void addLinkageNameAndObjectRefToMap(DWARFDie &CUDie, + mccasformats::v1::MCObjectProxy MCObj, + bool &LinkageFound, DWARFUnit &U, + DIDumpOptions &DumpOpts); + public: CASDWARFObject(const mccasformats::v1::MCSchema &Schema) : Schema(Schema) {} @@ -59,8 +69,8 @@ class CASDWARFObject : public DWARFObject { /// Dump MCObj as textual DWARF output. Error dump(raw_ostream &OS, int Indent, DWARFContext &DWARFCtx, - mccasformats::v1::MCObjectProxy MCObj, bool ShowForm, - bool Verbose); + mccasformats::v1::MCObjectProxy MCObj, bool ShowForm, bool Verbose, + bool DumpSameLinkageDifferentCU); StringRef getFileName() const override { return "CAS"; } ArrayRef getSectionNames() const override { return {}; } @@ -77,6 +87,10 @@ class CASDWARFObject : public DWARFObject { return {}; }; ArrayRef getSecOffsetVals() { return SecOffsetVals; } + DenseMap &getMapOfStringOffsets() { + return MapOfStringOffsets; + } + static Error dumpSimilarCUs(); }; } // namespace llvm diff --git a/llvm/tools/llvm-cas-dump/MCCASPrinter.cpp b/llvm/tools/llvm-cas-dump/MCCASPrinter.cpp index 3a9e2267836fc..59c3e3b88bbbf 100644 --- a/llvm/tools/llvm-cas-dump/MCCASPrinter.cpp +++ b/llvm/tools/llvm-cas-dump/MCCASPrinter.cpp @@ -53,7 +53,7 @@ MCCASPrinter::discoverDwarfSections(cas::ObjectRef CASObj) { if (!MCObj) return MCObj.takeError(); CASDWARFObject DWARFObj(MCObj->getSchema()); - if (Options.DwarfDump) { + if (Options.DwarfDump || Options.DumpSameLinkageDifferentCU) { if (Error E = DWARFObj.discoverDwarfSections(*MCObj)) return std::move(E); if (Error E = DWARFObj.discoverDebugInfoSection(*MCObj, OS)) @@ -79,7 +79,7 @@ Error MCCASPrinter::printMCObject(MCObjectProxy MCObj, CASDWARFObject &Obj, DWARFContext *DWARFCtx) { // Initialize DWARFObj. std::unique_ptr DWARFContextHolder; - if (Options.DwarfDump && !DWARFCtx) { + if ((Options.DwarfDump || Options.DumpSameLinkageDifferentCU) && !DWARFCtx) { auto DWARFObj = std::make_unique(Obj); DWARFContextHolder = std::make_unique(std::move(DWARFObj)); DWARFCtx = DWARFContextHolder.get(); @@ -91,24 +91,26 @@ Error MCCASPrinter::printMCObject(MCObjectProxy MCObj, CASDWARFObject &Obj, return Error::success(); // Print CAS Id. - OS.indent(Indent); - OS << formatv("{0, -15} {1} \n", MCObj.getKindString(), MCObj.getID()); - if (Options.HexDump) { - auto data = MCObj.getData(); - if (Options.HexDumpOneLine) { - OS.indent(Indent); - llvm::interleave( - data.take_front(data.size()), OS, - [this](unsigned char c) { OS << llvm::format_hex(c, 4); }, " "); - OS << "\n"; - } else { - while (!data.empty()) { + if (!Options.DumpSameLinkageDifferentCU) { + OS.indent(Indent); + OS << formatv("{0, -15} {1} \n", MCObj.getKindString(), MCObj.getID()); + if (Options.HexDump) { + auto data = MCObj.getData(); + if (Options.HexDumpOneLine) { OS.indent(Indent); llvm::interleave( - data.take_front(8), OS, + data.take_front(data.size()), OS, [this](unsigned char c) { OS << llvm::format_hex(c, 4); }, " "); OS << "\n"; - data = data.drop_front(data.size() < 8 ? data.size() : 8); + } else { + while (!data.empty()) { + OS.indent(Indent); + llvm::interleave( + data.take_front(8), OS, + [this](unsigned char c) { OS << llvm::format_hex(c, 4); }, " "); + OS << "\n"; + data = data.drop_front(data.size() < 8 ? data.size() : 8); + } } } } @@ -116,8 +118,9 @@ Error MCCASPrinter::printMCObject(MCObjectProxy MCObj, CASDWARFObject &Obj, // Dwarfdump. if (DWARFCtx) { IndentGuard Guard(Indent); - if (Error Err = Obj.dump(OS, Indent, *DWARFCtx, MCObj, Options.ShowForm, - Options.Verbose)) + if (Error Err = + Obj.dump(OS, Indent, *DWARFCtx, MCObj, Options.ShowForm, + Options.Verbose, Options.DumpSameLinkageDifferentCU)) return Err; } return printSimpleNested(MCObj, Obj, DWARFCtx); @@ -144,6 +147,20 @@ Error MCCASPrinter::printSimpleNested(MCObjectProxy AssemblerRef, if (auto E = printAbbrevOffsets(OS, *AbbrevOffsetsRef)) return E; + auto Data = AssemblerRef.getData(); + if (DebugAbbrevSectionRef::Cast(AssemblerRef) || + GroupRef::Cast(AssemblerRef) || SymbolTableRef::Cast(AssemblerRef) || + SectionRef::Cast(AssemblerRef) || + DebugLineSectionRef::Cast(AssemblerRef) || AtomRef::Cast(AssemblerRef)) { + auto Refs = MCObjectProxy::decodeReferences(AssemblerRef, Data); + if (!Refs) + return Refs.takeError(); + for (auto Ref : *Refs) { + if (Error E = printMCObject(Ref, Obj, DWARFCtx)) + return E; + } + return Error::success(); + } return AssemblerRef.forEachReference( [&](ObjectRef CASObj) { return printMCObject(CASObj, Obj, DWARFCtx); }); } diff --git a/llvm/tools/llvm-cas-dump/MCCASPrinter.h b/llvm/tools/llvm-cas-dump/MCCASPrinter.h index cb3624ca55f50..092541e2f5f79 100644 --- a/llvm/tools/llvm-cas-dump/MCCASPrinter.h +++ b/llvm/tools/llvm-cas-dump/MCCASPrinter.h @@ -28,6 +28,7 @@ struct PrinterOptions { bool HexDumpOneLine = false; bool ShowForm = false; bool Verbose = false; + bool DumpSameLinkageDifferentCU = false; }; struct MCCASPrinter { diff --git a/llvm/tools/llvm-cas-dump/llvm-cas-dump.cpp b/llvm/tools/llvm-cas-dump/llvm-cas-dump.cpp index 56fb64a6d370a..3512c7d8e3583 100644 --- a/llvm/tools/llvm-cas-dump/llvm-cas-dump.cpp +++ b/llvm/tools/llvm-cas-dump/llvm-cas-dump.cpp @@ -43,6 +43,11 @@ cl::opt cl::desc("Print out the DW_FORMs in the dwarfdump output")); cl::opt Verbose("v", cl::desc("Enable verbse output in the dwarfdump")); +cl::opt DumpSameLinkageDifferentCU( + "dump-same-link-diff-cu", + cl::desc("Dwarfdump the compile units that have the same linkage name but " + "different compile units in the the CAS")); + namespace { /// If the input is a file (--casid-file), open the file given by `InputStr` @@ -66,13 +71,19 @@ int main(int argc, char *argv[]) { ExitOnErr.setBanner(std::string(argv[0]) + ": "); cl::ParseCommandLineOptions(argc, argv); - PrinterOptions Options = { - DwarfSectionsOnly, DwarfDump, DebugAbbrevOffsets, HexDump, HexDumpOneLine, - ShowForm, Verbose}; + PrinterOptions Options = {DwarfSectionsOnly, + DwarfDump, + DebugAbbrevOffsets, + HexDump, + HexDumpOneLine, + ShowForm, + Verbose, + DumpSameLinkageDifferentCU}; std::unique_ptr CAS = ExitOnErr(createOnDiskCAS(CASPath)); MCCASPrinter Printer(Options, *CAS, llvm::outs()); + uint64_t count = 0; for (StringRef InputStr : InputStrings) { auto ID = getCASIDFromInput(*CAS, InputStr); @@ -89,6 +100,11 @@ int main(int argc, char *argv[]) { ExitOnErr(Obj.takeError()); ExitOnErr(Printer.printMCObject(*Ref, *Obj)); + count++; } + + if (Options.DumpSameLinkageDifferentCU) + ExitOnErr(CASDWARFObject::dumpSimilarCUs()); + return 0; }