Importing rustc-1.56.0 Change-Id: I98941481270706fa55f8fb2cb91686ae3bd30f38
diff --git a/src/llvm-project/llvm/tools/bugpoint/CrashDebugger.cpp b/src/llvm-project/llvm/tools/bugpoint/CrashDebugger.cpp index d06bca9..2601ee3 100644 --- a/src/llvm-project/llvm/tools/bugpoint/CrashDebugger.cpp +++ b/src/llvm-project/llvm/tools/bugpoint/CrashDebugger.cpp
@@ -425,7 +425,7 @@ } /// ReduceCrashingBlocks reducer - This works by setting the terminators of /// all terminators except the specified basic blocks to a 'ret' instruction, -/// then running the simplify-cfg pass. This has the effect of chopping up +/// then running the simplifycfg pass. This has the effect of chopping up /// the CFG really fast which can reduce large functions quickly. /// class ReduceCrashingBlocks : public ListReducer<const BasicBlock *> {
diff --git a/src/llvm-project/llvm/tools/bugpoint/FindBugs.cpp b/src/llvm-project/llvm/tools/bugpoint/FindBugs.cpp index 2b1146d..7713290 100644 --- a/src/llvm-project/llvm/tools/bugpoint/FindBugs.cpp +++ b/src/llvm-project/llvm/tools/bugpoint/FindBugs.cpp
@@ -41,7 +41,7 @@ // // Step 1: Randomize the order of the optimizer passes. // - std::shuffle(PassesToRun.begin(), PassesToRun.end(), randomness); + llvm::shuffle(PassesToRun.begin(), PassesToRun.end(), randomness); // // Step 2: Run optimizer passes on the program and check for success.
diff --git a/src/llvm-project/llvm/tools/bugpoint/ListReducer.h b/src/llvm-project/llvm/tools/bugpoint/ListReducer.h index 04f2207..06f8ddb 100644 --- a/src/llvm-project/llvm/tools/bugpoint/ListReducer.h +++ b/src/llvm-project/llvm/tools/bugpoint/ListReducer.h
@@ -92,7 +92,7 @@ // distribution (improving the speed of convergence). if (ShufflingEnabled && NumOfIterationsWithoutProgress > MaxIterations) { std::vector<ElTy> ShuffledList(TheList); - std::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness); + llvm::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness); errs() << "\n\n*** Testing shuffled set...\n\n"; // Check that random shuffle doesn't lose the bug Expected<TestResult> Result = doTest(ShuffledList, empty);
diff --git a/src/llvm-project/llvm/tools/bugpoint/ToolRunner.cpp b/src/llvm-project/llvm/tools/bugpoint/ToolRunner.cpp index c4ea1da..b81ab07 100644 --- a/src/llvm-project/llvm/tools/bugpoint/ToolRunner.cpp +++ b/src/llvm-project/llvm/tools/bugpoint/ToolRunner.cpp
@@ -607,12 +607,12 @@ static bool IsARMArchitecture(std::vector<StringRef> Args) { for (size_t I = 0; I < Args.size(); ++I) { - if (!Args[I].equals_lower("-arch")) + if (!Args[I].equals_insensitive("-arch")) continue; ++I; if (I == Args.size()) break; - if (Args[I].startswith_lower("arm")) + if (Args[I].startswith_insensitive("arm")) return true; }
diff --git a/src/llvm-project/llvm/tools/dsymutil/DebugMap.h b/src/llvm-project/llvm/tools/dsymutil/DebugMap.h index ee552ed..e4fbaa8 100644 --- a/src/llvm-project/llvm/tools/dsymutil/DebugMap.h +++ b/src/llvm-project/llvm/tools/dsymutil/DebugMap.h
@@ -114,9 +114,7 @@ const Triple &getTriple() const { return BinaryTriple; } - const ArrayRef<uint8_t> getUUID() const { - return ArrayRef<uint8_t>(BinaryUUID); - } + ArrayRef<uint8_t> getUUID() const { return ArrayRef<uint8_t>(BinaryUUID); } StringRef getBinaryPath() const { return BinaryPath; }
diff --git a/src/llvm-project/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp b/src/llvm-project/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp index 29408e7..ca14548 100644 --- a/src/llvm-project/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp +++ b/src/llvm-project/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
@@ -167,7 +167,7 @@ return true; Streamer = std::make_unique<DwarfStreamer>( - Options.FileType, OutFile, Options.Translator, Options.Minimize, + Options.FileType, OutFile, Options.Translator, [&](const Twine &Error, StringRef Context, const DWARFDie *) { error(Error, Context); }, @@ -252,7 +252,10 @@ } std::error_code EC; - raw_fd_ostream OS(Options.NoOutput ? "-" : Path.str(), EC, sys::fs::OF_None); + raw_fd_ostream OS(Options.NoOutput ? "-" : Path.str(), EC, + Options.RemarksFormat == remarks::Format::Bitstream + ? sys::fs::OF_None + : sys::fs::OF_Text); if (EC) return errorCodeToError(EC); @@ -322,6 +325,7 @@ GeneralLinker.setNumThreads(Options.Threads); GeneralLinker.setAccelTableKind(Options.TheAccelTableKind); GeneralLinker.setPrependPath(Options.PrependPath); + GeneralLinker.setKeepFunctionForStatic(Options.KeepFunctionForStatic); if (Options.Translator) GeneralLinker.setStringsTranslator(TranslationLambda); GeneralLinker.setWarningHandler( @@ -611,49 +615,56 @@ return FoundValidRelocs; } -bool DwarfLinkerForBinary::AddressManager::hasValidDebugAddrRelocationAt( - uint64_t Offset) { - auto It = std::lower_bound(ValidDebugAddrRelocs.begin(), - ValidDebugAddrRelocs.end(), Offset); - return It != ValidDebugAddrRelocs.end(); +std::vector<DwarfLinkerForBinary::AddressManager::ValidReloc> +DwarfLinkerForBinary::AddressManager::getRelocations( + const std::vector<ValidReloc> &Relocs, uint64_t StartPos, uint64_t EndPos) { + std::vector<DwarfLinkerForBinary::AddressManager::ValidReloc> Res; + + auto CurReloc = partition_point(Relocs, [StartPos](const ValidReloc &Reloc) { + return Reloc.Offset < StartPos; + }); + + while (CurReloc != Relocs.end() && CurReloc->Offset >= StartPos && + CurReloc->Offset < EndPos) { + Res.push_back(*CurReloc); + CurReloc++; + } + + return Res; } -bool DwarfLinkerForBinary::AddressManager::hasValidDebugInfoRelocationAt( - uint64_t StartOffset, uint64_t EndOffset, CompileUnit::DIEInfo &Info) { - assert(NextValidReloc == 0 || - StartOffset > ValidDebugInfoRelocs[NextValidReloc - 1].Offset); - if (NextValidReloc >= ValidDebugInfoRelocs.size()) - return false; - - uint64_t RelocOffset = ValidDebugInfoRelocs[NextValidReloc].Offset; - - // We might need to skip some relocs that we didn't consider. For - // example the high_pc of a discarded DIE might contain a reloc that - // is in the list because it actually corresponds to the start of a - // function that is in the debug map. - while (RelocOffset < StartOffset && - NextValidReloc < ValidDebugInfoRelocs.size() - 1) - RelocOffset = ValidDebugInfoRelocs[++NextValidReloc].Offset; - - if (RelocOffset < StartOffset || RelocOffset >= EndOffset) - return false; - - const auto &ValidReloc = ValidDebugInfoRelocs[NextValidReloc++]; - const auto &Mapping = ValidReloc.Mapping->getValue(); - const uint64_t BinaryAddress = Mapping.BinaryAddress; +void DwarfLinkerForBinary::AddressManager::printReloc(const ValidReloc &Reloc) { + const auto &Mapping = Reloc.Mapping->getValue(); const uint64_t ObjectAddress = Mapping.ObjectAddress ? uint64_t(*Mapping.ObjectAddress) : std::numeric_limits<uint64_t>::max(); - if (Linker.Options.Verbose) - outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey() - << "\t" - << format("0x%016" PRIx64 " => 0x%016" PRIx64 "\n", ObjectAddress, - BinaryAddress); - Info.AddrAdjust = BinaryAddress + ValidReloc.Addend; - if (Mapping.ObjectAddress) - Info.AddrAdjust -= ObjectAddress; + outs() << "Found valid debug map entry: " << Reloc.Mapping->getKey() << "\t" + << format("0x%016" PRIx64 " => 0x%016" PRIx64 "\n", ObjectAddress, + uint64_t(Mapping.BinaryAddress)); +} + +void DwarfLinkerForBinary::AddressManager::fillDieInfo( + const ValidReloc &Reloc, CompileUnit::DIEInfo &Info) { + Info.AddrAdjust = relocate(Reloc); + if (Reloc.Mapping->getValue().ObjectAddress) + Info.AddrAdjust -= uint64_t(*Reloc.Mapping->getValue().ObjectAddress); Info.InDebugMap = true; +} + +bool DwarfLinkerForBinary::AddressManager::hasValidRelocationAt( + const std::vector<ValidReloc> &AllRelocs, uint64_t StartOffset, + uint64_t EndOffset, CompileUnit::DIEInfo &Info) { + std::vector<ValidReloc> Relocs = + getRelocations(AllRelocs, StartOffset, EndOffset); + + if (Relocs.size() == 0) + return false; + + if (Linker.Options.Verbose) + printReloc(Relocs[0]); + fillDieInfo(Relocs[0], Info); + return true; } @@ -693,8 +704,8 @@ getAttributeOffsets(Abbrev, *LocationIdx, Offset, *DIE.getDwarfUnit()); // FIXME: Support relocations debug_addr. - return hasValidDebugInfoRelocationAt(LocationOffset, LocationEndOffset, - MyInfo); + return hasValidRelocationAt(ValidDebugInfoRelocs, LocationOffset, + LocationEndOffset, MyInfo); } bool DwarfLinkerForBinary::AddressManager::hasLiveAddressRange( @@ -712,16 +723,31 @@ uint64_t LowPcOffset, LowPcEndOffset; std::tie(LowPcOffset, LowPcEndOffset) = getAttributeOffsets(Abbrev, *LowPcIdx, Offset, *DIE.getDwarfUnit()); - return hasValidDebugInfoRelocationAt(LowPcOffset, LowPcEndOffset, MyInfo); + return hasValidRelocationAt(ValidDebugInfoRelocs, LowPcOffset, + LowPcEndOffset, MyInfo); } if (Form == dwarf::DW_FORM_addrx) { Optional<DWARFFormValue> AddrValue = DIE.find(dwarf::DW_AT_low_pc); - return hasValidDebugAddrRelocationAt(*AddrValue->getAsAddress()); + if (Optional<uint64_t> AddrOffsetSectionBase = + DIE.getDwarfUnit()->getAddrOffsetSectionBase()) { + uint64_t StartOffset = *AddrOffsetSectionBase + AddrValue->getRawUValue(); + uint64_t EndOffset = + StartOffset + DIE.getDwarfUnit()->getAddressByteSize(); + return hasValidRelocationAt(ValidDebugAddrRelocs, StartOffset, EndOffset, + MyInfo); + } else + Linker.reportWarning("no base offset for address table", SrcFileName); } return false; } + +uint64_t +DwarfLinkerForBinary::AddressManager::relocate(const ValidReloc &Reloc) const { + return Reloc.Mapping->getValue().BinaryAddress + Reloc.Addend; +} + /// Apply the valid relocations found by findValidRelocs() to /// the buffer \p Data, taking into account that Data is at \p BaseOffset /// in the debug_info section. @@ -733,49 +759,36 @@ bool DwarfLinkerForBinary::AddressManager::applyValidRelocs( MutableArrayRef<char> Data, uint64_t BaseOffset, bool IsLittleEndian) { assert(areRelocationsResolved()); - assert((NextValidReloc == 0 || - BaseOffset > ValidDebugInfoRelocs[NextValidReloc - 1].Offset) && - "BaseOffset should only be increasing."); - if (NextValidReloc >= ValidDebugInfoRelocs.size()) - return false; + std::vector<ValidReloc> Relocs = getRelocations( + ValidDebugInfoRelocs, BaseOffset, BaseOffset + Data.size()); - // Skip relocs that haven't been applied. - while (NextValidReloc < ValidDebugInfoRelocs.size() && - ValidDebugInfoRelocs[NextValidReloc].Offset < BaseOffset) - ++NextValidReloc; - - bool Applied = false; - uint64_t EndOffset = BaseOffset + Data.size(); - while (NextValidReloc < ValidDebugInfoRelocs.size() && - ValidDebugInfoRelocs[NextValidReloc].Offset >= BaseOffset && - ValidDebugInfoRelocs[NextValidReloc].Offset < EndOffset) { - const auto &ValidReloc = ValidDebugInfoRelocs[NextValidReloc++]; - assert(ValidReloc.Offset - BaseOffset < Data.size()); - assert(ValidReloc.Offset - BaseOffset + ValidReloc.Size <= Data.size()); + for (const ValidReloc &CurReloc : Relocs) { + assert(CurReloc.Offset - BaseOffset < Data.size()); + assert(CurReloc.Offset - BaseOffset + CurReloc.Size <= Data.size()); char Buf[8]; - uint64_t Value = ValidReloc.Mapping->getValue().BinaryAddress; - Value += ValidReloc.Addend; - for (unsigned I = 0; I != ValidReloc.Size; ++I) { - unsigned Index = IsLittleEndian ? I : (ValidReloc.Size - I - 1); + uint64_t Value = relocate(CurReloc); + for (unsigned I = 0; I != CurReloc.Size; ++I) { + unsigned Index = IsLittleEndian ? I : (CurReloc.Size - I - 1); Buf[I] = uint8_t(Value >> (Index * 8)); } - assert(ValidReloc.Size <= sizeof(Buf)); - memcpy(&Data[ValidReloc.Offset - BaseOffset], Buf, ValidReloc.Size); - Applied = true; + assert(CurReloc.Size <= sizeof(Buf)); + memcpy(&Data[CurReloc.Offset - BaseOffset], Buf, CurReloc.Size); } - return Applied; + return Relocs.size() > 0; } llvm::Expected<uint64_t> -DwarfLinkerForBinary::AddressManager::relocateIndexedAddr(uint64_t Offset) { - auto It = std::lower_bound(ValidDebugAddrRelocs.begin(), - ValidDebugAddrRelocs.end(), Offset); - if (It == ValidDebugAddrRelocs.end()) +DwarfLinkerForBinary::AddressManager::relocateIndexedAddr(uint64_t StartOffset, + uint64_t EndOffset) { + std::vector<ValidReloc> Relocs = + getRelocations(ValidDebugAddrRelocs, StartOffset, EndOffset); + if (Relocs.size() == 0) return createStringError( std::make_error_code(std::errc::invalid_argument), - "no relocation for offset %llu in debug_addr section", Offset); - return It->Mapping->getValue().BinaryAddress + It->Addend; + "no relocation for offset %llu in debug_addr section", StartOffset); + + return relocate(Relocs[0]); } bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder,
diff --git a/src/llvm-project/llvm/tools/dsymutil/DwarfLinkerForBinary.h b/src/llvm-project/llvm/tools/dsymutil/DwarfLinkerForBinary.h index c6c07d6..dc4691b 100644 --- a/src/llvm-project/llvm/tools/dsymutil/DwarfLinkerForBinary.h +++ b/src/llvm-project/llvm/tools/dsymutil/DwarfLinkerForBinary.h
@@ -82,18 +82,33 @@ std::vector<ValidReloc> ValidDebugAddrRelocs; /// } - /// Index into ValidRelocs of the next relocation to consider. As we walk - /// the DIEs in acsending file offset and as ValidRelocs is sorted by file - /// offset, keeping this index up to date is all we have to do to have a - /// cheap lookup during the root DIE selection and during DIE cloning. - unsigned NextValidReloc = 0; - RangesTy AddressRanges; + StringRef SrcFileName; + + /// Returns list of valid relocations from \p Relocs, + /// between \p StartOffset and \p NextOffset. + /// + /// \returns true if any relocation is found. + std::vector<ValidReloc> + getRelocations(const std::vector<ValidReloc> &Relocs, uint64_t StartPos, + uint64_t EndPos); + + /// Resolve specified relocation \p Reloc. + /// + /// \returns resolved value. + uint64_t relocate(const ValidReloc &Reloc) const; + + /// Fill \p Info with address information for the specified \p Reloc. + void fillDieInfo(const ValidReloc &Reloc, CompileUnit::DIEInfo &Info); + + /// Print contents of debug map entry for the specified \p Reloc. + void printReloc(const ValidReloc &Reloc); + public: AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj, const DebugMapObject &DMO) - : Linker(Linker) { + : Linker(Linker), SrcFileName(DMO.getObjectFilename()) { findValidRelocsInDebugSections(Obj, DMO); // Iterate over the debug map entries and put all the ones that are @@ -125,9 +140,7 @@ virtual bool areRelocationsResolved() const override { return true; } - bool hasValidRelocs(bool ResetRelocsPtr = true) override { - if (ResetRelocsPtr) - NextValidReloc = 0; + bool hasValidRelocs() override { return !ValidDebugInfoRelocs.empty() || !ValidDebugAddrRelocs.empty(); } @@ -149,18 +162,13 @@ std::vector<ValidReloc> &ValidRelocs); /// @} - /// Checks that there is a relocation in the debug_addr section against a + /// Checks that there is a relocation in the \p Relocs array against a /// debug map entry between \p StartOffset and \p NextOffset. /// - /// This function must be called with offsets in strictly ascending order - /// because it never looks back at relocations it already 'went past'. /// \returns true and sets Info.InDebugMap if it is the case. - bool hasValidDebugInfoRelocationAt(uint64_t StartOffset, uint64_t EndOffset, - CompileUnit::DIEInfo &Info); - - /// Checks that there is a relocation in the debug_addr section against a - /// debug map entry at the given offset. - bool hasValidDebugAddrRelocationAt(uint64_t Offset); + bool hasValidRelocationAt(const std::vector<ValidReloc> &Relocs, + uint64_t StartOffset, uint64_t EndOffset, + CompileUnit::DIEInfo &Info); bool hasLiveMemoryLocation(const DWARFDie &DIE, CompileUnit::DIEInfo &Info) override; @@ -170,7 +178,8 @@ bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset, bool IsLittleEndian) override; - llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t Offset) override; + llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t StartOffset, + uint64_t EndOffset) override; RangesTy &getValidAddressRanges() override { return AddressRanges; } @@ -178,7 +187,6 @@ AddressRanges.clear(); ValidDebugInfoRelocs.clear(); ValidDebugAddrRelocs.clear(); - NextValidReloc = 0; } };
diff --git a/src/llvm-project/llvm/tools/dsymutil/LinkUtils.h b/src/llvm-project/llvm/tools/dsymutil/LinkUtils.h index 52b3635..872a65d 100644 --- a/src/llvm-project/llvm/tools/dsymutil/LinkUtils.h +++ b/src/llvm-project/llvm/tools/dsymutil/LinkUtils.h
@@ -39,12 +39,13 @@ /// Update bool Update = false; - /// Minimize - bool Minimize = false; - /// Do not check swiftmodule timestamp bool NoTimestamp = false; + /// Whether we want a static variable to force us to keep its enclosing + /// function. + bool KeepFunctionForStatic = false; + /// Number of threads. unsigned Threads = 1;
diff --git a/src/llvm-project/llvm/tools/dsymutil/MachODebugMapParser.cpp b/src/llvm-project/llvm/tools/dsymutil/MachODebugMapParser.cpp index fccf2f5..ba2f9a1 100644 --- a/src/llvm-project/llvm/tools/dsymutil/MachODebugMapParser.cpp +++ b/src/llvm-project/llvm/tools/dsymutil/MachODebugMapParser.cpp
@@ -462,6 +462,19 @@ } } + // ThinLTO adds a unique suffix to exported private symbols. + if (ObjectSymIt == CurrentObjectAddresses.end()) { + for (auto Iter = CurrentObjectAddresses.begin(); + Iter != CurrentObjectAddresses.end(); ++Iter) { + llvm::StringRef SymbolName = Iter->getKey(); + auto Pos = SymbolName.rfind(".llvm."); + if (Pos != llvm::StringRef::npos && SymbolName.substr(0, Pos) == Name) { + ObjectSymIt = Iter; + break; + } + } + } + if (ObjectSymIt == CurrentObjectAddresses.end()) { Warning("could not find object file symbol for symbol " + Twine(Name)); return;
diff --git a/src/llvm-project/llvm/tools/dsymutil/Options.td b/src/llvm-project/llvm/tools/dsymutil/Options.td index 7bd21b0..67eac56 100644 --- a/src/llvm-project/llvm/tools/dsymutil/Options.td +++ b/src/llvm-project/llvm/tools/dsymutil/Options.td
@@ -24,6 +24,10 @@ HelpText<"Enable verbose mode.">, Group<grp_general>; +def keep_func_for_static: F<"keep-function-for-static">, + HelpText<"Make a static variable keep the enclosing function even if it would have been omitted otherwise.">, + Group<grp_general>; + def statistics: F<"statistics">, HelpText<"Print statistics about the contribution of each object file to " "the linked debug info. This prints a table after linking with the " @@ -81,19 +85,6 @@ HelpText<"Alias for --flat">, Group<grp_general>; -def minimize: F<"minimize">, - HelpText<"When used when creating a dSYM file with Apple accelerator tables, " - "this option will suppress the emission of the .debug_inlines, " - ".debug_pubnames, and .debug_pubtypes sections since dsymutil " - "has better equivalents: .apple_names and .apple_types. When used in " - "conjunction with --update option, this option will cause redundant " - "accelerator tables to be removed.">, - Group<grp_general>; -def: Flag<["-"], "z">, - Alias<minimize>, - HelpText<"Alias for --minimize">, - Group<grp_general>; - def update: F<"update">, HelpText<"Updates existing dSYM files to contain the latest accelerator tables and other DWARF optimizations.">, Group<grp_general>; @@ -146,7 +137,7 @@ def accelerator: Separate<["--", "-"], "accelerator">, MetaVarName<"<accelerator type>">, - HelpText<"Specify the desired type of accelerator table. Valid options are 'Apple', 'Dwarf' and 'Default'">, + HelpText<"Specify the desired type of accelerator table. Valid options are 'Apple' (.apple_names, .apple_namespaces, .apple_types, .apple_objc), 'Dwarf' (.debug_names), 'Pub' (.debug_pubnames, .debug_pubtypes) and 'Default'">, Group<grp_general>; def: Joined<["--", "-"], "accelerator=">, Alias<accelerator>;
diff --git a/src/llvm-project/llvm/tools/dsymutil/dsymutil.cpp b/src/llvm-project/llvm/tools/dsymutil/dsymutil.cpp index 347b2dd..2a8e317 100644 --- a/src/llvm-project/llvm/tools/dsymutil/dsymutil.cpp +++ b/src/llvm-project/llvm/tools/dsymutil/dsymutil.cpp
@@ -91,6 +91,7 @@ bool InputIsYAMLDebugMap = false; bool PaperTrailWarnings = false; bool Verify = false; + bool ForceKeepFunctionForStatic = false; std::string SymbolMap; std::string OutputFile; std::string Toolchain; @@ -200,11 +201,13 @@ return AccelTableKind::Apple; if (S == "Dwarf") return AccelTableKind::Dwarf; + if (S == "Pub") + return AccelTableKind::Pub; if (S == "Default") return AccelTableKind::Default; return make_error<StringError>( "invalid accelerator type specified: '" + S + - "'. Support values are 'Apple', 'Dwarf' and 'Default'.", + "'. Support values are 'Apple', 'Dwarf', 'Pub' and 'Default'.", inconvertibleErrorCode()); } return AccelTableKind::Default; @@ -222,13 +225,14 @@ Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail); Options.Verify = Args.hasArg(OPT_verify); - Options.LinkOpts.Minimize = Args.hasArg(OPT_minimize); Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr); Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output); Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp); Options.LinkOpts.Update = Args.hasArg(OPT_update); Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose); Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics); + Options.LinkOpts.KeepFunctionForStatic = + Args.hasArg(OPT_keep_func_for_static); if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) { Options.ReproMode = ReproducerMode::Use; @@ -312,7 +316,7 @@ SmallString<128> InfoPlist(BundleRoot); sys::path::append(InfoPlist, "Contents/Info.plist"); std::error_code EC; - raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_Text); + raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_TextWithCRLF); if (EC) return make_error<StringError>( "cannot create Plist: " + toString(errorCodeToError(EC)), EC); @@ -432,6 +436,11 @@ (Options.LinkOpts.Update || !Options.SymbolMap.empty())) return OutputLocation(std::string(InputFile)); + // When dumping the debug map, just return an empty output location. This + // allows us to compute the output location once. + if (Options.DumpDebugMap) + return OutputLocation(""); + // If a flat dSYM has been requested, things are pretty simple. if (Options.Flat) { if (Options.OutputFile.empty()) { @@ -489,25 +498,25 @@ } if (Args.hasArg(OPT_help)) { - T.PrintHelp( + T.printHelp( outs(), (std::string(argv[0]) + " [options] <input files>").c_str(), "manipulate archived DWARF debug symbol files.\n\n" "dsymutil links the DWARF debug information found in the object files\n" "for the executable <input file> by using debug symbols information\n" "contained in its symbol table.\n", false); - return 0; + return EXIT_SUCCESS; } if (Args.hasArg(OPT_version)) { cl::PrintVersionMessage(); - return 0; + return EXIT_SUCCESS; } auto OptionsOrErr = getOptions(Args); if (!OptionsOrErr) { WithColor::error() << toString(OptionsOrErr.takeError()); - return 1; + return EXIT_FAILURE; } auto &Options = *OptionsOrErr; @@ -521,7 +530,7 @@ Reproducer::createReproducer(Options.ReproMode, Options.ReproducerPath); if (!Repro) { WithColor::error() << toString(Repro.takeError()); - return 1; + return EXIT_FAILURE; } Options.LinkOpts.VFS = (*Repro)->getVFS(); @@ -530,7 +539,7 @@ if (Arch != "*" && Arch != "all" && !object::MachOObjectFile::isValidArch(Arch)) { WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n"; - return 1; + return EXIT_FAILURE; } SymbolMapLoader SymMapLoader(Options.SymbolMap); @@ -540,7 +549,7 @@ if (Options.DumpStab) { if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs, Options.LinkOpts.PrependPath)) - return 1; + return EXIT_FAILURE; continue; } @@ -552,7 +561,7 @@ if (auto EC = DebugMapPtrsOrErr.getError()) { WithColor::error() << "cannot parse the debug map for '" << InputFile << "': " << EC.message() << '\n'; - return 1; + return EXIT_FAILURE; } // Remember the number of debug maps that are being processed to decide how @@ -570,12 +579,21 @@ // Ensure that the debug map is not empty (anymore). if (DebugMapPtrsOrErr->empty()) { WithColor::error() << "no architecture to link\n"; - return 1; + return EXIT_FAILURE; } // Shared a single binary holder for all the link steps. BinaryHolder BinHolder(Options.LinkOpts.VFS); + // Compute the output location and update the resource directory. + Expected<OutputLocation> OutputLocationOrErr = + getOutputFileName(InputFile, Options); + if (!OutputLocationOrErr) { + WithColor::error() << toString(OutputLocationOrErr.takeError()); + return EXIT_FAILURE; + } + Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir(); + // Statistics only require different architectures to be processed // sequentially, the link itself can still happen in parallel. Change the // thread pool strategy here instead of modifying LinkOpts.Threads. @@ -617,14 +635,6 @@ // types don't work with std::bind in the ThreadPool implementation. std::shared_ptr<raw_fd_ostream> OS; - Expected<OutputLocation> OutputLocationOrErr = - getOutputFileName(InputFile, Options); - if (!OutputLocationOrErr) { - WithColor::error() << toString(OutputLocationOrErr.takeError()); - return 1; - } - Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir(); - std::string OutputFile = OutputLocationOrErr->DWARFFile; if (NeedsTempFiles) { TempFiles.emplace_back(Map->getTriple().getArchName().str()); @@ -632,7 +642,7 @@ auto E = TempFiles.back().createTempFile(); if (E) { WithColor::error() << toString(std::move(E)); - return 1; + return EXIT_FAILURE; } auto &TempFile = *(TempFiles.back().File); @@ -645,7 +655,7 @@ Options.LinkOpts.NoOutput ? "-" : OutputFile, EC, sys::fs::OF_None); if (EC) { WithColor::error() << OutputFile << ": " << EC.message(); - return 1; + return EXIT_FAILURE; } } @@ -671,21 +681,28 @@ Threads.wait(); if (!AllOK) - return 1; + return EXIT_FAILURE; if (NeedsTempFiles) { - Expected<OutputLocation> OutputLocationOrErr = - getOutputFileName(InputFile, Options); - if (!OutputLocationOrErr) { - WithColor::error() << toString(OutputLocationOrErr.takeError()); - return 1; - } if (!MachOUtils::generateUniversalBinary(TempFiles, OutputLocationOrErr->DWARFFile, Options.LinkOpts, SDKPath)) - return 1; + return EXIT_FAILURE; + } + + // The Mach-O object file format is limited to 4GB. Make sure that we print + // an error when we emit an invalid Mach-O companion file. Leave the + // invalid object file around on disk for inspection. + ErrorOr<vfs::Status> stat = + Options.LinkOpts.VFS->status(OutputLocationOrErr->DWARFFile); + if (stat) { + if (stat->getSize() > std::numeric_limits<uint32_t>::max()) { + WithColor::error() << "the linked debug info exceeds the 4GB Mach-O " + "object file format."; + return EXIT_FAILURE; + } } } - return 0; + return EXIT_SUCCESS; }
diff --git a/src/llvm-project/llvm/tools/gold/gold-plugin.cpp b/src/llvm-project/llvm/tools/gold/gold-plugin.cpp index 94a26bb..6bcb1e4 100644 --- a/src/llvm-project/llvm/tools/gold/gold-plugin.cpp +++ b/src/llvm-project/llvm/tools/gold/gold-plugin.cpp
@@ -623,8 +623,10 @@ sym.comdat_key = nullptr; int CI = Sym.getComdatIndex(); if (CI != -1) { - StringRef C = Obj->getComdatTable()[CI]; - sym.comdat_key = strdup(C.str().c_str()); + // Not setting comdat_key for nodeduplicate ensuress we don't deduplicate. + std::pair<StringRef, Comdat::SelectionKind> C = Obj->getComdatTable()[CI]; + if (C.second != Comdat::NoDeduplicate) + sym.comdat_key = strdup(C.first.str().c_str()); } sym.resolution = LDPR_UNKNOWN; @@ -748,12 +750,15 @@ case LDPR_RESOLVED_IR: case LDPR_RESOLVED_EXEC: - case LDPR_RESOLVED_DYN: case LDPR_PREEMPTED_IR: case LDPR_PREEMPTED_REG: case LDPR_UNDEF: break; + case LDPR_RESOLVED_DYN: + R.ExportDynamic = true; + break; + case LDPR_PREVAILING_DEF_IRONLY: R.Prevailing = !isUndefined(Sym); break; @@ -765,6 +770,9 @@ case LDPR_PREVAILING_DEF_IRONLY_EXP: R.Prevailing = !isUndefined(Sym); + // Identify symbols exported dynamically, and that therefore could be + // referenced by a shared library not visible to the linker. + R.ExportDynamic = true; if (!Res.CanOmitFromDynSym) R.VisibleToRegularObj = true; break; @@ -912,7 +920,10 @@ case options::OT_BC_ONLY: Conf.PostInternalizeModuleHook = [](size_t Task, const Module &M) { std::error_code EC; - raw_fd_ostream OS(output_name, EC, sys::fs::OpenFlags::OF_None); + SmallString<128> TaskFilename; + getOutputFileName(output_name, /* TempOutFile */ false, TaskFilename, + Task); + raw_fd_ostream OS(TaskFilename, EC, sys::fs::OpenFlags::OF_None); if (EC) message(LDPL_FATAL, "Failed to write the output file."); WriteBitcodeToFile(M, OS, /* ShouldPreserveUseListOrder */ false);
diff --git a/src/llvm-project/llvm/tools/llc/llc.cpp b/src/llvm-project/llvm/tools/llc/llc.cpp index 48f0adf..6a1e2ba 100644 --- a/src/llvm-project/llvm/tools/llc/llc.cpp +++ b/src/llvm-project/llvm/tools/llc/llc.cpp
@@ -126,9 +126,10 @@ static cl::opt<bool> ShowMCEncoding("show-mc-encoding", cl::Hidden, cl::desc("Show encoding in .s output")); -static cl::opt<bool> EnableDwarfDirectory( - "enable-dwarf-directory", cl::Hidden, - cl::desc("Use .file directives with an explicit directory.")); +static cl::opt<bool> + DwarfDirectory("dwarf-directory", cl::Hidden, + cl::desc("Use .file directives with an explicit directory"), + cl::init(true)); static cl::opt<bool> AsmVerbose("asm-verbose", cl::desc("Add comments to directives."), @@ -276,7 +277,7 @@ std::error_code EC; sys::fs::OpenFlags OpenFlags = sys::fs::OF_None; if (!Binary) - OpenFlags |= sys::fs::OF_Text; + OpenFlags |= sys::fs::OF_TextWithCRLF; auto FDOut = std::make_unique<ToolOutputFile>(OutputFilename, EC, OpenFlags); if (EC) { reportError(EC.message()); @@ -290,6 +291,22 @@ bool *HasError; LLCDiagnosticHandler(bool *HasErrorPtr) : HasError(HasErrorPtr) {} bool handleDiagnostics(const DiagnosticInfo &DI) override { + if (DI.getKind() == llvm::DK_SrcMgr) { + const auto &DISM = cast<DiagnosticInfoSrcMgr>(DI); + const SMDiagnostic &SMD = DISM.getSMDiag(); + + if (SMD.getKind() == SourceMgr::DK_Error) + *HasError = true; + + SMD.print(nullptr, errs()); + + // For testing purposes, we print the LocCookie here. + if (DISM.isInlineAsmDiag() && DISM.getLocCookie()) + WithColor::note() << "!srcloc = " << DISM.getLocCookie() << "\n"; + + return true; + } + if (DI.getSeverity() == DS_Error) *HasError = true; @@ -305,19 +322,6 @@ } }; -static void InlineAsmDiagHandler(const SMDiagnostic &SMD, void *Context, - unsigned LocCookie) { - bool *HasError = static_cast<bool *>(Context); - if (SMD.getKind() == SourceMgr::DK_Error) - *HasError = true; - - SMD.print(nullptr, errs()); - - // For testing purposes, we print the LocCookie here. - if (LocCookie) - WithColor::note() << "!srcloc = " << LocCookie << "\n"; -} - // main - Entry point for the llc compiler. // int main(int argc, char **argv) { @@ -349,8 +353,10 @@ initializeVectorization(*Registry); initializeScalarizeMaskedMemIntrinLegacyPassPass(*Registry); initializeExpandReductionsPass(*Registry); + initializeExpandVectorPredicationPass(*Registry); initializeHardwareLoopsPass(*Registry); initializeTransformUtils(*Registry); + initializeReplaceWithVeclibLegacyPass(*Registry); // Initialize debugging passes. initializeScavengerTestPass(*Registry); @@ -366,7 +372,6 @@ bool HasError = false; Context.setDiagnosticHandler( std::make_unique<LLCDiagnosticHandler>(&HasError)); - Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError); Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr = setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses, @@ -469,7 +474,7 @@ TargetMachine::parseBinutilsVersion(BinutilsVersion); Options.DisableIntegratedAS = NoIntegratedAssembler; Options.MCOptions.ShowMCEncoding = ShowMCEncoding; - Options.MCOptions.MCUseDwarfDirectory = EnableDwarfDirectory; + Options.MCOptions.MCUseDwarfDirectory = DwarfDirectory; Options.MCOptions.AsmVerbose = AsmVerbose; Options.MCOptions.PreserveAsmComments = PreserveComments; Options.MCOptions.IASSearchPaths = IncludeDirs;
diff --git a/src/llvm-project/llvm/tools/lli/CMakeLists.txt b/src/llvm-project/llvm/tools/lli/CMakeLists.txt index 098e9dd..90797cc 100644 --- a/src/llvm-project/llvm/tools/lli/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/lli/CMakeLists.txt
@@ -8,6 +8,7 @@ ExecutionEngine IRReader Interpreter + JITLink MC MCJIT Object @@ -50,6 +51,7 @@ add_llvm_tool(lli lli.cpp + ExecutionUtils.cpp DEPENDS intrinsics_gen
diff --git a/src/llvm-project/llvm/tools/lli/ExecutionUtils.cpp b/src/llvm-project/llvm/tools/lli/ExecutionUtils.cpp new file mode 100644 index 0000000..55370ed --- /dev/null +++ b/src/llvm-project/llvm/tools/lli/ExecutionUtils.cpp
@@ -0,0 +1,146 @@ +//===---- ExecutionUtils.cpp - Utilities for executing functions in lli ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ExecutionUtils.h" + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include <cstdint> +#include <vector> + +// Declarations follow the GDB JIT interface (version 1, 2009) and must match +// those of the DYLD used for testing. See: +// +// llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp +// llvm/lib/ExecutionEngine/GDBRegistrationListener.cpp +// +typedef enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN +} jit_actions_t; + +struct jit_code_entry { + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const char *symfile_addr; + uint64_t symfile_size; +}; + +struct jit_descriptor { + uint32_t version; + // This should be jit_actions_t, but we want to be specific about the + // bit-width. + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; +}; + +namespace llvm { + +template <typename... Ts> static void outsv(const char *Fmt, Ts &&...Vals) { + outs() << formatv(Fmt, Vals...); +} + +static const char *actionFlagToStr(uint32_t ActionFlag) { + switch (ActionFlag) { + case JIT_NOACTION: + return "JIT_NOACTION"; + case JIT_REGISTER_FN: + return "JIT_REGISTER_FN"; + case JIT_UNREGISTER_FN: + return "JIT_UNREGISTER_FN"; + } + return "<invalid action_flag>"; +} + +// Sample output: +// +// Reading __jit_debug_descriptor at 0x0000000000404048 +// +// Version: 0 +// Action: JIT_REGISTER_FN +// +// Entry Symbol File Size Previous Entry +// [ 0] 0x0000000000451290 0x0000000000002000 200 0x0000000000000000 +// [ 1] 0x0000000000451260 0x0000000000001000 100 0x0000000000451290 +// ... +// +static void dumpDebugDescriptor(void *Addr) { + outsv("Reading __jit_debug_descriptor at {0}\n\n", Addr); + + jit_descriptor *Descriptor = reinterpret_cast<jit_descriptor *>(Addr); + outsv("Version: {0}\n", Descriptor->version); + outsv("Action: {0}\n\n", actionFlagToStr(Descriptor->action_flag)); + outsv("{0,11} {1,24} {2,15} {3,14}\n", "Entry", "Symbol File", "Size", + "Previous Entry"); + + unsigned Idx = 0; + for (auto *Entry = Descriptor->first_entry; Entry; Entry = Entry->next_entry) + outsv("[{0,2}] {1:X16} {2:X16} {3,8:D} {4}\n", Idx++, Entry, + reinterpret_cast<const void *>(Entry->symfile_addr), + Entry->symfile_size, Entry->prev_entry); +} + +static LLIBuiltinFunctionGenerator *Generator = nullptr; + +static void dumpDebugObjects(void *Addr) { + jit_descriptor *Descriptor = reinterpret_cast<jit_descriptor *>(Addr); + for (auto *Entry = Descriptor->first_entry; Entry; Entry = Entry->next_entry) + Generator->appendDebugObject(Entry->symfile_addr, Entry->symfile_size); +} + +LLIBuiltinFunctionGenerator::LLIBuiltinFunctionGenerator( + std::vector<BuiltinFunctionKind> Enabled, orc::MangleAndInterner &Mangle) + : TestOut(nullptr) { + Generator = this; + for (BuiltinFunctionKind F : Enabled) { + switch (F) { + case BuiltinFunctionKind::DumpDebugDescriptor: + expose(Mangle("__dump_jit_debug_descriptor"), &dumpDebugDescriptor); + break; + case BuiltinFunctionKind::DumpDebugObjects: + expose(Mangle("__dump_jit_debug_objects"), &dumpDebugObjects); + TestOut = createToolOutput(); + break; + } + } +} + +Error LLIBuiltinFunctionGenerator::tryToGenerate( + orc::LookupState &LS, orc::LookupKind K, orc::JITDylib &JD, + orc::JITDylibLookupFlags JDLookupFlags, + const orc::SymbolLookupSet &Symbols) { + orc::SymbolMap NewSymbols; + for (const auto &NameFlags : Symbols) { + auto It = BuiltinFunctions.find(NameFlags.first); + if (It != BuiltinFunctions.end()) + NewSymbols.insert(*It); + } + + if (NewSymbols.empty()) + return Error::success(); + + return JD.define(absoluteSymbols(std::move(NewSymbols))); +} + +// static +std::unique_ptr<ToolOutputFile> +LLIBuiltinFunctionGenerator::createToolOutput() { + std::error_code EC; + auto TestOut = std::make_unique<ToolOutputFile>("-", EC, sys::fs::OF_None); + if (EC) { + errs() << "Error creating tool output file: " << EC.message() << '\n'; + exit(1); + } + return TestOut; +} + +} // namespace llvm
diff --git a/src/llvm-project/llvm/tools/lli/ExecutionUtils.h b/src/llvm-project/llvm/tools/lli/ExecutionUtils.h new file mode 100644 index 0000000..fcd1db0 --- /dev/null +++ b/src/llvm-project/llvm/tools/lli/ExecutionUtils.h
@@ -0,0 +1,60 @@ +//===- ExecutionUtils.h - Utilities for executing code in lli ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Contains utilities for executing code in lli. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLI_EXECUTIONUTILS_H +#define LLVM_TOOLS_LLI_EXECUTIONUTILS_H + +#include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/Mangling.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ToolOutputFile.h" + +#include <memory> +#include <utility> + +namespace llvm { + +enum class BuiltinFunctionKind { + DumpDebugDescriptor, + DumpDebugObjects, +}; + +// Utility class to expose symbols for special-purpose functions to the JIT. +class LLIBuiltinFunctionGenerator : public orc::DefinitionGenerator { +public: + LLIBuiltinFunctionGenerator(std::vector<BuiltinFunctionKind> Enabled, + orc::MangleAndInterner &Mangle); + + Error tryToGenerate(orc::LookupState &LS, orc::LookupKind K, + orc::JITDylib &JD, orc::JITDylibLookupFlags JDLookupFlags, + const orc::SymbolLookupSet &Symbols) override; + + void appendDebugObject(const char *Addr, size_t Size) { + TestOut->os().write(Addr, Size); + } + +private: + orc::SymbolMap BuiltinFunctions; + std::unique_ptr<ToolOutputFile> TestOut; + + template <typename T> void expose(orc::SymbolStringPtr Name, T *Handler) { + BuiltinFunctions[Name] = JITEvaluatedSymbol( + pointerToJITTargetAddress(Handler), JITSymbolFlags::Exported); + } + + static std::unique_ptr<ToolOutputFile> createToolOutput(); +}; + +} // end namespace llvm + +#endif // LLVM_TOOLS_LLI_EXECUTIONUTILS_H
diff --git a/src/llvm-project/llvm/tools/lli/lli.cpp b/src/llvm-project/llvm/tools/lli/lli.cpp index 70c8381..af614c0 100644 --- a/src/llvm-project/llvm/tools/lli/lli.cpp +++ b/src/llvm-project/llvm/tools/lli/lli.cpp
@@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "ExecutionUtils.h" #include "RemoteJITUtils.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" @@ -22,15 +23,21 @@ #include "llvm/ExecutionEngine/GenericValue.h" #include "llvm/ExecutionEngine/Interpreter.h" #include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/ObjectCache.h" +#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h" #include "llvm/ExecutionEngine/Orc/DebugUtils.h" +#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" +#include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" -#include "llvm/ExecutionEngine/Orc/MachOPlatform.h" #include "llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h" #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/IRBuilder.h" @@ -76,7 +83,8 @@ namespace { - enum class JITKind { MCJIT, OrcLazy }; + enum class JITKind { MCJIT, Orc, OrcLazy }; + enum class JITLinkerKind { Default, RuntimeDyld, JITLink }; cl::opt<std::string> InputFile(cl::desc("<input bitcode>"), cl::Positional, cl::init("-")); @@ -90,11 +98,22 @@ cl::opt<JITKind> UseJITKind( "jit-kind", cl::desc("Choose underlying JIT kind."), - cl::init(JITKind::MCJIT), + cl::init(JITKind::Orc), cl::values(clEnumValN(JITKind::MCJIT, "mcjit", "MCJIT"), + clEnumValN(JITKind::Orc, "orc", "Orc JIT"), clEnumValN(JITKind::OrcLazy, "orc-lazy", "Orc-based lazy JIT."))); + cl::opt<JITLinkerKind> + JITLinker("jit-linker", cl::desc("Choose the dynamic linker/loader."), + cl::init(JITLinkerKind::Default), + cl::values(clEnumValN(JITLinkerKind::Default, "default", + "Default for platform and JIT-kind"), + clEnumValN(JITLinkerKind::RuntimeDyld, "rtdyld", + "RuntimeDyld"), + clEnumValN(JITLinkerKind::JITLink, "jitlink", + "Orc-specific linker"))); + cl::opt<unsigned> LazyJITCompileThreads("compile-threads", cl::desc("Choose the number of compile threads " @@ -208,7 +227,7 @@ cl::desc("Do not resolve lli process symbols in JIT'd code"), cl::init(false)); - enum class LLJITPlatform { DetectHost, GenericIR, MachO }; + enum class LLJITPlatform { Inactive, DetectHost, GenericIR }; cl::opt<LLJITPlatform> Platform("lljit-platform", cl::desc("Platform to use with LLJIT"), @@ -217,8 +236,8 @@ "Select based on JIT target triple"), clEnumValN(LLJITPlatform::GenericIR, "GenericIR", "Use LLJITGenericIRPlatform"), - clEnumValN(LLJITPlatform::MachO, "MachO", - "Use LLJITMachOPlatform")), + clEnumValN(LLJITPlatform::Inactive, "Inactive", + "Disable platform support explicitly")), cl::Hidden); enum class DumpKind { @@ -243,9 +262,28 @@ "will overwrite existing files).")), cl::Hidden); + cl::list<BuiltinFunctionKind> GenerateBuiltinFunctions( + "generate", + cl::desc("Provide built-in functions for access by JITed code " + "(jit-kind=orc-lazy only)"), + cl::values(clEnumValN(BuiltinFunctionKind::DumpDebugDescriptor, + "__dump_jit_debug_descriptor", + "Dump __jit_debug_descriptor contents to stdout"), + clEnumValN(BuiltinFunctionKind::DumpDebugObjects, + "__dump_jit_debug_objects", + "Dump __jit_debug_descriptor in-memory debug " + "objects as tool output")), + cl::Hidden); + ExitOnError ExitOnErr; } +LLVM_ATTRIBUTE_USED void linkComponents() { + errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper + << (void *)&llvm_orc_deregisterEHFrameSectionWrapper + << (void *)&llvm_orc_registerJITLoaderGDBWrapper; +} + //===----------------------------------------------------------------------===// // Object cache // @@ -288,7 +326,8 @@ return nullptr; // Load the object from the cache filename ErrorOr<std::unique_ptr<MemoryBuffer>> IRObjectBuffer = - MemoryBuffer::getFile(CacheName, -1, false); + MemoryBuffer::getFile(CacheName, /*IsText=*/false, + /*RequiresNullTerminator=*/false); // If the file isn't there, that's OK. if (!IRObjectBuffer) return nullptr; @@ -378,7 +417,7 @@ } Error loadDylibs(); -int runOrcLazyJIT(const char *ProgName); +int runOrcJIT(const char *ProgName); void disallowOrcOptions(); //===----------------------------------------------------------------------===// @@ -405,11 +444,12 @@ ExitOnErr(loadDylibs()); - if (UseJITKind == JITKind::OrcLazy) - return runOrcLazyJIT(argv[0]); - else + if (UseJITKind == JITKind::MCJIT) disallowOrcOptions(); + else + return runOrcJIT(argv[0]); + // Old lli implementation based on ExecutionEngine and MCJIT. LLVMContext Context; // Load the bitcode... @@ -677,7 +717,8 @@ } // Create a remote target client running over the channel. - llvm::orc::ExecutionSession ES; + llvm::orc::ExecutionSession ES( + std::make_unique<orc::UnsupportedExecutorProcessControl>()); ES.setErrorReporter([&](Error Err) { ExitOnErr(std::move(Err)); }); typedef orc::remote::OrcRemoteTargetClient MyRemote; auto R = ExitOnErr(MyRemote::Create(*C, ES)); @@ -748,7 +789,8 @@ case DumpKind::DumpModsToDisk: return [](Module &M) { std::error_code EC; - raw_fd_ostream Out(M.getModuleIdentifier() + ".ll", EC, sys::fs::OF_Text); + raw_fd_ostream Out(M.getModuleIdentifier() + ".ll", EC, + sys::fs::OF_TextWithCRLF); if (EC) { errs() << "Couldn't open " << M.getModuleIdentifier() << " for dumping.\nError:" << EC.message() << "\n"; @@ -791,7 +833,7 @@ return orc::ThreadSafeModule(std::move(M), std::move(TSCtx)); } -int runOrcLazyJIT(const char *ProgName) { +int runOrcJIT(const char *ProgName) { // Start setting up the JIT environment. // Parse the main module. @@ -829,6 +871,17 @@ .setRelocationModel(codegen::getExplicitRelocModel()) .setCodeModel(codegen::getExplicitCodeModel()); + // FIXME: Setting a dummy call-through manager in non-lazy mode prevents the + // JIT builder to instantiate a default (which would fail with an error for + // unsupported architectures). + if (UseJITKind != JITKind::OrcLazy) { + auto ES = std::make_unique<orc::ExecutionSession>( + ExitOnErr(orc::SelfExecutorProcessControl::Create())); + Builder.setLazyCallthroughManager( + std::make_unique<orc::LazyCallThroughManager>(*ES, 0, nullptr)); + Builder.setExecutionSession(std::move(ES)); + } + Builder.setLazyCompileFailureAddr( pointerToJITTargetAddress(exitOnLazyCallThroughFailure)); Builder.setNumCompileThreads(LazyJITCompileThreads); @@ -859,32 +912,43 @@ // Set up LLJIT platform. { LLJITPlatform P = Platform; - if (P == LLJITPlatform::DetectHost) { - if (TT->isOSBinFormatMachO()) - P = LLJITPlatform::MachO; - else - P = LLJITPlatform::GenericIR; - } + if (P == LLJITPlatform::DetectHost) + P = LLJITPlatform::GenericIR; switch (P) { case LLJITPlatform::GenericIR: // Nothing to do: LLJITBuilder will use this by default. break; - case LLJITPlatform::MachO: - Builder.setPlatformSetUp(orc::setUpMachOPlatform); - ExitOnErr(orc::enableObjCRegistration("libobjc.dylib")); + case LLJITPlatform::Inactive: + Builder.setPlatformSetUp(orc::setUpInactivePlatform); break; default: llvm_unreachable("Unrecognized platform value"); } } + std::unique_ptr<orc::ExecutorProcessControl> EPC = nullptr; + if (JITLinker == JITLinkerKind::JITLink) { + EPC = ExitOnErr(orc::SelfExecutorProcessControl::Create( + std::make_shared<orc::SymbolStringPool>())); + + Builder.setObjectLinkingLayerCreator([&EPC](orc::ExecutionSession &ES, + const Triple &) { + auto L = std::make_unique<orc::ObjectLinkingLayer>(ES, EPC->getMemMgr()); + L->addPlugin(std::make_unique<orc::EHFrameRegistrationPlugin>( + ES, ExitOnErr(orc::EPCEHFrameRegistrar::Create(ES)))); + L->addPlugin(std::make_unique<orc::DebugObjectManagerPlugin>( + ES, ExitOnErr(orc::createJITLoaderGDBRegistrar(ES)))); + return L; + }); + } + auto J = ExitOnErr(Builder.create()); - if (TT->isOSBinFormatELF()) - static_cast<llvm::orc::RTDyldObjectLinkingLayer &>(J->getObjLinkingLayer()) - .registerJITEventListener( - *JITEventListener::createGDBRegistrationListener()); + auto *ObjLayer = &J->getObjLinkingLayer(); + if (auto *RTDyldObjLayer = dyn_cast<orc::RTDyldObjectLinkingLayer>(ObjLayer)) + RTDyldObjLayer->registerJITEventListener( + *JITEventListener::createGDBRegistrationListener()); if (PerModuleLazy) J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule); @@ -916,8 +980,22 @@ return Name != MainName; }))); + if (GenerateBuiltinFunctions.size() > 0) + J->getMainJITDylib().addGenerator( + std::make_unique<LLIBuiltinFunctionGenerator>(GenerateBuiltinFunctions, + Mangle)); + + // Regular modules are greedy: They materialize as a whole and trigger + // materialization for all required symbols recursively. Lazy modules go + // through partitioning and they replace outgoing calls with reexport stubs + // that resolve on call-through. + auto AddModule = [&](orc::JITDylib &JD, orc::ThreadSafeModule M) { + return UseJITKind == JITKind::OrcLazy ? J->addLazyIRModule(JD, std::move(M)) + : J->addIRModule(JD, std::move(M)); + }; + // Add the main module. - ExitOnErr(J->addLazyIRModule(std::move(MainModule))); + ExitOnErr(AddModule(J->getMainJITDylib(), std::move(MainModule))); // Create JITDylibs and add any extra modules. { @@ -945,7 +1023,7 @@ assert(EMIdx != 0 && "ExtraModule should have index > 0"); auto JDItr = std::prev(IdxToDylib.lower_bound(EMIdx)); auto &JD = *JDItr->second; - ExitOnErr(J->addLazyIRModule(JD, std::move(M))); + ExitOnErr(AddModule(JD, std::move(M))); } for (auto EAItr = ExtraArchives.begin(), EAEnd = ExtraArchives.end(); @@ -978,13 +1056,19 @@ AltEntryThreads.push_back(std::thread([EntryPoint]() { EntryPoint(); })); } - // Run main. - auto MainSym = ExitOnErr(J->lookup("main")); + // Resolve and run the main function. + JITEvaluatedSymbol MainSym = ExitOnErr(J->lookup(EntryFunc)); + int Result; - typedef int (*MainFnPtr)(int, char *[]); - auto Result = orc::runAsMain( - jitTargetAddressToFunction<MainFnPtr>(MainSym.getAddress()), InputArgv, - StringRef(InputFile)); + if (EPC) { + // ExecutorProcessControl-based execution with JITLink. + Result = ExitOnErr(EPC->runAsMain(MainSym.getAddress(), InputArgv)); + } else { + // Manual in-process execution with RuntimeDyld. + using MainFnTy = int(int, char *[]); + auto MainFn = jitTargetAddressToFunction<MainFnTy *>(MainSym.getAddress()); + Result = orc::runAsMain(MainFn, InputArgv, StringRef(InputFile)); + } // Wait for -entry-point threads. for (auto &AltEntryThread : AltEntryThreads)
diff --git a/src/llvm-project/llvm/tools/llvm-ar/llvm-ar.cpp b/src/llvm-project/llvm/tools/llvm-ar/llvm-ar.cpp index 4c26c8c..0e1dce6 100644 --- a/src/llvm-project/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/src/llvm-project/llvm/tools/llvm-ar/llvm-ar.cpp
@@ -126,9 +126,9 @@ )"; static void printHelpMessage() { - if (Stem.contains_lower("ranlib")) + if (Stem.contains_insensitive("ranlib")) outs() << RanlibHelp; - else if (Stem.contains_lower("ar")) + else if (Stem.contains_insensitive("ar")) outs() << ArHelp; } @@ -270,7 +270,8 @@ } static object::Archive &readLibrary(const Twine &Library) { - auto BufOrErr = MemoryBuffer::getFile(Library, -1, false); + auto BufOrErr = MemoryBuffer::getFile(Library, /*IsText=*/false, + /*RequiresNullTerminator=*/false); failIfError(BufOrErr.getError(), "could not open library " + Library); ArchiveBuffers.push_back(std::move(*BufOrErr)); auto LibOrErr = @@ -995,8 +996,8 @@ static int performOperation(ArchiveOperation Operation, std::vector<NewArchiveMember> *NewMembers) { // Create or open the archive object. - ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = - MemoryBuffer::getFile(ArchiveName, -1, false); + ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile( + ArchiveName, /*IsText=*/false, /*RequiresNullTerminator=*/false); std::error_code EC = Buf.getError(); if (EC && EC != errc::no_such_file_or_directory) fail("unable to open '" + ArchiveName + "': " + EC.message()); @@ -1275,7 +1276,7 @@ // Lib.exe -> lib (see D44808, MSBuild runs Lib.exe) // dlltool.exe -> dlltool // arm-pokymllib32-linux-gnueabi-llvm-ar-10 -> ar - auto I = Stem.rfind_lower(Tool); + auto I = Stem.rfind_insensitive(Tool); return I != StringRef::npos && (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); };
diff --git a/src/llvm-project/llvm/tools/llvm-as/llvm-as.cpp b/src/llvm-project/llvm/tools/llvm-as/llvm-as.cpp index f2b5289..307a7f9 100644 --- a/src/llvm-project/llvm/tools/llvm-as/llvm-as.cpp +++ b/src/llvm-project/llvm/tools/llvm-as/llvm-as.cpp
@@ -115,9 +115,9 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); - LLVMContext Context; cl::HideUnrelatedOptions(AsCat); cl::ParseCommandLineOptions(argc, argv, "llvm .ll -> .bc assembler\n"); + LLVMContext Context; // Parse the file now... SMDiagnostic Err;
diff --git a/src/llvm-project/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/src/llvm-project/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index 639a6d1..f4851bf 100644 --- a/src/llvm-project/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/src/llvm-project/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
@@ -32,37 +32,48 @@ #include "llvm/Support/Error.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <memory> using namespace llvm; -static cl::opt<std::string> - InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); +static cl::OptionCategory BCAnalyzerCategory("BC Analyzer Options"); -static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace")); +static cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input bitcode>"), + cl::init("-"), + cl::cat(BCAnalyzerCategory)); + +static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace"), + cl::cat(BCAnalyzerCategory)); //===----------------------------------------------------------------------===// // Bitcode specific analysis. //===----------------------------------------------------------------------===// static cl::opt<bool> NoHistogram("disable-histogram", - cl::desc("Do not print per-code histogram")); + cl::desc("Do not print per-code histogram"), + cl::cat(BCAnalyzerCategory)); static cl::opt<bool> NonSymbolic("non-symbolic", cl::desc("Emit numeric info in dump even if" - " symbolic info is available")); + " symbolic info is available"), + cl::cat(BCAnalyzerCategory)); static cl::opt<std::string> BlockInfoFilename("block-info", - cl::desc("Use the BLOCK_INFO from the given file")); + cl::desc("Use the BLOCK_INFO from the given file"), + cl::cat(BCAnalyzerCategory)); static cl::opt<bool> ShowBinaryBlobs("show-binary-blobs", - cl::desc("Print binary blobs using hex escapes")); + cl::desc("Print binary blobs using hex escapes"), + cl::cat(BCAnalyzerCategory)); static cl::opt<std::string> CheckHash( "check-hash", - cl::desc("Check module hash using the argument as a string table")); + cl::desc("Check module hash using the argument as a string table"), + cl::cat(BCAnalyzerCategory)); static Error reportError(StringRef Message) { return createStringError(std::errc::illegal_byte_sequence, Message.data()); @@ -85,6 +96,8 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); + + cl::HideUnrelatedOptions({&BCAnalyzerCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm-bcanalyzer file analyzer\n"); ExitOnError ExitOnErr("llvm-bcanalyzer: ");
diff --git a/src/llvm-project/llvm/tools/llvm-c-test/echo.cpp b/src/llvm-project/llvm/tools/llvm-c-test/echo.cpp index a29f360..d54adeb 100644 --- a/src/llvm-project/llvm/tools/llvm-c-test/echo.cpp +++ b/src/llvm-project/llvm/tools/llvm-c-test/echo.cpp
@@ -18,6 +18,7 @@ #include "llvm-c/DebugInfo.h" #include "llvm-c/Target.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/ErrorHandling.h" #include <stdio.h> @@ -653,6 +654,7 @@ LLVMAtomicOrdering Ord = LLVMGetOrdering(Src); LLVMBool SingleThread = LLVMIsAtomicSingleThread(Src); Dst = LLVMBuildAtomicRMW(Builder, BinOp, Ptr, Val, Ord, SingleThread); + LLVMSetAlignment(Dst, LLVMGetAlignment(Src)); LLVMSetVolatile(Dst, LLVMGetVolatile(Src)); LLVMSetValueName2(Dst, Name, NameLen); break; @@ -667,6 +669,7 @@ Dst = LLVMBuildAtomicCmpXchg(Builder, Ptr, Cmp, New, Succ, Fail, SingleThread); + LLVMSetAlignment(Dst, LLVMGetAlignment(Src)); LLVMSetVolatile(Dst, LLVMGetVolatile(Src)); LLVMSetWeak(Dst, LLVMGetWeak(Src)); LLVMSetValueName2(Dst, Name, NameLen);
diff --git a/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt index 41d55ed..679bffe 100644 --- a/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt
@@ -1,16 +1,4 @@ -add_library(LLVMCFIVerify - STATIC - FileAnalysis.cpp - FileAnalysis.h - GraphBuilder.cpp - GraphBuilder.h - ) - -llvm_update_compile_flags(LLVMCFIVerify) -if (LLVM_LINK_LLVM_DYLIB) - set(libs LLVM) -else() - llvm_map_components_to_libnames(libs +set(LLVM_LINK_COMPONENTS DebugInfoDWARF MC MCParser @@ -18,6 +6,11 @@ Support Symbolize ) -endif() -target_link_libraries(LLVMCFIVerify ${libs}) -set_target_properties(LLVMCFIVerify PROPERTIES FOLDER "Libraries") + +add_llvm_library(LLVMCFIVerify + STATIC + FileAnalysis.cpp + FileAnalysis.h + GraphBuilder.cpp + GraphBuilder.h + )
diff --git a/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp index 100f5d8..e1bca0d 100644 --- a/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp +++ b/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
@@ -374,7 +374,9 @@ MCPU = ""; std::string ErrorString; - Symbolizer.reset(new LLVMSymbolizer()); + LLVMSymbolizer::Options Opt; + Opt.UseSymbolTable = false; + Symbolizer.reset(new LLVMSymbolizer(Opt)); ObjectTarget = TargetRegistry::lookupTarget(ArchName, ObjectTriple, ErrorString); @@ -405,7 +407,8 @@ if (!MII) return make_error<UnsupportedDisassembly>("Failed to initialise MII."); - Context.reset(new MCContext(AsmInfo.get(), RegisterInfo.get(), &MOFI)); + Context.reset(new MCContext(Triple(TripleName), AsmInfo.get(), + RegisterInfo.get(), SubtargetInfo.get())); Disassembler.reset( ObjectTarget->createMCDisassembler(*SubtargetInfo, *Context));
diff --git a/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h b/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h index 27135c0..b256f7e 100644 --- a/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h +++ b/src/llvm-project/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
@@ -207,7 +207,6 @@ std::unique_ptr<const MCAsmInfo> AsmInfo; std::unique_ptr<MCSubtargetInfo> SubtargetInfo; std::unique_ptr<const MCInstrInfo> MII; - MCObjectFileInfo MOFI; std::unique_ptr<MCContext> Context; std::unique_ptr<const MCDisassembler> Disassembler; std::unique_ptr<const MCInstrAnalysis> MIA;
diff --git a/src/llvm-project/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp b/src/llvm-project/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp index 1331442..3cb0e84 100644 --- a/src/llvm-project/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp +++ b/src/llvm-project/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
@@ -32,21 +32,24 @@ using namespace llvm::object; using namespace llvm::cfi_verify; +static cl::OptionCategory CFIVerifyCategory("CFI Verify Options"); + cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"), - cl::Required); + cl::Required, cl::cat(CFIVerifyCategory)); cl::opt<std::string> BlacklistFilename(cl::Positional, cl::desc("[blacklist file]"), - cl::init("-")); + cl::init("-"), + cl::cat(CFIVerifyCategory)); cl::opt<bool> PrintGraphs( "print-graphs", cl::desc("Print graphs around indirect CF instructions in DOT format."), - cl::init(false)); + cl::init(false), cl::cat(CFIVerifyCategory)); cl::opt<unsigned> PrintBlameContext( "blame-context", cl::desc("Print the blame context (if possible) for BAD instructions. This " "specifies the number of lines of context to include, where zero " "disables this feature."), - cl::init(0)); + cl::init(0), cl::cat(CFIVerifyCategory)); cl::opt<unsigned> PrintBlameContextAll( "blame-context-all", cl::desc("Prints the blame context (if possible) for ALL instructions. " @@ -54,9 +57,9 @@ "instructions (see --blame-context). If --blame-context is " "unspecified, it prints this number of contextual lines for BAD " "instructions as well."), - cl::init(0)); + cl::init(0), cl::cat(CFIVerifyCategory)); cl::opt<bool> Summarize("summarize", cl::desc("Print the summary only."), - cl::init(false)); + cl::init(false), cl::cat(CFIVerifyCategory)); ExitOnError ExitOnErr; @@ -245,6 +248,7 @@ } int main(int argc, char **argv) { + cl::HideUnrelatedOptions({&CFIVerifyCategory, &getColorCategory()}); cl::ParseCommandLineOptions( argc, argv, "Identifies whether Control Flow Integrity protects all indirect control "
diff --git a/src/llvm-project/llvm/tools/llvm-cov/CodeCoverage.cpp b/src/llvm-project/llvm/tools/llvm-cov/CodeCoverage.cpp index baa9688..02c0106 100644 --- a/src/llvm-project/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/src/llvm-project/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -80,6 +80,12 @@ /// directory, recursively collect all of the paths within the directory. void collectPaths(const std::string &Path); + /// Check if the two given files are the same file. + bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2); + + /// Retrieve a file status with a cache. + Optional<sys::fs::file_status> getFileStatus(StringRef FilePath); + /// Return a memory buffer for the given source file. ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); @@ -153,6 +159,9 @@ /// remapped to, when using -path-equivalence. Optional<std::pair<std::string, std::string>> PathRemapping; + /// File status cache used when finding the same file. + StringMap<Optional<sys::fs::file_status>> FileStatusCache; + /// The architecture the coverage mapping data targets. std::vector<StringRef> CoverageArches; @@ -239,6 +248,27 @@ } } +Optional<sys::fs::file_status> +CodeCoverageTool::getFileStatus(StringRef FilePath) { + auto It = FileStatusCache.try_emplace(FilePath); + auto &CachedStatus = It.first->getValue(); + if (!It.second) + return CachedStatus; + + sys::fs::file_status Status; + if (!sys::fs::status(FilePath, Status)) + CachedStatus = Status; + return CachedStatus; +} + +bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1, + StringRef FilePath2) { + auto Status1 = getFileStatus(FilePath1); + auto Status2 = getFileStatus(FilePath2); + return Status1.hasValue() && Status2.hasValue() && + sys::fs::equivalent(Status1.getValue(), Status2.getValue()); +} + ErrorOr<const MemoryBuffer &> CodeCoverageTool::getSourceFile(StringRef SourceFile) { // If we've remapped filenames, look up the real location for this file. @@ -249,7 +279,7 @@ SourceFile = Loc->second; } for (const auto &Files : LoadedSourceFiles) - if (sys::fs::equivalent(SourceFile, Files.first)) + if (isEquivalentFile(SourceFile, Files.first)) return *Files.second; auto Buffer = MemoryBuffer::getFile(SourceFile); if (auto EC = Buffer.getError()) { @@ -404,7 +434,8 @@ warning("profile data may be out of date - object is newer", ObjectFilename); auto CoverageOrErr = - CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches); + CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches, + ViewOpts.CompilationDirectory); if (Error E = CoverageOrErr.takeError()) { error("Failed to load coverage: " + toString(std::move(E)), join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); @@ -445,7 +476,7 @@ SmallString<128> NativePath; sys::path::native(Path, NativePath); sys::path::remove_dots(NativePath, true); - if (!sys::path::is_separator(NativePath.back())) + if (!NativePath.empty() && !sys::path::is_separator(NativePath.back())) NativePath += sys::path::get_separator(); return NativePath.c_str(); }; @@ -709,6 +740,10 @@ cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), cl::aliasopt(NumThreads)); + cl::opt<std::string> CompilationDirectory( + "compilation-dir", cl::init(""), + cl::desc("Directory used as a base for relative coverage mapping paths")); + auto commandLineParser = [&, this](int argc, const char **argv) -> int { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); ViewOpts.Debug = DebugDump; @@ -843,6 +878,7 @@ ViewOpts.ShowInstantiationSummary = InstantiationSummary; ViewOpts.ExportSummaryOnly = SummaryOnly; ViewOpts.NumThreads = NumThreads; + ViewOpts.CompilationDirectory = CompilationDirectory; return 0; };
diff --git a/src/llvm-project/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/src/llvm-project/llvm/tools/llvm-cov/CoverageExporterJson.cpp index d1446f3..d341abe 100644 --- a/src/llvm-project/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/src/llvm-project/llvm/tools/llvm-cov/CoverageExporterJson.cpp
@@ -123,8 +123,7 @@ // Recursively collect branches from nested expansions. auto NestedExpansions = ExpansionCoverage.getExpansions(); auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions); - Branches.insert(Branches.end(), NestedExBranches.begin(), - NestedExBranches.end()); + append_range(Branches, NestedExBranches); // Add branches from this level of expansion. auto ExBranches = ExpansionCoverage.getBranches();
diff --git a/src/llvm-project/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/src/llvm-project/llvm/tools/llvm-cov/CoverageExporterLcov.cpp index 99ca037..6cf5d92 100644 --- a/src/llvm-project/llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ b/src/llvm-project/llvm/tools/llvm-cov/CoverageExporterLcov.cpp
@@ -91,8 +91,7 @@ auto NestedExpansions = ExpansionCoverage.getExpansions(); auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions, ViewDepth + 1, SrcLine); - Branches.insert(Branches.end(), NestedExBranches.begin(), - NestedExBranches.end()); + append_range(Branches, NestedExBranches); // Add branches from this level of expansion. auto ExBranches = ExpansionCoverage.getBranches(); @@ -123,7 +122,7 @@ collectNestedBranches(Coverage, FileCoverage.getExpansions()); // Append Expansion Branches to Source Branches. - Branches.insert(Branches.end(), ExBranches.begin(), ExBranches.end()); + append_range(Branches, ExBranches); // Sort branches based on line number to ensure branches corresponding to the // same source line are counted together.
diff --git a/src/llvm-project/llvm/tools/llvm-cov/CoverageViewOptions.h b/src/llvm-project/llvm/tools/llvm-cov/CoverageViewOptions.h index eee4ba7..045fb17 100644 --- a/src/llvm-project/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/src/llvm-project/llvm/tools/llvm-cov/CoverageViewOptions.h
@@ -49,6 +49,7 @@ std::string ProjectTitle; std::string CreatedTimeStr; unsigned NumThreads; + std::string CompilationDirectory; /// Change the output's stream color if the colors are enabled. ColoredRawOstream colored_ostream(raw_ostream &OS,
diff --git a/src/llvm-project/llvm/tools/llvm-cov/TestingSupport.cpp b/src/llvm-project/llvm/tools/llvm-cov/TestingSupport.cpp index b99bd83..9c6b25f 100644 --- a/src/llvm-project/llvm/tools/llvm-cov/TestingSupport.cpp +++ b/src/llvm-project/llvm/tools/llvm-cov/TestingSupport.cpp
@@ -10,6 +10,7 @@ #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" #include <functional> @@ -47,7 +48,7 @@ // Look for the sections that we are interested in. int FoundSectionCount = 0; - SectionRef ProfileNames, CoverageMapping; + SectionRef ProfileNames, CoverageMapping, CoverageRecords; auto ObjFormat = OF->getTripleObjectFormat(); for (const auto &Section : OF->sections()) { StringRef Name; @@ -64,16 +65,20 @@ } else if (Name == llvm::getInstrProfSectionName( IPSK_covmap, ObjFormat, /*AddSegmentInfo=*/false)) { CoverageMapping = Section; + } else if (Name == llvm::getInstrProfSectionName( + IPSK_covfun, ObjFormat, /*AddSegmentInfo=*/false)) { + CoverageRecords = Section; } else continue; ++FoundSectionCount; } - if (FoundSectionCount != 2) + if (FoundSectionCount != 3) return 1; // Get the contents of the given sections. uint64_t ProfileNamesAddress = ProfileNames.getAddress(); StringRef CoverageMappingData; + StringRef CoverageRecordsData; StringRef ProfileNamesData; if (Expected<StringRef> E = CoverageMapping.getContents()) CoverageMappingData = *E; @@ -81,6 +86,12 @@ consumeError(E.takeError()); return 1; } + if (Expected<StringRef> E = CoverageRecords.getContents()) + CoverageRecordsData = *E; + else { + consumeError(E.takeError()); + return 1; + } if (Expected<StringRef> E = ProfileNames.getContents()) ProfileNamesData = *E; else { @@ -103,6 +114,10 @@ for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad) OS.write(uint8_t(0)); OS << CoverageMappingData; + // Coverage records data is expected to have an alignment of 8. + for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad) + OS.write(uint8_t(0)); + OS << CoverageRecordsData; return 0; }
diff --git a/src/llvm-project/llvm/tools/llvm-cov/gcov.cpp b/src/llvm-project/llvm/tools/llvm-cov/gcov.cpp index d42e7cd..9a1ebeb 100644 --- a/src/llvm-project/llvm/tools/llvm-cov/gcov.cpp +++ b/src/llvm-project/llvm/tools/llvm-cov/gcov.cpp
@@ -46,7 +46,8 @@ // Open .gcda and .gcda without requiring a NUL terminator. The concurrent // modification may nullify the NUL terminator condition. ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff = - MemoryBuffer::getFileOrSTDIN(GCNO, -1, /*RequiresNullTerminator=*/false); + MemoryBuffer::getFileOrSTDIN(GCNO, /*IsText=*/false, + /*RequiresNullTerminator=*/false); if (std::error_code EC = GCNO_Buff.getError()) { errs() << GCNO << ": " << EC.message() << "\n"; return; @@ -58,7 +59,8 @@ } ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff = - MemoryBuffer::getFileOrSTDIN(GCDA, -1, /*RequiresNullTerminator=*/false); + MemoryBuffer::getFileOrSTDIN(GCDA, /*IsText=*/false, + /*RequiresNullTerminator=*/false); if (std::error_code EC = GCDA_Buff.getError()) { if (EC != errc::no_such_file_or_directory) { errs() << GCDA << ": " << EC.message() << "\n";
diff --git a/src/llvm-project/llvm/tools/llvm-cov/llvm-cov.cpp b/src/llvm-project/llvm/tools/llvm-cov/llvm-cov.cpp index 172ec9f..0e320c0 100644 --- a/src/llvm-project/llvm/tools/llvm-cov/llvm-cov.cpp +++ b/src/llvm-project/llvm/tools/llvm-cov/llvm-cov.cpp
@@ -60,7 +60,7 @@ InitLLVM X(argc, argv); // If argv[0] is or ends with 'gcov', always be gcov compatible - if (sys::path::stem(argv[0]).endswith_lower("gcov")) + if (sys::path::stem(argv[0]).endswith_insensitive("gcov")) return gcovMain(argc, argv); // Check if we are invoking a specific tool command.
diff --git a/src/llvm-project/llvm/tools/llvm-cvtres/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-cvtres/CMakeLists.txt index e912030..0898318 100644 --- a/src/llvm-project/llvm/tools/llvm-cvtres/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-cvtres/CMakeLists.txt
@@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + BinaryFormat Object Option Support
diff --git a/src/llvm-project/llvm/tools/llvm-cvtres/llvm-cvtres.cpp b/src/llvm-project/llvm/tools/llvm-cvtres/llvm-cvtres.cpp index 11cfb46..24b3c65 100644 --- a/src/llvm-project/llvm/tools/llvm-cvtres/llvm-cvtres.cpp +++ b/src/llvm-project/llvm/tools/llvm-cvtres/llvm-cvtres.cpp
@@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/Binary.h" #include "llvm/Object/WindowsMachineFlag.h" #include "llvm/Object/WindowsResource.h" @@ -75,6 +76,14 @@ reportError(Twine(Input) + ": " + EC.message() + ".\n"); } +static void error(StringRef Input, Error EC) { + if (!EC) + return; + handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) { + reportError(Twine(Input) + ": " + EI.message() + ".\n"); + }); +} + static void error(Error EC) { if (!EC) return; @@ -95,6 +104,16 @@ return std::move(EC.get()); } +template <typename T> T error(StringRef Input, Expected<T> EC) { + if (!EC) + error(Input, EC.takeError()); + return std::move(EC.get()); +} + +template <typename T> T error(StringRef Input, ErrorOr<T> &&EC) { + return error(Input, errorOrToExpected(std::move(EC))); +} + int main(int Argc, const char **Argv) { InitLLVM X(Argc, Argv); @@ -104,7 +123,7 @@ opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); if (InputArgs.hasArg(OPT_HELP)) { - T.PrintHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter"); + T.printHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter"); return 0; } @@ -155,15 +174,17 @@ WindowsResourceParser Parser; for (const auto &File : InputFiles) { - Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File); - if (!BinaryOrErr) - reportError(File, errorToErrorCode(BinaryOrErr.takeError())); - - Binary &Binary = *BinaryOrErr.get().getBinary(); - - WindowsResource *RF = dyn_cast<WindowsResource>(&Binary); - if (!RF) + std::unique_ptr<MemoryBuffer> Buffer = error( + File, MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false, + /*RequiresNullTerminator=*/false)); + file_magic Type = identify_magic(Buffer->getMemBufferRef().getBuffer()); + if (Type != file_magic::windows_resource) reportError(File + ": unrecognized file format.\n"); + std::unique_ptr<WindowsResource> Binary = error( + File, + WindowsResource::createWindowsResource(Buffer->getMemBufferRef())); + + WindowsResource *RF = Binary.get(); if (Verbose) { int EntryNumber = 0; @@ -199,12 +220,14 @@ error(FileBuffer->commit()); if (Verbose) { - Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(OutputFile); - if (!BinaryOrErr) - reportError(OutputFile, errorToErrorCode(BinaryOrErr.takeError())); - Binary &Binary = *BinaryOrErr.get().getBinary(); + std::unique_ptr<MemoryBuffer> Buffer = + error(OutputFile, + MemoryBuffer::getFileOrSTDIN(OutputFile, /*IsText=*/false, + /*RequiresNullTerminator=*/false)); + ScopedPrinter W(errs()); - W.printBinaryBlock("Output File Raw Data", Binary.getData()); + W.printBinaryBlock("Output File Raw Data", + Buffer->getMemBufferRef().getBuffer()); } return 0;
diff --git a/src/llvm-project/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp b/src/llvm-project/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp index 03e1bab..f214288 100644 --- a/src/llvm-project/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp +++ b/src/llvm-project/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp
@@ -33,9 +33,10 @@ using namespace llvm::support; namespace opts { +cl::OptionCategory CXXDumpCategory("CXX Dump Options"); cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input object files>"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(CXXDumpCategory)); } // namespace opts namespace llvm { @@ -549,6 +550,7 @@ // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + cl::HideUnrelatedOptions({&opts::CXXDumpCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "LLVM C++ ABI Data Dumper\n"); // Default to stdin if no filename is specified.
diff --git a/src/llvm-project/llvm/tools/llvm-cxxfilt/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-cxxfilt/CMakeLists.txt index 2a78aca..07ba9f9 100644 --- a/src/llvm-project/llvm/tools/llvm-cxxfilt/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-cxxfilt/CMakeLists.txt
@@ -1,10 +1,18 @@ set(LLVM_LINK_COMPONENTS Demangle + Option Support ) +set(LLVM_TARGET_DEFINITIONS Opts.td) +tablegen(LLVM Opts.inc -gen-opt-parser-defs) +add_public_tablegen_target(CxxfiltOptsTableGen) + add_llvm_tool(llvm-cxxfilt llvm-cxxfilt.cpp + + DEPENDS + CxxfiltOptsTableGen ) if(LLVM_INSTALL_BINUTILS_SYMLINKS)
diff --git a/src/llvm-project/llvm/tools/llvm-cxxfilt/Opts.td b/src/llvm-project/llvm/tools/llvm-cxxfilt/Opts.td new file mode 100644 index 0000000..93f8652 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-cxxfilt/Opts.td
@@ -0,0 +1,28 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass BB<string name, string help1, string help2> { + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; +} + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, + HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def help : FF<"help", "Display this help">; +defm strip_underscore : BB<"strip-underscore", "Strip the leading underscore", "Don't strip the leading underscore">; +def types : FF<"types", "">; +def version : FF<"version", "Display the version">; + +defm : Eq<"format", "Specify mangling format. Currently ignored because only 'gnu' is supported">; +def : F<"s", "Alias for --format">; + +def : F<"_", "Alias for --strip-underscore">, Alias<strip_underscore>; +def : F<"h", "Alias for --help">, Alias<help>; +def : F<"n", "Alias for --no-strip-underscore">, Alias<no_strip_underscore>; +def : F<"t", "Alias for --types">, Alias<types>;
diff --git a/src/llvm-project/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/src/llvm-project/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp index 93d6322..d8bf8db 100644 --- a/src/llvm-project/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp +++ b/src/llvm-project/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
@@ -9,69 +9,59 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <cstdlib> #include <iostream> using namespace llvm; -enum Style { - Auto, ///< auto-detect mangling - GNU, ///< GNU - Lucid, ///< Lucid compiler (lcc) - ARM, - HP, ///< HP compiler (xCC) - EDG, ///< EDG compiler - GNUv3, ///< GNU C++ v3 ABI - Java, ///< Java (gcj) - GNAT ///< ADA compiler (gnat) +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION }; -static cl::opt<Style> - Format("format", cl::desc("decoration style"), - cl::values(clEnumValN(Auto, "auto", "auto-detect style"), - clEnumValN(GNU, "gnu", "GNU (itanium) style")), - cl::init(Auto)); -static cl::alias FormatShort("s", cl::desc("alias for --format"), - cl::aliasopt(Format)); -static cl::opt<bool> StripUnderscore("strip-underscore", - cl::desc("strip the leading underscore"), - cl::init(false)); -static cl::alias StripUnderscoreShort("_", - cl::desc("alias for --strip-underscore"), - cl::aliasopt(StripUnderscore)); -static cl::opt<bool> - NoStripUnderscore("no-strip-underscore", - cl::desc("do not strip the leading underscore"), - cl::init(false)); -static cl::alias - NoStripUnderscoreShort("n", cl::desc("alias for --no-strip-underscore"), - cl::aliasopt(NoStripUnderscore)); +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX -static cl::opt<bool> - Types("types", - cl::desc("attempt to demangle types as well as function names"), - cl::init(false)); -static cl::alias TypesShort("t", cl::desc("alias for --types"), - cl::aliasopt(Types)); +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; -static cl::list<std::string> -Decorated(cl::Positional, cl::desc("<mangled>"), cl::ZeroOrMore); +class CxxfiltOptTable : public opt::OptTable { +public: + CxxfiltOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; +} // namespace -static cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); +static bool StripUnderscore; +static bool Types; -static bool shouldStripUnderscore() { - if (StripUnderscore) - return true; - if (NoStripUnderscore) - return false; - // If none of them are set, use the default value for platform. - // macho has symbols prefix with "_" so strip by default. - return Triple(sys::getProcessTriple()).isOSBinFormatMachO(); +static StringRef ToolName; + +static void error(const Twine &Message) { + WithColor::error(errs(), ToolName) << Message << '\n'; + exit(1); } static std::string demangle(const std::string &Mangled) { @@ -79,7 +69,7 @@ std::string Prefix; const char *DecoratedStr = Mangled.c_str(); - if (shouldStripUnderscore()) + if (StripUnderscore) if (DecoratedStr[0] == '_') ++DecoratedStr; size_t DecoratedLength = strlen(DecoratedStr); @@ -97,6 +87,11 @@ Undecorated = itaniumDemangle(DecoratedStr + 6, nullptr, nullptr, &Status); } + if (!Undecorated && + (DecoratedLength >= 2 && strncmp(DecoratedStr, "_R", 2) == 0)) { + Undecorated = rustDemangle(DecoratedStr, nullptr, nullptr, &Status); + } + std::string Result(Undecorated ? Prefix + Undecorated : Mangled); free(Undecorated); return Result; @@ -154,9 +149,37 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); + BumpPtrAllocator A; + StringSaver Saver(A); + CxxfiltOptTable Tbl; + ToolName = argv[0]; + opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, + [&](StringRef Msg) { error(Msg); }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp(outs(), + (Twine(ToolName) + " [options] <mangled>").str().c_str(), + "LLVM symbol undecoration tool"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + outs() << ToolName << '\n'; + cl::PrintVersionMessage(); + return 0; + } - cl::ParseCommandLineOptions(argc, argv, "llvm symbol undecoration tool\n"); + // The default value depends on the default triple. Mach-O has symbols + // prefixed with "_", so strip by default. + if (opt::Arg *A = + Args.getLastArg(OPT_strip_underscore, OPT_no_strip_underscore)) + StripUnderscore = A->getOption().matches(OPT_strip_underscore); + else + StripUnderscore = Triple(sys::getProcessTriple()).isOSBinFormatMachO(); + Types = Args.hasArg(OPT_types); + + std::vector<std::string> Decorated = Args.getAllArgValues(OPT_INPUT); if (Decorated.empty()) for (std::string Mangled; std::getline(std::cin, Mangled);) demangleLine(llvm::outs(), Mangled, true);
diff --git a/src/llvm-project/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp b/src/llvm-project/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp index b53a636..1e18e37 100644 --- a/src/llvm-project/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp +++ b/src/llvm-project/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp
@@ -11,10 +11,11 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" @@ -24,23 +25,33 @@ using namespace llvm; +cl::OptionCategory CXXMapCategory("CXX Map Options"); + cl::opt<std::string> OldSymbolFile(cl::Positional, cl::Required, - cl::desc("<symbol-file>")); + cl::desc("<symbol-file>"), + cl::cat(CXXMapCategory)); cl::opt<std::string> NewSymbolFile(cl::Positional, cl::Required, - cl::desc("<symbol-file>")); + cl::desc("<symbol-file>"), + cl::cat(CXXMapCategory)); cl::opt<std::string> RemappingFile("remapping-file", cl::Required, - cl::desc("Remapping file")); -cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile)); + cl::desc("Remapping file"), + cl::cat(CXXMapCategory)); +cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile), + cl::cat(CXXMapCategory)); cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file")); -cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename)); + cl::init("-"), cl::desc("Output file"), + cl::cat(CXXMapCategory)); +cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename), + cl::cat(CXXMapCategory)); cl::opt<bool> WarnAmbiguous( "Wambiguous", - cl::desc("Warn on equivalent symbols in the output symbol list")); + cl::desc("Warn on equivalent symbols in the output symbol list"), + cl::cat(CXXMapCategory)); cl::opt<bool> WarnIncomplete( "Wincomplete", - cl::desc("Warn on input symbols missing from output symbol list")); + cl::desc("Warn on input symbols missing from output symbol list"), + cl::cat(CXXMapCategory)); static void warn(Twine Message, Twine Whence = "", std::string Hint = "") { @@ -130,6 +141,7 @@ int main(int argc, const char *argv[]) { InitLLVM X(argc, argv); + cl::HideUnrelatedOptions({&CXXMapCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "LLVM C++ mangled name remapper\n"); auto OldSymbolBufOrError = MemoryBuffer::getFileOrSTDIN(OldSymbolFile); @@ -145,7 +157,7 @@ exitWithErrorCode(RemappingBufOrError.getError(), RemappingFile); std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); if (EC) exitWithErrorCode(EC, OutputFilename);
diff --git a/src/llvm-project/llvm/tools/llvm-diff/DiffConsumer.cpp b/src/llvm-project/llvm/tools/llvm-diff/DiffConsumer.cpp index 6228ff2..a703f42 100644 --- a/src/llvm-project/llvm/tools/llvm-diff/DiffConsumer.cpp +++ b/src/llvm-project/llvm/tools/llvm-diff/DiffConsumer.cpp
@@ -17,34 +17,33 @@ using namespace llvm; -static void ComputeNumbering(Function *F, DenseMap<Value*,unsigned> &Numbering){ +static void ComputeNumbering(const Function *F, + DenseMap<const Value *, unsigned> &Numbering) { unsigned IN = 0; // Arguments get the first numbers. - for (Function::arg_iterator - AI = F->arg_begin(), AE = F->arg_end(); AI != AE; ++AI) - if (!AI->hasName()) - Numbering[&*AI] = IN++; + for (const auto &Arg : F->args()) + if (!Arg.hasName()) + Numbering[&Arg] = IN++; // Walk the basic blocks in order. - for (Function::iterator FI = F->begin(), FE = F->end(); FI != FE; ++FI) { - if (!FI->hasName()) - Numbering[&*FI] = IN++; + for (const auto &Func : *F) { + if (!Func.hasName()) + Numbering[&Func] = IN++; // Walk the instructions in order. - for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ++BI) + for (const auto &BB : Func) // void instructions don't get numbers. - if (!BI->hasName() && !BI->getType()->isVoidTy()) - Numbering[&*BI] = IN++; + if (!BB.hasName() && !BB.getType()->isVoidTy()) + Numbering[&BB] = IN++; } assert(!Numbering.empty() && "asked for numbering but numbering was no-op"); } - void Consumer::anchor() { } -void DiffConsumer::printValue(Value *V, bool isL) { +void DiffConsumer::printValue(const Value *V, bool isL) { if (V->hasName()) { out << (isa<GlobalValue>(V) ? '@' : '%') << V->getName(); return; @@ -99,16 +98,16 @@ // Extra newline between functions. if (Differences) out << "\n"; - Function *L = cast<Function>(I->L); - Function *R = cast<Function>(I->R); + const Function *L = cast<Function>(I->L); + const Function *R = cast<Function>(I->R); if (L->getName() != R->getName()) out << "in function " << L->getName() << " / " << R->getName() << ":\n"; else out << "in function " << L->getName() << ":\n"; } else if (isa<BasicBlock>(I->L)) { - BasicBlock *L = cast<BasicBlock>(I->L); - BasicBlock *R = cast<BasicBlock>(I->R); + const BasicBlock *L = cast<BasicBlock>(I->L); + const BasicBlock *R = cast<BasicBlock>(I->R); if (L->hasName() && R->hasName() && L->getName() == R->getName()) out << " in block %" << L->getName() << ":\n"; else { @@ -139,7 +138,7 @@ return Differences; } -void DiffConsumer::enterContext(Value *L, Value *R) { +void DiffConsumer::enterContext(const Value *L, const Value *R) { contexts.push_back(DiffContext(L, R)); Indent += 2; }
diff --git a/src/llvm-project/llvm/tools/llvm-diff/DiffConsumer.h b/src/llvm-project/llvm/tools/llvm-diff/DiffConsumer.h index 6cb8f2e..f7b2f24 100644 --- a/src/llvm-project/llvm/tools/llvm-diff/DiffConsumer.h +++ b/src/llvm-project/llvm/tools/llvm-diff/DiffConsumer.h
@@ -34,7 +34,7 @@ /// Right are IR "containers" of some sort which are being /// considered for structural equivalence: global variables, /// functions, blocks, instructions, etc. - virtual void enterContext(Value *Left, Value *Right) = 0; + virtual void enterContext(const Value *Left, const Value *Right) = 0; /// Record that a local context has been exited. virtual void exitContext() = 0; @@ -55,14 +55,14 @@ class DiffConsumer : public Consumer { private: struct DiffContext { - DiffContext(Value *L, Value *R) - : L(L), R(R), Differences(false), IsFunction(isa<Function>(L)) {} - Value *L; - Value *R; + DiffContext(const Value *L, const Value *R) + : L(L), R(R), Differences(false), IsFunction(isa<Function>(L)) {} + const Value *L; + const Value *R; bool Differences; bool IsFunction; - DenseMap<Value*,unsigned> LNumbering; - DenseMap<Value*,unsigned> RNumbering; + DenseMap<const Value *, unsigned> LNumbering; + DenseMap<const Value *, unsigned> RNumbering; }; raw_ostream &out; @@ -70,7 +70,7 @@ bool Differences; unsigned Indent; - void printValue(Value *V, bool isL); + void printValue(const Value *V, bool isL); void header(); void indent(); @@ -79,7 +79,7 @@ : out(errs()), Differences(false), Indent(0) {} bool hadDifferences() const; - void enterContext(Value *L, Value *R) override; + void enterContext(const Value *L, const Value *R) override; void exitContext() override; void log(StringRef text) override; void logf(const LogBuilder &Log) override;
diff --git a/src/llvm-project/llvm/tools/llvm-diff/DiffLog.cpp b/src/llvm-project/llvm/tools/llvm-diff/DiffLog.cpp index 6484197..d31a345 100644 --- a/src/llvm-project/llvm/tools/llvm-diff/DiffLog.cpp +++ b/src/llvm-project/llvm/tools/llvm-diff/DiffLog.cpp
@@ -24,18 +24,18 @@ StringRef LogBuilder::getFormat() const { return Format; } unsigned LogBuilder::getNumArguments() const { return Arguments.size(); } -Value *LogBuilder::getArgument(unsigned I) const { return Arguments[I]; } +const Value *LogBuilder::getArgument(unsigned I) const { return Arguments[I]; } DiffLogBuilder::~DiffLogBuilder() { consumer.logd(*this); } -void DiffLogBuilder::addMatch(Instruction *L, Instruction *R) { +void DiffLogBuilder::addMatch(const Instruction *L, const Instruction *R) { Diff.push_back(DiffRecord(L, R)); } -void DiffLogBuilder::addLeft(Instruction *L) { +void DiffLogBuilder::addLeft(const Instruction *L) { // HACK: VS 2010 has a bug in the stdlib that requires this. Diff.push_back(DiffRecord(L, DiffRecord::second_type(nullptr))); } -void DiffLogBuilder::addRight(Instruction *R) { +void DiffLogBuilder::addRight(const Instruction *R) { // HACK: VS 2010 has a bug in the stdlib that requires this. Diff.push_back(DiffRecord(DiffRecord::first_type(nullptr), R)); } @@ -46,5 +46,9 @@ return (Diff[I].first ? (Diff[I].second ? DC_match : DC_left) : DC_right); } -Instruction *DiffLogBuilder::getLeft(unsigned I) const { return Diff[I].first; } -Instruction *DiffLogBuilder::getRight(unsigned I) const { return Diff[I].second; } +const Instruction *DiffLogBuilder::getLeft(unsigned I) const { + return Diff[I].first; +} +const Instruction *DiffLogBuilder::getRight(unsigned I) const { + return Diff[I].second; +}
diff --git a/src/llvm-project/llvm/tools/llvm-diff/DiffLog.h b/src/llvm-project/llvm/tools/llvm-diff/DiffLog.h index 0c89524..d8b07b9 100644 --- a/src/llvm-project/llvm/tools/llvm-diff/DiffLog.h +++ b/src/llvm-project/llvm/tools/llvm-diff/DiffLog.h
@@ -34,7 +34,7 @@ /// might be initializing this format. StringRef Format; - SmallVector<Value*, 4> Arguments; + SmallVector<const Value *, 4> Arguments; public: LogBuilder(Consumer &c, StringRef Format) : consumer(&c), Format(Format) {} @@ -44,7 +44,7 @@ L.consumer = nullptr; } - LogBuilder &operator<<(Value *V) { + LogBuilder &operator<<(const Value *V) { Arguments.push_back(V); return *this; } @@ -53,12 +53,12 @@ StringRef getFormat() const; unsigned getNumArguments() const; - Value *getArgument(unsigned I) const; + const Value *getArgument(unsigned I) const; }; /// A temporary-object class for building up diff messages. class DiffLogBuilder { - typedef std::pair<Instruction*,Instruction*> DiffRecord; + typedef std::pair<const Instruction *, const Instruction *> DiffRecord; SmallVector<DiffRecord, 20> Diff; Consumer &consumer; @@ -67,15 +67,15 @@ DiffLogBuilder(Consumer &c) : consumer(c) {} ~DiffLogBuilder(); - void addMatch(Instruction *L, Instruction *R); + void addMatch(const Instruction *L, const Instruction *R); // HACK: VS 2010 has a bug in the stdlib that requires this. - void addLeft(Instruction *L); - void addRight(Instruction *R); + void addLeft(const Instruction *L); + void addRight(const Instruction *R); unsigned getNumLines() const; DiffChange getLineKind(unsigned I) const; - Instruction *getLeft(unsigned I) const; - Instruction *getRight(unsigned I) const; + const Instruction *getLeft(unsigned I) const; + const Instruction *getRight(unsigned I) const; }; }
diff --git a/src/llvm-project/llvm/tools/llvm-diff/DifferenceEngine.cpp b/src/llvm-project/llvm/tools/llvm-diff/DifferenceEngine.cpp index 64c0dc6..eb746cd 100644 --- a/src/llvm-project/llvm/tools/llvm-diff/DifferenceEngine.cpp +++ b/src/llvm-project/llvm/tools/llvm-diff/DifferenceEngine.cpp
@@ -113,22 +113,29 @@ class FunctionDifferenceEngine { DifferenceEngine &Engine; + // Some initializers may reference the variable we're currently checking. This + // can cause an infinite loop. The Saved[LR]HS ivars can be checked to prevent + // recursing. + const Value *SavedLHS; + const Value *SavedRHS; + /// The current mapping from old local values to new local values. - DenseMap<Value*, Value*> Values; + DenseMap<const Value *, const Value *> Values; /// The current mapping from old blocks to new blocks. - DenseMap<BasicBlock*, BasicBlock*> Blocks; + DenseMap<const BasicBlock *, const BasicBlock *> Blocks; - DenseSet<std::pair<Value*, Value*> > TentativeValues; + DenseSet<std::pair<const Value *, const Value *>> TentativeValues; - unsigned getUnprocPredCount(BasicBlock *Block) const { + unsigned getUnprocPredCount(const BasicBlock *Block) const { unsigned Count = 0; - for (pred_iterator I = pred_begin(Block), E = pred_end(Block); I != E; ++I) + for (const_pred_iterator I = pred_begin(Block), E = pred_end(Block); I != E; + ++I) if (!Blocks.count(*I)) Count++; return Count; } - typedef std::pair<BasicBlock*, BasicBlock*> BlockPair; + typedef std::pair<const BasicBlock *, const BasicBlock *> BlockPair; /// A type which sorts a priority queue by the number of unprocessed /// predecessor blocks it has remaining. @@ -138,7 +145,7 @@ const FunctionDifferenceEngine &fde; explicit QueueSorter(const FunctionDifferenceEngine &fde) : fde(fde) {} - bool operator()(const BlockPair &Old, const BlockPair &New) { + bool operator()(BlockPair &Old, BlockPair &New) { return fde.getUnprocPredCount(Old.first) < fde.getUnprocPredCount(New.first); } @@ -151,8 +158,8 @@ /// if they haven't already been processed. /// /// Returns true if there was a problem unifying them. - bool tryUnify(BasicBlock *L, BasicBlock *R) { - BasicBlock *&Ref = Blocks[L]; + bool tryUnify(const BasicBlock *L, const BasicBlock *R) { + const BasicBlock *&Ref = Blocks[L]; if (Ref) { if (Ref == R) return false; @@ -167,10 +174,10 @@ Queue.insert(BlockPair(L, R)); return false; } - + /// Unifies two instructions, given that they're known not to have /// structural differences. - void unify(Instruction *L, Instruction *R) { + void unify(const Instruction *L, const Instruction *R) { DifferenceEngine::Context C(Engine, L, R); bool Result = diff(L, R, true, true); @@ -187,15 +194,15 @@ } } - void diff(BasicBlock *L, BasicBlock *R) { + void diff(const BasicBlock *L, const BasicBlock *R) { DifferenceEngine::Context C(Engine, L, R); - BasicBlock::iterator LI = L->begin(), LE = L->end(); - BasicBlock::iterator RI = R->begin(); + BasicBlock::const_iterator LI = L->begin(), LE = L->end(); + BasicBlock::const_iterator RI = R->begin(); do { assert(LI != LE && RI != R->end()); - Instruction *LeftI = &*LI, *RightI = &*RI; + const Instruction *LeftI = &*LI, *RightI = &*RI; // If the instructions differ, start the more sophisticated diff // algorithm at the start of the block. @@ -219,10 +226,11 @@ unify(&*LI, &*RI); } - bool matchForBlockDiff(Instruction *L, Instruction *R); - void runBlockDiff(BasicBlock::iterator LI, BasicBlock::iterator RI); + bool matchForBlockDiff(const Instruction *L, const Instruction *R); + void runBlockDiff(BasicBlock::const_iterator LI, + BasicBlock::const_iterator RI); - bool diffCallSites(CallBase &L, CallBase &R, bool Complain) { + bool diffCallSites(const CallBase &L, const CallBase &R, bool Complain) { // FIXME: call attributes if (!equivalentAsOperands(L.getCalledOperand(), R.getCalledOperand())) { if (Complain) Engine.log("called functions differ"); @@ -242,7 +250,8 @@ return false; } - bool diff(Instruction *L, Instruction *R, bool Complain, bool TryUnify) { + bool diff(const Instruction *L, const Instruction *R, bool Complain, + bool TryUnify) { // FIXME: metadata (if Complain is set) // Different opcodes always imply different operations. @@ -273,8 +282,8 @@ // Terminators. } else if (isa<InvokeInst>(L)) { - InvokeInst &LI = cast<InvokeInst>(*L); - InvokeInst &RI = cast<InvokeInst>(*R); + const InvokeInst &LI = cast<InvokeInst>(*L); + const InvokeInst &RI = cast<InvokeInst>(*R); if (diffCallSites(LI, RI, Complain)) return true; @@ -284,9 +293,30 @@ } return false; + } else if (isa<CallBrInst>(L)) { + const CallBrInst &LI = cast<CallBrInst>(*L); + const CallBrInst &RI = cast<CallBrInst>(*R); + if (LI.getNumIndirectDests() != RI.getNumIndirectDests()) { + if (Complain) + Engine.log("callbr # of indirect destinations differ"); + return true; + } + + // Perform the "try unify" step so that we can equate the indirect + // destinations before checking the call site. + for (unsigned I = 0; I < LI.getNumIndirectDests(); I++) + tryUnify(LI.getIndirectDest(I), RI.getIndirectDest(I)); + + if (diffCallSites(LI, RI, Complain)) + return true; + + if (TryUnify) + tryUnify(LI.getDefaultDest(), RI.getDefaultDest()); + return false; + } else if (isa<BranchInst>(L)) { - BranchInst *LI = cast<BranchInst>(L); - BranchInst *RI = cast<BranchInst>(R); + const BranchInst *LI = cast<BranchInst>(L); + const BranchInst *RI = cast<BranchInst>(R); if (LI->isConditional() != RI->isConditional()) { if (Complain) Engine.log("branch conditionality differs"); return true; @@ -303,8 +333,8 @@ return false; } else if (isa<IndirectBrInst>(L)) { - IndirectBrInst *LI = cast<IndirectBrInst>(L); - IndirectBrInst *RI = cast<IndirectBrInst>(R); + const IndirectBrInst *LI = cast<IndirectBrInst>(L); + const IndirectBrInst *RI = cast<IndirectBrInst>(R); if (LI->getNumDestinations() != RI->getNumDestinations()) { if (Complain) Engine.log("indirectbr # of destinations differ"); return true; @@ -323,8 +353,8 @@ return false; } else if (isa<SwitchInst>(L)) { - SwitchInst *LI = cast<SwitchInst>(L); - SwitchInst *RI = cast<SwitchInst>(R); + const SwitchInst *LI = cast<SwitchInst>(L); + const SwitchInst *RI = cast<SwitchInst>(R); if (!equivalentAsOperands(LI->getCondition(), RI->getCondition())) { if (Complain) Engine.log("switch conditions differ"); return true; @@ -333,13 +363,13 @@ bool Difference = false; - DenseMap<ConstantInt*,BasicBlock*> LCases; + DenseMap<const ConstantInt *, const BasicBlock *> LCases; for (auto Case : LI->cases()) LCases[Case.getCaseValue()] = Case.getCaseSuccessor(); for (auto Case : RI->cases()) { - ConstantInt *CaseValue = Case.getCaseValue(); - BasicBlock *LCase = LCases[CaseValue]; + const ConstantInt *CaseValue = Case.getCaseValue(); + const BasicBlock *LCase = LCases[CaseValue]; if (LCase) { if (TryUnify) tryUnify(LCase, Case.getCaseSuccessor()); @@ -351,8 +381,10 @@ } } if (!Difference) - for (DenseMap<ConstantInt*,BasicBlock*>::iterator - I = LCases.begin(), E = LCases.end(); I != E; ++I) { + for (DenseMap<const ConstantInt *, const BasicBlock *>::iterator + I = LCases.begin(), + E = LCases.end(); + I != E; ++I) { if (Complain) Engine.logf("left switch has extra case %l") << I->first; Difference = true; @@ -378,14 +410,15 @@ return false; } - bool equivalentAsOperands(Constant *L, Constant *R) { +public: + bool equivalentAsOperands(const Constant *L, const Constant *R) { // Use equality as a preliminary filter. if (L == R) return true; if (L->getValueID() != R->getValueID()) return false; - + // Ask the engine about global values. if (isa<GlobalValue>(L)) return Engine.equivalentAsOperands(cast<GlobalValue>(L), @@ -409,8 +442,8 @@ // If L and R are ConstantVectors, compare each element if (isa<ConstantVector>(L)) { - ConstantVector *CVL = cast<ConstantVector>(L); - ConstantVector *CVR = cast<ConstantVector>(R); + const ConstantVector *CVL = cast<ConstantVector>(L); + const ConstantVector *CVR = cast<ConstantVector>(R); if (CVL->getType()->getNumElements() != CVR->getType()->getNumElements()) return false; for (unsigned i = 0; i < CVL->getType()->getNumElements(); i++) { @@ -420,12 +453,68 @@ return true; } + // If L and R are ConstantArrays, compare the element count and types. + if (isa<ConstantArray>(L)) { + const ConstantArray *CAL = cast<ConstantArray>(L); + const ConstantArray *CAR = cast<ConstantArray>(R); + // Sometimes a type may be equivalent, but not uniquified---e.g. it may + // contain a GEP instruction. Do a deeper comparison of the types. + if (CAL->getType()->getNumElements() != CAR->getType()->getNumElements()) + return false; + + for (unsigned I = 0; I < CAL->getType()->getNumElements(); ++I) { + if (!equivalentAsOperands(CAL->getAggregateElement(I), + CAR->getAggregateElement(I))) + return false; + } + + return true; + } + + // If L and R are ConstantStructs, compare each field and type. + if (isa<ConstantStruct>(L)) { + const ConstantStruct *CSL = cast<ConstantStruct>(L); + const ConstantStruct *CSR = cast<ConstantStruct>(R); + + const StructType *LTy = cast<StructType>(CSL->getType()); + const StructType *RTy = cast<StructType>(CSR->getType()); + + // The StructTypes should have the same attributes. Don't use + // isLayoutIdentical(), because that just checks the element pointers, + // which may not work here. + if (LTy->getNumElements() != RTy->getNumElements() || + LTy->isPacked() != RTy->isPacked()) + return false; + + for (unsigned I = 0; I < LTy->getNumElements(); I++) { + const Value *LAgg = CSL->getAggregateElement(I); + const Value *RAgg = CSR->getAggregateElement(I); + + if (LAgg == SavedLHS || RAgg == SavedRHS) { + if (LAgg != SavedLHS || RAgg != SavedRHS) + // If the left and right operands aren't both re-analyzing the + // variable, then the initialiers don't match, so report "false". + // Otherwise, we skip these operands.. + return false; + + continue; + } + + if (!equivalentAsOperands(LAgg, RAgg)) { + return false; + } + } + + return true; + } + return false; } - bool equivalentAsOperands(ConstantExpr *L, ConstantExpr *R) { + bool equivalentAsOperands(const ConstantExpr *L, const ConstantExpr *R) { if (L == R) return true; + if (L->getOpcode() != R->getOpcode()) return false; @@ -447,14 +536,28 @@ if (L->getNumOperands() != R->getNumOperands()) return false; - for (unsigned I = 0, E = L->getNumOperands(); I != E; ++I) - if (!equivalentAsOperands(L->getOperand(I), R->getOperand(I))) + for (unsigned I = 0, E = L->getNumOperands(); I != E; ++I) { + const auto *LOp = L->getOperand(I); + const auto *ROp = R->getOperand(I); + + if (LOp == SavedLHS || ROp == SavedRHS) { + if (LOp != SavedLHS || ROp != SavedRHS) + // If the left and right operands aren't both re-analyzing the + // variable, then the initialiers don't match, so report "false". + // Otherwise, we skip these operands.. + return false; + + continue; + } + + if (!equivalentAsOperands(LOp, ROp)) return false; + } return true; } - bool equivalentAsOperands(Value *L, Value *R) { + bool equivalentAsOperands(const Value *L, const Value *R) { // Fall out if the values have different kind. // This possibly shouldn't take priority over oracles. if (L->getValueID() != R->getValueID()) @@ -483,17 +586,19 @@ FunctionDifferenceEngine *this_() { return this; } public: - FunctionDifferenceEngine(DifferenceEngine &Engine) : - Engine(Engine), Queue(QueueSorter(*this_())) {} + FunctionDifferenceEngine(DifferenceEngine &Engine, + const Value *SavedLHS = nullptr, + const Value *SavedRHS = nullptr) + : Engine(Engine), SavedLHS(SavedLHS), SavedRHS(SavedRHS), + Queue(QueueSorter(*this_())) {} - void diff(Function *L, Function *R) { + void diff(const Function *L, const Function *R) { if (L->arg_size() != R->arg_size()) Engine.log("different argument counts"); // Map the arguments. - for (Function::arg_iterator - LI = L->arg_begin(), LE = L->arg_end(), - RI = R->arg_begin(), RE = R->arg_end(); + for (Function::const_arg_iterator LI = L->arg_begin(), LE = L->arg_end(), + RI = R->arg_begin(), RE = R->arg_end(); LI != LE && RI != RE; ++LI, ++RI) Values[&*LI] = &*RI; @@ -509,15 +614,15 @@ llvm::SmallVector<char, 8> Path; // actually of DifferenceEngine::DiffChange }; -bool FunctionDifferenceEngine::matchForBlockDiff(Instruction *L, - Instruction *R) { +bool FunctionDifferenceEngine::matchForBlockDiff(const Instruction *L, + const Instruction *R) { return !diff(L, R, false, false); } -void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, - BasicBlock::iterator RStart) { - BasicBlock::iterator LE = LStart->getParent()->end(); - BasicBlock::iterator RE = RStart->getParent()->end(); +void FunctionDifferenceEngine::runBlockDiff(BasicBlock::const_iterator LStart, + BasicBlock::const_iterator RStart) { + BasicBlock::const_iterator LE = LStart->getParent()->end(); + BasicBlock::const_iterator RE = RStart->getParent()->end(); unsigned NL = std::distance(LStart, LE); @@ -540,14 +645,14 @@ Cur[I].Path.push_back(DC_left); } - for (BasicBlock::iterator RI = RStart; RI != RE; ++RI) { + for (BasicBlock::const_iterator RI = RStart; RI != RE; ++RI) { // Initialize the first row. Next[0] = Cur[0]; Next[0].Cost += RightCost; Next[0].Path.push_back(DC_right); unsigned Index = 1; - for (BasicBlock::iterator LI = LStart; LI != LE; ++LI, ++Index) { + for (BasicBlock::const_iterator LI = LStart; LI != LE; ++LI, ++Index) { if (matchForBlockDiff(&*LI, &*RI)) { Next[Index] = Cur[Index-1]; Next[Index].Cost += MatchCost; @@ -572,7 +677,7 @@ TentativeValues.clear(); SmallVectorImpl<char> &Path = Cur[NL].Path; - BasicBlock::iterator LI = LStart, RI = RStart; + BasicBlock::const_iterator LI = LStart, RI = RStart; DiffLogBuilder Diff(Engine.getConsumer()); @@ -595,7 +700,7 @@ case DC_match: assert(LI != LE && RI != RE); { - Instruction *L = &*LI, *R = &*RI; + const Instruction *L = &*LI, *R = &*RI; unify(L, R); Diff.addMatch(L, R); } @@ -628,16 +733,16 @@ // If the terminators have different kinds, but one is an invoke and the // other is an unconditional branch immediately following a call, unify // the results and the destinations. - Instruction *LTerm = LStart->getParent()->getTerminator(); - Instruction *RTerm = RStart->getParent()->getTerminator(); + const Instruction *LTerm = LStart->getParent()->getTerminator(); + const Instruction *RTerm = RStart->getParent()->getTerminator(); if (isa<BranchInst>(LTerm) && isa<InvokeInst>(RTerm)) { if (cast<BranchInst>(LTerm)->isConditional()) return; - BasicBlock::iterator I = LTerm->getIterator(); + BasicBlock::const_iterator I = LTerm->getIterator(); if (I == LStart->getParent()->begin()) return; --I; if (!isa<CallInst>(*I)) return; - CallInst *LCall = cast<CallInst>(&*I); - InvokeInst *RInvoke = cast<InvokeInst>(RTerm); + const CallInst *LCall = cast<CallInst>(&*I); + const InvokeInst *RInvoke = cast<InvokeInst>(RTerm); if (!equivalentAsOperands(LCall->getCalledOperand(), RInvoke->getCalledOperand())) return; @@ -646,12 +751,12 @@ tryUnify(LTerm->getSuccessor(0), RInvoke->getNormalDest()); } else if (isa<InvokeInst>(LTerm) && isa<BranchInst>(RTerm)) { if (cast<BranchInst>(RTerm)->isConditional()) return; - BasicBlock::iterator I = RTerm->getIterator(); + BasicBlock::const_iterator I = RTerm->getIterator(); if (I == RStart->getParent()->begin()) return; --I; if (!isa<CallInst>(*I)) return; - CallInst *RCall = cast<CallInst>(I); - InvokeInst *LInvoke = cast<InvokeInst>(LTerm); + const CallInst *RCall = cast<CallInst>(I); + const InvokeInst *LInvoke = cast<InvokeInst>(LTerm); if (!equivalentAsOperands(LInvoke->getCalledOperand(), RCall->getCalledOperand())) return; @@ -660,12 +765,11 @@ tryUnify(LInvoke->getNormalDest(), RTerm->getSuccessor(0)); } } - } void DifferenceEngine::Oracle::anchor() { } -void DifferenceEngine::diff(Function *L, Function *R) { +void DifferenceEngine::diff(const Function *L, const Function *R) { Context C(*this, L, R); // FIXME: types @@ -683,15 +787,15 @@ FunctionDifferenceEngine(*this).diff(L, R); } -void DifferenceEngine::diff(Module *L, Module *R) { +void DifferenceEngine::diff(const Module *L, const Module *R) { StringSet<> LNames; - SmallVector<std::pair<Function*,Function*>, 20> Queue; + SmallVector<std::pair<const Function *, const Function *>, 20> Queue; unsigned LeftAnonCount = 0; unsigned RightAnonCount = 0; - for (Module::iterator I = L->begin(), E = L->end(); I != E; ++I) { - Function *LFn = &*I; + for (Module::const_iterator I = L->begin(), E = L->end(); I != E; ++I) { + const Function *LFn = &*I; StringRef Name = LFn->getName(); if (Name.empty()) { ++LeftAnonCount; @@ -706,8 +810,8 @@ logf("function %l exists only in left module") << LFn; } - for (Module::iterator I = R->begin(), E = R->end(); I != E; ++I) { - Function *RFn = &*I; + for (Module::const_iterator I = R->begin(), E = R->end(); I != E; ++I) { + const Function *RFn = &*I; StringRef Name = RFn->getName(); if (Name.empty()) { ++RightAnonCount; @@ -718,7 +822,6 @@ logf("function %r exists only in right module") << RFn; } - if (LeftAnonCount != 0 || RightAnonCount != 0) { SmallString<32> Tmp; logf(("not comparing " + Twine(LeftAnonCount) + @@ -727,20 +830,24 @@ .toStringRef(Tmp)); } - for (SmallVectorImpl<std::pair<Function*,Function*> >::iterator - I = Queue.begin(), E = Queue.end(); I != E; ++I) + for (SmallVectorImpl<std::pair<const Function *, const Function *>>::iterator + I = Queue.begin(), + E = Queue.end(); + I != E; ++I) diff(I->first, I->second); } -bool DifferenceEngine::equivalentAsOperands(GlobalValue *L, GlobalValue *R) { +bool DifferenceEngine::equivalentAsOperands(const GlobalValue *L, + const GlobalValue *R) { if (globalValueOracle) return (*globalValueOracle)(L, R); if (isa<GlobalVariable>(L) && isa<GlobalVariable>(R)) { - GlobalVariable *GVL = cast<GlobalVariable>(L); - GlobalVariable *GVR = cast<GlobalVariable>(R); + const GlobalVariable *GVL = cast<GlobalVariable>(L); + const GlobalVariable *GVR = cast<GlobalVariable>(R); if (GVL->hasLocalLinkage() && GVL->hasUniqueInitializer() && GVR->hasLocalLinkage() && GVR->hasUniqueInitializer()) - return GVL->getInitializer() == GVR->getInitializer(); + return FunctionDifferenceEngine(*this, GVL, GVR) + .equivalentAsOperands(GVL->getInitializer(), GVR->getInitializer()); } return L->getName() == R->getName();
diff --git a/src/llvm-project/llvm/tools/llvm-diff/DifferenceEngine.h b/src/llvm-project/llvm/tools/llvm-diff/DifferenceEngine.h index da1b652..436a355 100644 --- a/src/llvm-project/llvm/tools/llvm-diff/DifferenceEngine.h +++ b/src/llvm-project/llvm/tools/llvm-diff/DifferenceEngine.h
@@ -33,7 +33,8 @@ public: /// A RAII object for recording the current context. struct Context { - Context(DifferenceEngine &Engine, Value *L, Value *R) : Engine(Engine) { + Context(DifferenceEngine &Engine, const Value *L, const Value *R) + : Engine(Engine) { Engine.consumer.enterContext(L, R); } @@ -50,7 +51,7 @@ class Oracle { virtual void anchor(); public: - virtual bool operator()(Value *L, Value *R) = 0; + virtual bool operator()(const Value *L, const Value *R) = 0; protected: virtual ~Oracle() {} @@ -59,8 +60,8 @@ DifferenceEngine(Consumer &consumer) : consumer(consumer), globalValueOracle(nullptr) {} - void diff(Module *L, Module *R); - void diff(Function *L, Function *R); + void diff(const Module *L, const Module *R); + void diff(const Function *L, const Function *R); void log(StringRef text) { consumer.log(text); } @@ -78,7 +79,7 @@ } /// Determines whether two global values are equivalent. - bool equivalentAsOperands(GlobalValue *L, GlobalValue *R); + bool equivalentAsOperands(const GlobalValue *L, const GlobalValue *R); private: Consumer &consumer;
diff --git a/src/llvm-project/llvm/tools/llvm-diff/llvm-diff.cpp b/src/llvm-project/llvm/tools/llvm-diff/llvm-diff.cpp index aaf7989..8a11179 100644 --- a/src/llvm-project/llvm/tools/llvm-diff/llvm-diff.cpp +++ b/src/llvm-project/llvm/tools/llvm-diff/llvm-diff.cpp
@@ -55,16 +55,20 @@ errs() << "No function named @" << Name << " in right module\n"; } +cl::OptionCategory DiffCategory("Diff Options"); + static cl::opt<std::string> LeftFilename(cl::Positional, - cl::desc("<first file>"), - cl::Required); + cl::desc("<first file>"), cl::Required, + cl::cat(DiffCategory)); static cl::opt<std::string> RightFilename(cl::Positional, cl::desc("<second file>"), - cl::Required); + cl::Required, cl::cat(DiffCategory)); static cl::list<std::string> GlobalsToCompare(cl::Positional, - cl::desc("<globals to compare>")); + cl::desc("<globals to compare>"), + cl::cat(DiffCategory)); int main(int argc, char **argv) { + cl::HideUnrelatedOptions({&DiffCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv); LLVMContext Context;
diff --git a/src/llvm-project/llvm/tools/llvm-dis/llvm-dis.cpp b/src/llvm-project/llvm/tools/llvm-dis/llvm-dis.cpp index 5d60946..62d165f 100644 --- a/src/llvm-project/llvm/tools/llvm-dis/llvm-dis.cpp +++ b/src/llvm-project/llvm/tools/llvm-dis/llvm-dis.cpp
@@ -35,37 +35,44 @@ #include <system_error> using namespace llvm; -static cl::opt<std::string> -InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); +static cl::OptionCategory DisCategory("Disassembler Options"); -static cl::opt<std::string> -OutputFilename("o", cl::desc("Override output filename"), - cl::value_desc("filename")); +static cl::list<std::string> InputFilenames(cl::Positional, cl::ZeroOrMore, + cl::desc("[input bitcode]..."), + cl::cat(DisCategory)); -static cl::opt<bool> -Force("f", cl::desc("Enable binary output on terminals")); +static cl::opt<std::string> OutputFilename("o", + cl::desc("Override output filename"), + cl::value_desc("filename"), + cl::cat(DisCategory)); -static cl::opt<bool> -DontPrint("disable-output", cl::desc("Don't output the .ll file"), cl::Hidden); +static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"), + cl::cat(DisCategory)); + +static cl::opt<bool> DontPrint("disable-output", + cl::desc("Don't output the .ll file"), + cl::Hidden, cl::cat(DisCategory)); static cl::opt<bool> SetImporting("set-importing", cl::desc("Set lazy loading to pretend to import a module"), - cl::Hidden); + cl::Hidden, cl::cat(DisCategory)); static cl::opt<bool> ShowAnnotations("show-annotations", - cl::desc("Add informational comments to the .ll file")); + cl::desc("Add informational comments to the .ll file"), + cl::cat(DisCategory)); static cl::opt<bool> PreserveAssemblyUseListOrder( "preserve-ll-uselistorder", cl::desc("Preserve use-list order when writing LLVM assembly."), - cl::init(false), cl::Hidden); + cl::init(false), cl::Hidden, cl::cat(DisCategory)); static cl::opt<bool> MaterializeMetadata("materialize-metadata", cl::desc("Load module without materializing metadata, " - "then materialize only the metadata")); + "then materialize only the metadata"), + cl::cat(DisCategory)); namespace { @@ -151,77 +158,89 @@ ExitOnErr.setBanner(std::string(argv[0]) + ": error: "); + cl::HideUnrelatedOptions({&DisCategory, &getColorCategory()}); + cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n"); + LLVMContext Context; Context.setDiagnosticHandler( std::make_unique<LLVMDisDiagnosticHandler>(argv[0])); - cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n"); - std::unique_ptr<MemoryBuffer> MB = - ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename))); + if (InputFilenames.size() < 1) { + InputFilenames.push_back("-"); + } else if (InputFilenames.size() > 1 && !OutputFilename.empty()) { + errs() + << "error: output file name cannot be set for multiple input files\n"; + return 1; + } - BitcodeFileContents IF = ExitOnErr(llvm::getBitcodeFileContents(*MB)); + for (std::string InputFilename : InputFilenames) { + std::unique_ptr<MemoryBuffer> MB = ExitOnErr( + errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename))); - const size_t N = IF.Mods.size(); + BitcodeFileContents IF = ExitOnErr(llvm::getBitcodeFileContents(*MB)); - if (OutputFilename == "-" && N > 1) + const size_t N = IF.Mods.size(); + + if (OutputFilename == "-" && N > 1) errs() << "only single module bitcode files can be written to stdout\n"; - for (size_t i = 0; i < N; ++i) { - BitcodeModule MB = IF.Mods[i]; - std::unique_ptr<Module> M = ExitOnErr(MB.getLazyModule(Context, MaterializeMetadata, - SetImporting)); - if (MaterializeMetadata) - ExitOnErr(M->materializeMetadata()); - else - ExitOnErr(M->materializeAll()); + for (size_t I = 0; I < N; ++I) { + BitcodeModule MB = IF.Mods[I]; + std::unique_ptr<Module> M = ExitOnErr( + MB.getLazyModule(Context, MaterializeMetadata, SetImporting)); + if (MaterializeMetadata) + ExitOnErr(M->materializeMetadata()); + else + ExitOnErr(M->materializeAll()); - BitcodeLTOInfo LTOInfo = ExitOnErr(MB.getLTOInfo()); - std::unique_ptr<ModuleSummaryIndex> Index; - if (LTOInfo.HasSummary) - Index = ExitOnErr(MB.getSummary()); + BitcodeLTOInfo LTOInfo = ExitOnErr(MB.getLTOInfo()); + std::unique_ptr<ModuleSummaryIndex> Index; + if (LTOInfo.HasSummary) + Index = ExitOnErr(MB.getSummary()); - std::string FinalFilename(OutputFilename); + std::string FinalFilename(OutputFilename); - // Just use stdout. We won't actually print anything on it. - if (DontPrint) - FinalFilename = "-"; - - if (FinalFilename.empty()) { // Unspecified output, infer it. - if (InputFilename == "-") { + // Just use stdout. We won't actually print anything on it. + if (DontPrint) FinalFilename = "-"; + + if (FinalFilename.empty()) { // Unspecified output, infer it. + if (InputFilename == "-") { + FinalFilename = "-"; + } else { + StringRef IFN = InputFilename; + FinalFilename = (IFN.endswith(".bc") ? IFN.drop_back(3) : IFN).str(); + if (N > 1) + FinalFilename += std::string(".") + std::to_string(I); + FinalFilename += ".ll"; + } } else { - StringRef IFN = InputFilename; - FinalFilename = (IFN.endswith(".bc") ? IFN.drop_back(3) : IFN).str(); if (N > 1) - FinalFilename += std::string(".") + std::to_string(i); - FinalFilename += ".ll"; + FinalFilename += std::string(".") + std::to_string(I); } - } else { - if (N > 1) - FinalFilename += std::string(".") + std::to_string(i); + + std::error_code EC; + std::unique_ptr<ToolOutputFile> Out( + new ToolOutputFile(FinalFilename, EC, sys::fs::OF_TextWithCRLF)); + if (EC) { + errs() << EC.message() << '\n'; + return 1; + } + + std::unique_ptr<AssemblyAnnotationWriter> Annotator; + if (ShowAnnotations) + Annotator.reset(new CommentWriter()); + + // All that llvm-dis does is write the assembly to a file. + if (!DontPrint) { + M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder); + if (Index) + Index->print(Out->os()); + } + + // Declare success. + Out->keep(); } - - std::error_code EC; - std::unique_ptr<ToolOutputFile> Out( - new ToolOutputFile(FinalFilename, EC, sys::fs::OF_Text)); - if (EC) { - errs() << EC.message() << '\n'; - return 1; - } - - std::unique_ptr<AssemblyAnnotationWriter> Annotator; - if (ShowAnnotations) - Annotator.reset(new CommentWriter()); - - // All that llvm-dis does is write the assembly to a file. - if (!DontPrint) { - M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder); - if (Index) - Index->print(Out->os()); - } - - // Declare success. - Out->keep(); } return 0;
diff --git a/src/llvm-project/llvm/tools/llvm-dwarfdump/SectionSizes.cpp b/src/llvm-project/llvm/tools/llvm-dwarfdump/SectionSizes.cpp index 8c456d5..731bf05 100644 --- a/src/llvm-project/llvm/tools/llvm-dwarfdump/SectionSizes.cpp +++ b/src/llvm-project/llvm/tools/llvm-dwarfdump/SectionSizes.cpp
@@ -18,10 +18,8 @@ const StringRef SectionNameTitle) { // The minimum column width should be the size of "SECTION". size_t Width = SectionNameTitle.size(); - for (const auto &DebugSec : Sizes.DebugSectionSizes) { - StringRef SectionName = DebugSec.getKey(); - Width = std::max(Width, SectionName.size()); - } + for (const auto &It : Sizes.DebugSectionSizes) + Width = std::max(Width, It.first.size()); return Width; } @@ -29,8 +27,8 @@ const StringRef SectionSizeTitle) { // The minimum column width should be the size of the column title. size_t Width = SectionSizeTitle.size(); - for (const auto &DebugSec : Sizes.DebugSectionSizes) { - size_t NumWidth = std::to_string(DebugSec.getValue()).size(); + for (const auto &It : Sizes.DebugSectionSizes) { + size_t NumWidth = std::to_string(It.second).size(); Width = std::max(Width, NumWidth); } return Width; @@ -59,13 +57,13 @@ OS << "-"; OS << '\n'; - for (const auto &DebugSec : Sizes.DebugSectionSizes) { - OS << left_justify(DebugSec.getKey(), NameColWidth) << " "; + for (const auto &It : Sizes.DebugSectionSizes) { + OS << left_justify(It.first, NameColWidth) << " "; - auto NumBytes = std::to_string(DebugSec.getValue()); + std::string NumBytes = std::to_string(It.second); OS << right_justify(NumBytes, SizeColWidth) << " (" - << format("%0.2f", DebugSec.getValue() / - static_cast<double>(Sizes.TotalObjectSize) * 100) + << format("%0.2f", + It.second / static_cast<double>(Sizes.TotalObjectSize) * 100) << "%)\n"; } @@ -95,11 +93,11 @@ LLVM_DEBUG(dbgs() << SectionName.str() << ": " << Section.getSize() << '\n'); - if (!Section.isDebugSection(SectionName)) + if (!Section.isDebugSection()) continue; Sizes.TotalDebugSectionsSize += Section.getSize(); - Sizes.DebugSectionSizes[SectionName] += Section.getSize(); + Sizes.DebugSectionSizes[std::string(SectionName)] += Section.getSize(); } }
diff --git a/src/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp b/src/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp index 82da06e..19a971a 100644 --- a/src/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/src/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp
@@ -8,9 +8,7 @@ #include "llvm-dwarfdump.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" -#include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Object/ObjectFile.h" @@ -21,13 +19,23 @@ using namespace llvm::dwarfdump; using namespace llvm::object; +namespace { /// This represents the number of categories of debug location coverage being /// calculated. The first category is the number of variables with 0% location /// coverage, but the last category is the number of variables with 100% /// location coverage. constexpr int NumOfCoverageCategories = 12; -namespace { +/// This is used for zero location coverage bucket. +constexpr unsigned ZeroCoverageBucket = 0; + +/// This represents variables DIE offsets. +using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>; +/// This maps function DIE offset to its variables. +using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>; +/// This represents function DIE offsets containing an abstract_origin. +using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>; + /// Holds statistics for one function (or other entity that has a PC range and /// contains variables, such as a compile unit). struct PerFunctionStats { @@ -164,12 +172,14 @@ }; unsigned CoverageBucket = getCoverageBucket(); + VarParamLocStats[CoverageBucket]++; if (IsParam) ParamLocStats[CoverageBucket]++; else if (IsLocalVar) LocalVarLocStats[CoverageBucket]++; } + /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName /// and DeclLine. The identifier aims to be unique for any unique entities, /// but keeping the same among different instances of the same entity. @@ -210,12 +220,18 @@ } /// Collect debug info quality metrics for one DIE. -static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, - std::string VarPrefix, uint64_t BytesInScope, - uint32_t InlineDepth, +static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix, + const std::string &VarPrefix, + uint64_t BytesInScope, uint32_t InlineDepth, StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats, - LocationStats &LocStats) { + LocationStats &LocStats, + AbstractOriginVarsTy *AbstractOriginVariables) { + const dwarf::Tag Tag = Die.getTag(); + // Skip CU node. + if (Tag == dwarf::DW_TAG_compile_unit) + return; + bool HasLoc = false; bool HasSrcLoc = false; bool HasType = false; @@ -223,19 +239,22 @@ uint64_t ScopeBytesCovered = 0; uint64_t BytesEntryValuesCovered = 0; auto &FnStats = FnStatMap[FnPrefix]; - bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter; - bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable; - bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member && + bool IsParam = Tag == dwarf::DW_TAG_formal_parameter; + bool IsLocalVar = Tag == dwarf::DW_TAG_variable; + bool IsConstantMember = Tag == dwarf::DW_TAG_member && Die.find(dwarf::DW_AT_const_value); - if (Die.getTag() == dwarf::DW_TAG_call_site || - Die.getTag() == dwarf::DW_TAG_GNU_call_site) { + // For zero covered inlined variables the locstats will be + // calculated later. + bool DeferLocStats = false; + + if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) { GlobalStats.CallSiteDIEs++; return; } - if (Die.getTag() == dwarf::DW_TAG_call_site_parameter || - Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) { + if (Tag == dwarf::DW_TAG_call_site_parameter || + Tag == dwarf::DW_TAG_GNU_call_site_parameter) { GlobalStats.CallSiteParamDIEs++; return; } @@ -256,6 +275,21 @@ if (Die.findRecursively(dwarf::DW_AT_type)) HasType = true; + if (Die.find(dwarf::DW_AT_abstract_origin)) { + if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) { + if (AbstractOriginVariables) { + auto Offset = Die.find(dwarf::DW_AT_abstract_origin); + // Do not track this variable any more, since it has location + // coverage. + llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue()); + } + } else { + // The locstats will be handled at the end of + // the collectStatsRecursive(). + DeferLocStats = true; + } + } + auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool { DWARFUnit *U = Die.getDwarfUnit(); DataExtractor Data(toStringRef(D), @@ -315,7 +349,7 @@ } // Calculate the debug location statistics. - if (BytesInScope) { + if (BytesInScope && !DeferLocStats) { LocStats.NumVarParam++; if (IsParam) LocStats.NumParam++; @@ -389,27 +423,73 @@ } } +/// Recursively collect variables from subprogram with DW_AT_inline attribute. +static void collectAbstractOriginFnInfo( + DWARFDie Die, uint64_t SPOffset, + AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo) { + DWARFDie Child = Die.getFirstChild(); + while (Child) { + const dwarf::Tag ChildTag = Child.getTag(); + if (ChildTag == dwarf::DW_TAG_formal_parameter || + ChildTag == dwarf::DW_TAG_variable) + GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset()); + else if (ChildTag == dwarf::DW_TAG_lexical_block) + collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo); + Child = Child.getSibling(); + } +} + /// Recursively collect debug info quality metrics. -static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, - std::string VarPrefix, uint64_t BytesInScope, - uint32_t InlineDepth, - StringMap<PerFunctionStats> &FnStatMap, - GlobalStats &GlobalStats, - LocationStats &LocStats) { +static void collectStatsRecursive( + DWARFDie Die, std::string FnPrefix, std::string VarPrefix, + uint64_t BytesInScope, uint32_t InlineDepth, + StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats, + LocationStats &LocStats, + AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, + FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed, + AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) { + // Skip NULL nodes. + if (Die.isNULL()) + return; + const dwarf::Tag Tag = Die.getTag(); // Skip function types. if (Tag == dwarf::DW_TAG_subroutine_type) return; // Handle any kind of lexical scope. + const bool HasAbstractOrigin = Die.find(dwarf::DW_AT_abstract_origin) != None; const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; - if (IsFunction || IsInlinedFunction || IsBlock) { + // We want to know how many variables (with abstract_origin) don't have + // location info. + const bool IsCandidateForZeroLocCovTracking = + (IsInlinedFunction || (IsFunction && HasAbstractOrigin)); + AbstractOriginVarsTy AbstractOriginVars; + + // Get the vars of the inlined fn, so the locstats + // reports the missing vars (with coverage 0%). + if (IsCandidateForZeroLocCovTracking) { + auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin); + if (OffsetFn) { + uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue(); + if (GlobalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) { + AbstractOriginVars = GlobalAbstractOriginFnInfo[OffsetOfInlineFnCopy]; + AbstractOriginVarsPtr = &AbstractOriginVars; + } else { + // This means that the DW_AT_inline fn copy is out of order, + // so this abstract origin instance will be processed later. + FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset()); + AbstractOriginVarsPtr = nullptr; + } + } + } + + if (IsFunction || IsInlinedFunction || IsBlock) { // Reset VarPrefix when entering a new function. - if (Die.getTag() == dwarf::DW_TAG_subprogram || - Die.getTag() == dwarf::DW_TAG_inlined_subroutine) + if (IsFunction || IsInlinedFunction) VarPrefix = "v"; // Ignore forward declarations. @@ -434,9 +514,15 @@ // Count the function. if (!IsBlock) { - // Skip over abstract origins. - if (Die.find(dwarf::DW_AT_inline)) + // Skip over abstract origins, but collect variables + // from it so it can be used for location statistics + // for inlined instancies. + if (Die.find(dwarf::DW_AT_inline)) { + uint64_t SPOffset = Die.getOffset(); + collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo); return; + } + std::string FnID = constructDieID(Die); // We've seen an instance of this function. auto &FnStats = FnStatMap[FnID]; @@ -465,7 +551,7 @@ } else { // Not a scope, visit the Die itself. It could be a variable. collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth, - FnStatMap, GlobalStats, LocStats); + FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr); } // Set InlineDepth correctly for child recursion @@ -485,10 +571,33 @@ if (Child.getTag() == dwarf::DW_TAG_formal_parameter) ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; - collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope, - InlineDepth, FnStatMap, GlobalStats, LocStats); + collectStatsRecursive( + Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap, + GlobalStats, LocStats, GlobalAbstractOriginFnInfo, + FnsWithAbstractOriginToBeProcessed, AbstractOriginVarsPtr); Child = Child.getSibling(); } + + if (!IsCandidateForZeroLocCovTracking) + return; + + // After we have processed all vars of the inlined function (or function with + // an abstract_origin), we want to know how many variables have no location. + for (auto Offset : AbstractOriginVars) { + LocStats.NumVarParam++; + LocStats.VarParamLocStats[ZeroCoverageBucket]++; + auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset); + if (!FnDie) + continue; + auto Tag = FnDie.getTag(); + if (Tag == dwarf::DW_TAG_formal_parameter) { + LocStats.NumParam++; + LocStats.ParamLocStats[ZeroCoverageBucket]++; + } else if (Tag == dwarf::DW_TAG_variable) { + LocStats.NumVar++; + LocStats.LocalVarLocStats[ZeroCoverageBucket]++; + } + } } /// Print human-readable output. @@ -536,9 +645,63 @@ } static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) { - for (const auto &DebugSec : Sizes.DebugSectionSizes) - J.attribute((Twine("#bytes in ") + DebugSec.getKey()).str(), - int64_t(DebugSec.getValue())); + for (const auto &It : Sizes.DebugSectionSizes) + J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second)); +} + +/// Stop tracking variables that contain abstract_origin with a location. +/// This is used for out-of-order DW_AT_inline subprograms only. +static void updateVarsWithAbstractOriginLocCovInfo( + DWARFDie FnDieWithAbstractOrigin, + AbstractOriginVarsTy &AbstractOriginVars) { + DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild(); + while (Child) { + const dwarf::Tag ChildTag = Child.getTag(); + if ((ChildTag == dwarf::DW_TAG_formal_parameter || + ChildTag == dwarf::DW_TAG_variable) && + (Child.find(dwarf::DW_AT_location) || + Child.find(dwarf::DW_AT_const_value))) { + auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin); + if (OffsetVar) + llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue()); + } else if (ChildTag == dwarf::DW_TAG_lexical_block) + updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars); + Child = Child.getSibling(); + } +} + +/// Collect zero location coverage for inlined variables which refer to +/// a DW_AT_inline copy of subprogram that is out of order in the DWARF. +/// Also cover the variables of a concrete function (represented with +/// the DW_TAG_subprogram) with an abstract_origin attribute. +static void collectZeroLocCovForVarsWithAbstractOrigin( + DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats, + AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, + FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) { + for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) { + DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset); + auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin); + AbstractOriginVarsTy AbstractOriginVars; + if (!FnCopy) + continue; + + AbstractOriginVars = GlobalAbstractOriginFnInfo[(*FnCopy).getRawUValue()]; + updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin, + AbstractOriginVars); + + for (auto Offset : AbstractOriginVars) { + LocStats.NumVarParam++; + LocStats.VarParamLocStats[ZeroCoverageBucket]++; + auto Tag = DwUnit->getDIEForOffset(Offset).getTag(); + if (Tag == dwarf::DW_TAG_formal_parameter) { + LocStats.NumParam++; + LocStats.ParamLocStats[ZeroCoverageBucket]++; + } else if (Tag == dwarf::DW_TAG_variable) { + LocStats.NumVar++; + LocStats.LocalVarLocStats[ZeroCoverageBucket]++; + } + } + } } /// \} @@ -558,10 +721,27 @@ GlobalStats GlobalStats; LocationStats LocStats; StringMap<PerFunctionStats> Statistics; - for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) - if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) + for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) { + if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) { + // These variables are being reset for each CU, since there could be + // a situation where we have two subprogram DIEs with the same offsets + // in two diferent CUs, and we can end up using wrong variables info + // when trying to resolve abstract_origin attribute. + // TODO: Handle LTO cases where the abstract origin of + // the function is in a different CU than the one it's + // referenced from or inlined into. + AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo; + FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed; + collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, - LocStats); + LocStats, GlobalAbstractOriginFnInfo, + FnsWithAbstractOriginToBeProcessed); + + collectZeroLocCovForVarsWithAbstractOrigin( + CUDie.getDwarfUnit(), GlobalStats, LocStats, + GlobalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed); + } + } /// Collect the sizes of debug sections. SectionSizes Sizes; @@ -570,7 +750,7 @@ /// The version number should be increased every time the algorithm is changed /// (including bug fixes). New metrics may be added without increasing the /// version. - unsigned Version = 6; + unsigned Version = 8; unsigned VarParamTotal = 0; unsigned VarParamUnique = 0; unsigned VarParamWithLoc = 0;
diff --git a/src/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/src/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index 4322f12..a324ff7 100644 --- a/src/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/src/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -130,7 +130,8 @@ static opt<bool> DumpAll("all", desc("Dump all debug info sections"), cat(SectionCategory)); -static alias DumpAllAlias("a", desc("Alias for -all"), aliasopt(DumpAll)); +static alias DumpAllAlias("a", desc("Alias for --all"), aliasopt(DumpAll), + cl::NotHidden); // Options for dumping specific sections. static unsigned DumpType = DIDT_Null; @@ -143,7 +144,8 @@ #include "llvm/BinaryFormat/Dwarf.def" #undef HANDLE_DWARF_SECTION -static alias DumpDebugFrameAlias("eh-frame", desc("Alias for -debug-frame"), +// The aliased DumpDebugFrame is created by the Dwarf.def x-macro just above. +static alias DumpDebugFrameAlias("eh-frame", desc("Alias for --debug-frame"), NotHidden, cat(SectionCategory), aliasopt(DumpDebugFrame)); static list<std::string> @@ -164,19 +166,21 @@ "accelerator tables are available, the slower but more complete " "-name option can be used instead."), value_desc("name"), cat(DwarfDumpCategory)); -static alias FindAlias("f", desc("Alias for -find."), aliasopt(Find)); +static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find), + cl::NotHidden); static opt<bool> IgnoreCase("ignore-case", desc("Ignore case distinctions when searching."), value_desc("i"), cat(DwarfDumpCategory)); -static alias IgnoreCaseAlias("i", desc("Alias for -ignore-case."), - aliasopt(IgnoreCase)); +static alias IgnoreCaseAlias("i", desc("Alias for --ignore-case."), + aliasopt(IgnoreCase), cl::NotHidden); static list<std::string> Name( "name", desc("Find and print all debug info entries whose name (DW_AT_name " "attribute) matches the exact text in <pattern>. When used with the " "the -regex option <pattern> is interpreted as a regular expression."), value_desc("pattern"), cat(DwarfDumpCategory)); -static alias NameAlias("n", desc("Alias for -name"), aliasopt(Name)); +static alias NameAlias("n", desc("Alias for --name"), aliasopt(Name), + cl::NotHidden); static opt<uint64_t> Lookup("lookup", desc("Lookup <address> in the debug information and print out any " @@ -193,34 +197,36 @@ desc("Treat any <pattern> strings as regular expressions when " "searching instead of just as an exact string match."), cat(DwarfDumpCategory)); -static alias RegexAlias("x", desc("Alias for -regex"), aliasopt(UseRegex)); +static alias RegexAlias("x", desc("Alias for --regex"), aliasopt(UseRegex), + cl::NotHidden); static opt<bool> ShowChildren("show-children", desc("Show a debug info entry's children when selectively " "printing entries."), cat(DwarfDumpCategory)); -static alias ShowChildrenAlias("c", desc("Alias for -show-children."), - aliasopt(ShowChildren)); +static alias ShowChildrenAlias("c", desc("Alias for --show-children."), + aliasopt(ShowChildren), cl::NotHidden); static opt<bool> ShowParents("show-parents", desc("Show a debug info entry's parents when selectively " "printing entries."), cat(DwarfDumpCategory)); -static alias ShowParentsAlias("p", desc("Alias for -show-parents."), - aliasopt(ShowParents)); +static alias ShowParentsAlias("p", desc("Alias for --show-parents."), + aliasopt(ShowParents), cl::NotHidden); static opt<bool> ShowForm("show-form", desc("Show DWARF form types after the DWARF attribute types."), cat(DwarfDumpCategory)); -static alias ShowFormAlias("F", desc("Alias for -show-form."), - aliasopt(ShowForm), cat(DwarfDumpCategory)); +static alias ShowFormAlias("F", desc("Alias for --show-form."), + aliasopt(ShowForm), cat(DwarfDumpCategory), + cl::NotHidden); static opt<unsigned> ChildRecurseDepth("recurse-depth", desc("Only recurse to a depth of N when displaying " "children of debug info entries."), cat(DwarfDumpCategory), init(-1U), value_desc("N")); -static alias ChildRecurseDepthAlias("r", desc("Alias for -recurse-depth."), - aliasopt(ChildRecurseDepth)); +static alias ChildRecurseDepthAlias("r", desc("Alias for --recurse-depth."), + aliasopt(ChildRecurseDepth), cl::NotHidden); static opt<unsigned> ParentRecurseDepth("parent-recurse-depth", desc("Only recurse to a depth of N when displaying " @@ -245,25 +251,30 @@ cat(DwarfDumpCategory)); static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."), cat(DwarfDumpCategory)); -static alias DumpUUIDAlias("u", desc("Alias for -uuid."), aliasopt(DumpUUID)); +static alias DumpUUIDAlias("u", desc("Alias for --uuid."), aliasopt(DumpUUID), + cl::NotHidden); static opt<bool> Verbose("verbose", desc("Print more low-level encoding details."), cat(DwarfDumpCategory)); -static alias VerboseAlias("v", desc("Alias for -verbose."), aliasopt(Verbose), - cat(DwarfDumpCategory)); +static alias VerboseAlias("v", desc("Alias for --verbose."), aliasopt(Verbose), + cat(DwarfDumpCategory), cl::NotHidden); static cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); } // namespace /// @} //===----------------------------------------------------------------------===// -static void error(StringRef Prefix, std::error_code EC) { - if (!EC) +static void error(StringRef Prefix, Error Err) { + if (!Err) return; - WithColor::error() << Prefix << ": " << EC.message() << "\n"; + WithColor::error() << Prefix << ": " << toString(std::move(Err)) << "\n"; exit(1); } +static void error(StringRef Prefix, std::error_code EC) { + error(Prefix, errorCodeToError(EC)); +} + static DIDumpOptions getDumpOpts(DWARFContext &C) { DIDumpOptions DumpOpts; DumpOpts.DumpType = DumpType; @@ -484,7 +495,7 @@ // fails. raw_ostream &stream = Quiet ? nulls() : OS; stream << "Verifying " << Filename.str() << ":\tfile format " - << Obj.getFileFormatName() << "\n"; + << Obj.getFileFormatName() << "\n"; bool Result = DICtx.verify(stream, getDumpOpts(DICtx)); if (Result) stream << "No errors.\n"; @@ -502,13 +513,13 @@ Error Err = Error::success(); for (auto Child : Arch.children(Err)) { auto BuffOrErr = Child.getMemoryBufferRef(); - error(Filename, errorToErrorCode(BuffOrErr.takeError())); + error(Filename, BuffOrErr.takeError()); auto NameOrErr = Child.getName(); - error(Filename, errorToErrorCode(NameOrErr.takeError())); + error(Filename, NameOrErr.takeError()); std::string Name = (Filename + "(" + NameOrErr.get() + ")").str(); Result &= handleBuffer(Name, BuffOrErr.get(), HandleObj, OS); } - error(Filename, errorToErrorCode(std::move(Err))); + error(Filename, std::move(Err)); return Result; } @@ -516,7 +527,7 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, HandlerFn HandleObj, raw_ostream &OS) { Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer); - error(Filename, errorToErrorCode(BinOrErr.takeError())); + error(Filename, BinOrErr.takeError()); bool Result = true; auto RecoverableErrorHandler = [&](Error E) { @@ -530,8 +541,7 @@ if (!HandleObj(*Obj, *DICtx, Filename, OS)) Result = false; } - } - else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) + } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) for (auto &ObjForArch : Fat->objects()) { std::string ObjName = (Filename + "(" + ObjForArch.getArchFlagName() + ")").str(); @@ -547,7 +557,7 @@ } else consumeError(MachOOrErr.takeError()); if (auto ArchiveOrErr = ObjForArch.getAsArchive()) { - error(ObjName, errorToErrorCode(ArchiveOrErr.takeError())); + error(ObjName, ArchiveOrErr.takeError()); if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS)) Result = false; continue; @@ -562,7 +572,7 @@ static bool handleFile(StringRef Filename, HandlerFn HandleObj, raw_ostream &OS) { ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = - MemoryBuffer::getFileOrSTDIN(Filename); + MemoryBuffer::getFileOrSTDIN(Filename); error(Filename, BuffOrErr.getError()); std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get()); return handleBuffer(Filename, *Buffer, HandleObj, OS); @@ -613,7 +623,8 @@ llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); - HideUnrelatedOptions({&DwarfDumpCategory, &SectionCategory, &ColorCategory}); + HideUnrelatedOptions( + {&DwarfDumpCategory, &SectionCategory, &getColorCategory()}); cl::ParseCommandLineOptions( argc, argv, "pretty-print DWARF debug information in object files" @@ -628,8 +639,8 @@ } std::error_code EC; - ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_Text); - error("Unable to open output file" + OutputFilename, EC); + ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_TextWithCRLF); + error("unable to open output file " + OutputFilename, EC); // Don't remove output file if we exit with an error. OutputFile.keep(); @@ -659,7 +670,8 @@ } // Unless dumping a specific DIE, default to --show-children. - if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && Find.empty()) + if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && + Find.empty()) ShowChildren = true; // Defaults to a.out if no filenames specified.
diff --git a/src/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h b/src/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h index dc41298..cf7da56 100644 --- a/src/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h +++ b/src/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h
@@ -9,6 +9,8 @@ #ifndef LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H #define LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/Twine.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Object/ObjectFile.h" @@ -21,7 +23,7 @@ struct SectionSizes { /// Map of .debug section names and their sizes across all such-named /// sections. - StringMap<uint64_t> DebugSectionSizes; + MapVector<std::string, uint64_t, StringMap<uint64_t>> DebugSectionSizes; /// Total number of bytes of all sections. uint64_t TotalObjectSize = 0; /// Total number of bytes of all debug sections.
diff --git a/src/llvm-project/llvm/tools/llvm-dwp/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-dwp/CMakeLists.txt index bf40768..15210c4 100644 --- a/src/llvm-project/llvm/tools/llvm-dwp/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-dwp/CMakeLists.txt
@@ -1,19 +1,16 @@ set(LLVM_LINK_COMPONENTS - AllTargetsAsmParsers AllTargetsCodeGens AllTargetsDescs AllTargetsInfos - AsmPrinter DebugInfoDWARF + DWP MC Object Support - Target ) add_llvm_tool(llvm-dwp llvm-dwp.cpp - DWPError.cpp DEPENDS intrinsics_gen
diff --git a/src/llvm-project/llvm/tools/llvm-dwp/DWPError.cpp b/src/llvm-project/llvm/tools/llvm-dwp/DWPError.cpp deleted file mode 100644 index 21d53ed..0000000 --- a/src/llvm-project/llvm/tools/llvm-dwp/DWPError.cpp +++ /dev/null
@@ -1,3 +0,0 @@ -#include "DWPError.h" -using namespace llvm; -char DWPError::ID;
diff --git a/src/llvm-project/llvm/tools/llvm-dwp/DWPError.h b/src/llvm-project/llvm/tools/llvm-dwp/DWPError.h deleted file mode 100644 index 62025ed..0000000 --- a/src/llvm-project/llvm/tools/llvm-dwp/DWPError.h +++ /dev/null
@@ -1,23 +0,0 @@ -#ifndef TOOLS_LLVM_DWP_DWPERROR -#define TOOLS_LLVM_DWP_DWPERROR - -#include "llvm/Support/Error.h" -#include "llvm/Support/ErrorHandling.h" -#include <string> - -namespace llvm { -class DWPError : public ErrorInfo<DWPError> { -public: - DWPError(std::string Info) : Info(std::move(Info)) {} - void log(raw_ostream &OS) const override { OS << Info; } - std::error_code convertToErrorCode() const override { - llvm_unreachable("Not implemented"); - } - static char ID; - -private: - std::string Info; -}; -} - -#endif
diff --git a/src/llvm-project/llvm/tools/llvm-dwp/DWPStringPool.h b/src/llvm-project/llvm/tools/llvm-dwp/DWPStringPool.h deleted file mode 100644 index e423076..0000000 --- a/src/llvm-project/llvm/tools/llvm-dwp/DWPStringPool.h +++ /dev/null
@@ -1,56 +0,0 @@ -#ifndef TOOLS_LLVM_DWP_DWPSTRINGPOOL -#define TOOLS_LLVM_DWP_DWPSTRINGPOOL - -#include "llvm/ADT/DenseMap.h" -#include "llvm/MC/MCSection.h" -#include "llvm/MC/MCStreamer.h" -#include <cassert> - -namespace llvm { -class DWPStringPool { - - struct CStrDenseMapInfo { - static inline const char *getEmptyKey() { - return reinterpret_cast<const char *>(~static_cast<uintptr_t>(0)); - } - static inline const char *getTombstoneKey() { - return reinterpret_cast<const char *>(~static_cast<uintptr_t>(1)); - } - static unsigned getHashValue(const char *Val) { - assert(Val != getEmptyKey() && "Cannot hash the empty key!"); - assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!"); - return (unsigned)hash_value(StringRef(Val)); - } - static bool isEqual(const char *LHS, const char *RHS) { - if (RHS == getEmptyKey()) - return LHS == getEmptyKey(); - if (RHS == getTombstoneKey()) - return LHS == getTombstoneKey(); - return strcmp(LHS, RHS) == 0; - } - }; - - MCStreamer &Out; - MCSection *Sec; - DenseMap<const char *, uint32_t, CStrDenseMapInfo> Pool; - uint32_t Offset = 0; - -public: - DWPStringPool(MCStreamer &Out, MCSection *Sec) : Out(Out), Sec(Sec) {} - - uint32_t getOffset(const char *Str, unsigned Length) { - assert(strlen(Str) + 1 == Length && "Ensure length hint is correct"); - - auto Pair = Pool.insert(std::make_pair(Str, Offset)); - if (Pair.second) { - Out.SwitchSection(Sec); - Out.emitBytes(StringRef(Str, Length)); - Offset += Length; - } - - return Pair.first->second; - } -}; -} - -#endif
diff --git a/src/llvm-project/llvm/tools/llvm-dwp/llvm-dwp.cpp b/src/llvm-project/llvm/tools/llvm-dwp/llvm-dwp.cpp index d495bd3..1f58372 100644 --- a/src/llvm-project/llvm/tools/llvm-dwp/llvm-dwp.cpp +++ b/src/llvm-project/llvm/tools/llvm-dwp/llvm-dwp.cpp
@@ -10,39 +10,22 @@ // package files). // //===----------------------------------------------------------------------===// -#include "DWPError.h" -#include "DWPStringPool.h" -#include "llvm/ADT/MapVector.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" -#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" -#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" +#include "llvm/DWP/DWP.h" +#include "llvm/DWP/DWPError.h" +#include "llvm/DWP/DWPStringPool.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" -#include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCTargetOptionsCommandFlags.h" -#include "llvm/Object/Decompressor.h" -#include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/DataExtractor.h" -#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::object; @@ -64,449 +47,6 @@ cl::value_desc("filename"), cl::cat(DwpCategory)); -static void writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings, - MCSection *StrOffsetSection, - StringRef CurStrSection, - StringRef CurStrOffsetSection) { - // Could possibly produce an error or warning if one of these was non-null but - // the other was null. - if (CurStrSection.empty() || CurStrOffsetSection.empty()) - return; - - DenseMap<uint64_t, uint32_t> OffsetRemapping; - - DataExtractor Data(CurStrSection, true, 0); - uint64_t LocalOffset = 0; - uint64_t PrevOffset = 0; - while (const char *s = Data.getCStr(&LocalOffset)) { - OffsetRemapping[PrevOffset] = - Strings.getOffset(s, LocalOffset - PrevOffset); - PrevOffset = LocalOffset; - } - - Data = DataExtractor(CurStrOffsetSection, true, 0); - - Out.SwitchSection(StrOffsetSection); - - uint64_t Offset = 0; - uint64_t Size = CurStrOffsetSection.size(); - while (Offset < Size) { - auto OldOffset = Data.getU32(&Offset); - auto NewOffset = OffsetRemapping[OldOffset]; - Out.emitIntValue(NewOffset, 4); - } -} - -static uint64_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) { - uint64_t CurCode; - uint64_t Offset = 0; - DataExtractor AbbrevData(Abbrev, true, 0); - while ((CurCode = AbbrevData.getULEB128(&Offset)) != AbbrCode) { - // Tag - AbbrevData.getULEB128(&Offset); - // DW_CHILDREN - AbbrevData.getU8(&Offset); - // Attributes - while (AbbrevData.getULEB128(&Offset) | AbbrevData.getULEB128(&Offset)) - ; - } - return Offset; -} - -struct CompileUnitIdentifiers { - uint64_t Signature = 0; - const char *Name = ""; - const char *DWOName = ""; -}; - -static Expected<const char *> -getIndexedString(dwarf::Form Form, DataExtractor InfoData, - uint64_t &InfoOffset, StringRef StrOffsets, StringRef Str) { - if (Form == dwarf::DW_FORM_string) - return InfoData.getCStr(&InfoOffset); - if (Form != dwarf::DW_FORM_GNU_str_index) - return make_error<DWPError>( - "string field encoded without DW_FORM_string or DW_FORM_GNU_str_index"); - auto StrIndex = InfoData.getULEB128(&InfoOffset); - DataExtractor StrOffsetsData(StrOffsets, true, 0); - uint64_t StrOffsetsOffset = 4 * StrIndex; - uint64_t StrOffset = StrOffsetsData.getU32(&StrOffsetsOffset); - DataExtractor StrData(Str, true, 0); - return StrData.getCStr(&StrOffset); -} - -static Expected<CompileUnitIdentifiers> getCUIdentifiers(StringRef Abbrev, - StringRef Info, - StringRef StrOffsets, - StringRef Str) { - uint64_t Offset = 0; - DataExtractor InfoData(Info, true, 0); - dwarf::DwarfFormat Format = dwarf::DwarfFormat::DWARF32; - uint64_t Length = InfoData.getU32(&Offset); - CompileUnitIdentifiers ID; - Optional<uint64_t> Signature = None; - // If the length is 0xffffffff, then this indictes that this is a DWARF 64 - // stream and the length is actually encoded into a 64 bit value that follows. - if (Length == 0xffffffffU) { - Format = dwarf::DwarfFormat::DWARF64; - Length = InfoData.getU64(&Offset); - } - uint16_t Version = InfoData.getU16(&Offset); - if (Version >= 5) { - auto UnitType = InfoData.getU8(&Offset); - if (UnitType != dwarf::DW_UT_split_compile) - return make_error<DWPError>( - std::string("unit type DW_UT_split_compile type not found in " - "debug_info header. Unexpected unit type 0x" + - utostr(UnitType) + " found")); - } - InfoData.getU32(&Offset); // Abbrev offset (should be zero) - uint8_t AddrSize = InfoData.getU8(&Offset); - if (Version >= 5) - Signature = InfoData.getU64(&Offset); - uint32_t AbbrCode = InfoData.getULEB128(&Offset); - - DataExtractor AbbrevData(Abbrev, true, 0); - uint64_t AbbrevOffset = getCUAbbrev(Abbrev, AbbrCode); - auto Tag = static_cast<dwarf::Tag>(AbbrevData.getULEB128(&AbbrevOffset)); - if (Tag != dwarf::DW_TAG_compile_unit) - return make_error<DWPError>("top level DIE is not a compile unit"); - // DW_CHILDREN - AbbrevData.getU8(&AbbrevOffset); - uint32_t Name; - dwarf::Form Form; - while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) | - (Form = static_cast<dwarf::Form>(AbbrevData.getULEB128(&AbbrevOffset))) && - (Name != 0 || Form != 0)) { - switch (Name) { - case dwarf::DW_AT_name: { - Expected<const char *> EName = - getIndexedString(Form, InfoData, Offset, StrOffsets, Str); - if (!EName) - return EName.takeError(); - ID.Name = *EName; - break; - } - case dwarf::DW_AT_GNU_dwo_name: - case dwarf::DW_AT_dwo_name: { - Expected<const char *> EName = - getIndexedString(Form, InfoData, Offset, StrOffsets, Str); - if (!EName) - return EName.takeError(); - ID.DWOName = *EName; - break; - } - case dwarf::DW_AT_GNU_dwo_id: - Signature = InfoData.getU64(&Offset); - break; - default: - DWARFFormValue::skipValue(Form, InfoData, &Offset, - dwarf::FormParams({Version, AddrSize, Format})); - } - } - if (!Signature) - return make_error<DWPError>("compile unit missing dwo_id"); - ID.Signature = *Signature; - return ID; -} - -struct UnitIndexEntry { - DWARFUnitIndex::Entry::SectionContribution Contributions[8]; - std::string Name; - std::string DWOName; - StringRef DWPName; -}; - -static bool isSupportedSectionKind(DWARFSectionKind Kind) { - return Kind != DW_SECT_EXT_unknown; -} - -// Convert an internal section identifier into the index to use with -// UnitIndexEntry::Contributions. -static unsigned getContributionIndex(DWARFSectionKind Kind) { - // Assuming the pre-standard DWP format. - assert(serializeSectionKind(Kind, 2) >= DW_SECT_INFO); - return serializeSectionKind(Kind, 2) - DW_SECT_INFO; -} - -// Convert a UnitIndexEntry::Contributions index to the corresponding on-disk -// value of the section identifier. -static unsigned getOnDiskSectionId(unsigned Index) { - return Index + DW_SECT_INFO; -} - -static StringRef getSubsection(StringRef Section, - const DWARFUnitIndex::Entry &Entry, - DWARFSectionKind Kind) { - const auto *Off = Entry.getContribution(Kind); - if (!Off) - return StringRef(); - return Section.substr(Off->Offset, Off->Length); -} - -static void addAllTypesFromDWP( - MCStreamer &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries, - const DWARFUnitIndex &TUIndex, MCSection *OutputTypes, StringRef Types, - const UnitIndexEntry &TUEntry, uint32_t &TypesOffset) { - Out.SwitchSection(OutputTypes); - for (const DWARFUnitIndex::Entry &E : TUIndex.getRows()) { - auto *I = E.getContributions(); - if (!I) - continue; - auto P = TypeIndexEntries.insert(std::make_pair(E.getSignature(), TUEntry)); - if (!P.second) - continue; - auto &Entry = P.first->second; - // Zero out the debug_info contribution - Entry.Contributions[0] = {}; - for (auto Kind : TUIndex.getColumnKinds()) { - if (!isSupportedSectionKind(Kind)) - continue; - auto &C = Entry.Contributions[getContributionIndex(Kind)]; - C.Offset += I->Offset; - C.Length = I->Length; - ++I; - } - unsigned TypesIndex = getContributionIndex(DW_SECT_EXT_TYPES); - auto &C = Entry.Contributions[TypesIndex]; - Out.emitBytes(Types.substr( - C.Offset - TUEntry.Contributions[TypesIndex].Offset, C.Length)); - C.Offset = TypesOffset; - TypesOffset += C.Length; - } -} - -static void addAllTypes(MCStreamer &Out, - MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries, - MCSection *OutputTypes, - const std::vector<StringRef> &TypesSections, - const UnitIndexEntry &CUEntry, uint32_t &TypesOffset) { - for (StringRef Types : TypesSections) { - Out.SwitchSection(OutputTypes); - uint64_t Offset = 0; - DataExtractor Data(Types, true, 0); - while (Data.isValidOffset(Offset)) { - UnitIndexEntry Entry = CUEntry; - // Zero out the debug_info contribution - Entry.Contributions[0] = {}; - auto &C = Entry.Contributions[getContributionIndex(DW_SECT_EXT_TYPES)]; - C.Offset = TypesOffset; - auto PrevOffset = Offset; - // Length of the unit, including the 4 byte length field. - C.Length = Data.getU32(&Offset) + 4; - - Data.getU16(&Offset); // Version - Data.getU32(&Offset); // Abbrev offset - Data.getU8(&Offset); // Address size - auto Signature = Data.getU64(&Offset); - Offset = PrevOffset + C.Length; - - auto P = TypeIndexEntries.insert(std::make_pair(Signature, Entry)); - if (!P.second) - continue; - - Out.emitBytes(Types.substr(PrevOffset, C.Length)); - TypesOffset += C.Length; - } - } -} - -static void -writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets, - const MapVector<uint64_t, UnitIndexEntry> &IndexEntries, - uint32_t DWARFUnitIndex::Entry::SectionContribution::*Field) { - for (const auto &E : IndexEntries) - for (size_t i = 0; i != array_lengthof(E.second.Contributions); ++i) - if (ContributionOffsets[i]) - Out.emitIntValue(E.second.Contributions[i].*Field, 4); -} - -static void -writeIndex(MCStreamer &Out, MCSection *Section, - ArrayRef<unsigned> ContributionOffsets, - const MapVector<uint64_t, UnitIndexEntry> &IndexEntries) { - if (IndexEntries.empty()) - return; - - unsigned Columns = 0; - for (auto &C : ContributionOffsets) - if (C) - ++Columns; - - std::vector<unsigned> Buckets(NextPowerOf2(3 * IndexEntries.size() / 2)); - uint64_t Mask = Buckets.size() - 1; - size_t i = 0; - for (const auto &P : IndexEntries) { - auto S = P.first; - auto H = S & Mask; - auto HP = ((S >> 32) & Mask) | 1; - while (Buckets[H]) { - assert(S != IndexEntries.begin()[Buckets[H] - 1].first && - "Duplicate unit"); - H = (H + HP) & Mask; - } - Buckets[H] = i + 1; - ++i; - } - - Out.SwitchSection(Section); - Out.emitIntValue(2, 4); // Version - Out.emitIntValue(Columns, 4); // Columns - Out.emitIntValue(IndexEntries.size(), 4); // Num Units - Out.emitIntValue(Buckets.size(), 4); // Num Buckets - - // Write the signatures. - for (const auto &I : Buckets) - Out.emitIntValue(I ? IndexEntries.begin()[I - 1].first : 0, 8); - - // Write the indexes. - for (const auto &I : Buckets) - Out.emitIntValue(I, 4); - - // Write the column headers (which sections will appear in the table) - for (size_t i = 0; i != ContributionOffsets.size(); ++i) - if (ContributionOffsets[i]) - Out.emitIntValue(getOnDiskSectionId(i), 4); - - // Write the offsets. - writeIndexTable(Out, ContributionOffsets, IndexEntries, - &DWARFUnitIndex::Entry::SectionContribution::Offset); - - // Write the lengths. - writeIndexTable(Out, ContributionOffsets, IndexEntries, - &DWARFUnitIndex::Entry::SectionContribution::Length); -} - -static std::string buildDWODescription(StringRef Name, StringRef DWPName, - StringRef DWOName) { - std::string Text = "\'"; - Text += Name; - Text += '\''; - if (!DWPName.empty()) { - Text += " (from "; - if (!DWOName.empty()) { - Text += '\''; - Text += DWOName; - Text += "' in "; - } - Text += '\''; - Text += DWPName; - Text += "')"; - } - return Text; -} - -static Error createError(StringRef Name, Error E) { - return make_error<DWPError>( - ("failure while decompressing compressed section: '" + Name + "', " + - llvm::toString(std::move(E))) - .str()); -} - -static Error -handleCompressedSection(std::deque<SmallString<32>> &UncompressedSections, - StringRef &Name, StringRef &Contents) { - if (!Decompressor::isGnuStyle(Name)) - return Error::success(); - - Expected<Decompressor> Dec = - Decompressor::create(Name, Contents, false /*IsLE*/, false /*Is64Bit*/); - if (!Dec) - return createError(Name, Dec.takeError()); - - UncompressedSections.emplace_back(); - if (Error E = Dec->resizeAndDecompress(UncompressedSections.back())) - return createError(Name, std::move(E)); - - Name = Name.substr(2); // Drop ".z" - Contents = UncompressedSections.back(); - return Error::success(); -} - -static Error handleSection( - const StringMap<std::pair<MCSection *, DWARFSectionKind>> &KnownSections, - const MCSection *StrSection, const MCSection *StrOffsetSection, - const MCSection *TypesSection, const MCSection *CUIndexSection, - const MCSection *TUIndexSection, const SectionRef &Section, MCStreamer &Out, - std::deque<SmallString<32>> &UncompressedSections, - uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry, - StringRef &CurStrSection, StringRef &CurStrOffsetSection, - std::vector<StringRef> &CurTypesSection, StringRef &InfoSection, - StringRef &AbbrevSection, StringRef &CurCUIndexSection, - StringRef &CurTUIndexSection) { - if (Section.isBSS()) - return Error::success(); - - if (Section.isVirtual()) - return Error::success(); - - Expected<StringRef> NameOrErr = Section.getName(); - if (!NameOrErr) - return NameOrErr.takeError(); - StringRef Name = *NameOrErr; - - Expected<StringRef> ContentsOrErr = Section.getContents(); - if (!ContentsOrErr) - return ContentsOrErr.takeError(); - StringRef Contents = *ContentsOrErr; - - if (auto Err = handleCompressedSection(UncompressedSections, Name, Contents)) - return Err; - - Name = Name.substr(Name.find_first_not_of("._")); - - auto SectionPair = KnownSections.find(Name); - if (SectionPair == KnownSections.end()) - return Error::success(); - - if (DWARFSectionKind Kind = SectionPair->second.second) { - auto Index = getContributionIndex(Kind); - if (Kind != DW_SECT_EXT_TYPES) { - CurEntry.Contributions[Index].Offset = ContributionOffsets[Index]; - ContributionOffsets[Index] += - (CurEntry.Contributions[Index].Length = Contents.size()); - } - - switch (Kind) { - case DW_SECT_INFO: - InfoSection = Contents; - break; - case DW_SECT_ABBREV: - AbbrevSection = Contents; - break; - default: - break; - } - } - - MCSection *OutSection = SectionPair->second.first; - if (OutSection == StrOffsetSection) - CurStrOffsetSection = Contents; - else if (OutSection == StrSection) - CurStrSection = Contents; - else if (OutSection == TypesSection) - CurTypesSection.push_back(Contents); - else if (OutSection == CUIndexSection) - CurCUIndexSection = Contents; - else if (OutSection == TUIndexSection) - CurTUIndexSection = Contents; - else { - Out.SwitchSection(OutSection); - Out.emitBytes(Contents); - } - return Error::success(); -} - -static Error -buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE, - const CompileUnitIdentifiers &ID, StringRef DWPName) { - return make_error<DWPError>( - std::string("duplicate DWO ID (") + utohexstr(PrevE.first) + ") in " + - buildDWODescription(PrevE.second.Name, PrevE.second.DWPName, - PrevE.second.DWOName) + - " and " + buildDWODescription(ID.Name, DWPName, ID.DWOName)); -} - static Expected<SmallVector<std::string, 16>> getDWOFilenames(StringRef ExecFilename) { auto ErrOrObj = object::ObjectFile::createObjectFile(ExecFilename); @@ -536,160 +76,6 @@ return std::move(DWOPaths); } -static Error write(MCStreamer &Out, ArrayRef<std::string> Inputs) { - const auto &MCOFI = *Out.getContext().getObjectFileInfo(); - MCSection *const StrSection = MCOFI.getDwarfStrDWOSection(); - MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection(); - MCSection *const TypesSection = MCOFI.getDwarfTypesDWOSection(); - MCSection *const CUIndexSection = MCOFI.getDwarfCUIndexSection(); - MCSection *const TUIndexSection = MCOFI.getDwarfTUIndexSection(); - const StringMap<std::pair<MCSection *, DWARFSectionKind>> KnownSections = { - {"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}}, - {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_EXT_TYPES}}, - {"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}}, - {"debug_str.dwo", {StrSection, static_cast<DWARFSectionKind>(0)}}, - {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_EXT_LOC}}, - {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}}, - {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}}, - {"debug_cu_index", {CUIndexSection, static_cast<DWARFSectionKind>(0)}}, - {"debug_tu_index", {TUIndexSection, static_cast<DWARFSectionKind>(0)}}}; - - MapVector<uint64_t, UnitIndexEntry> IndexEntries; - MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries; - - uint32_t ContributionOffsets[8] = {}; - - DWPStringPool Strings(Out, StrSection); - - SmallVector<OwningBinary<object::ObjectFile>, 128> Objects; - Objects.reserve(Inputs.size()); - - std::deque<SmallString<32>> UncompressedSections; - - for (const auto &Input : Inputs) { - auto ErrOrObj = object::ObjectFile::createObjectFile(Input); - if (!ErrOrObj) - return ErrOrObj.takeError(); - - auto &Obj = *ErrOrObj->getBinary(); - Objects.push_back(std::move(*ErrOrObj)); - - UnitIndexEntry CurEntry = {}; - - StringRef CurStrSection; - StringRef CurStrOffsetSection; - std::vector<StringRef> CurTypesSection; - StringRef InfoSection; - StringRef AbbrevSection; - StringRef CurCUIndexSection; - StringRef CurTUIndexSection; - - for (const auto &Section : Obj.sections()) - if (auto Err = handleSection( - KnownSections, StrSection, StrOffsetSection, TypesSection, - CUIndexSection, TUIndexSection, Section, Out, - UncompressedSections, ContributionOffsets, CurEntry, - CurStrSection, CurStrOffsetSection, CurTypesSection, InfoSection, - AbbrevSection, CurCUIndexSection, CurTUIndexSection)) - return Err; - - if (InfoSection.empty()) - continue; - - writeStringsAndOffsets(Out, Strings, StrOffsetSection, CurStrSection, - CurStrOffsetSection); - - if (CurCUIndexSection.empty()) { - Expected<CompileUnitIdentifiers> EID = getCUIdentifiers( - AbbrevSection, InfoSection, CurStrOffsetSection, CurStrSection); - if (!EID) - return createFileError(Input, EID.takeError()); - const auto &ID = *EID; - auto P = IndexEntries.insert(std::make_pair(ID.Signature, CurEntry)); - if (!P.second) - return buildDuplicateError(*P.first, ID, ""); - P.first->second.Name = ID.Name; - P.first->second.DWOName = ID.DWOName; - addAllTypes(Out, TypeIndexEntries, TypesSection, CurTypesSection, - CurEntry, - ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)]); - continue; - } - - DWARFUnitIndex CUIndex(DW_SECT_INFO); - DataExtractor CUIndexData(CurCUIndexSection, Obj.isLittleEndian(), 0); - if (!CUIndex.parse(CUIndexData)) - return make_error<DWPError>("failed to parse cu_index"); - if (CUIndex.getVersion() != 2) - return make_error<DWPError>( - "unsupported cu_index version: " + utostr(CUIndex.getVersion()) + - " (only version 2 is supported)"); - - for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) { - auto *I = E.getContributions(); - if (!I) - continue; - auto P = IndexEntries.insert(std::make_pair(E.getSignature(), CurEntry)); - Expected<CompileUnitIdentifiers> EID = getCUIdentifiers( - getSubsection(AbbrevSection, E, DW_SECT_ABBREV), - getSubsection(InfoSection, E, DW_SECT_INFO), - getSubsection(CurStrOffsetSection, E, DW_SECT_STR_OFFSETS), - CurStrSection); - if (!EID) - return createFileError(Input, EID.takeError()); - const auto &ID = *EID; - if (!P.second) - return buildDuplicateError(*P.first, ID, Input); - auto &NewEntry = P.first->second; - NewEntry.Name = ID.Name; - NewEntry.DWOName = ID.DWOName; - NewEntry.DWPName = Input; - for (auto Kind : CUIndex.getColumnKinds()) { - if (!isSupportedSectionKind(Kind)) - continue; - auto &C = NewEntry.Contributions[getContributionIndex(Kind)]; - C.Offset += I->Offset; - C.Length = I->Length; - ++I; - } - } - - if (!CurTypesSection.empty()) { - if (CurTypesSection.size() != 1) - return make_error<DWPError>("multiple type unit sections in .dwp file"); - DWARFUnitIndex TUIndex(DW_SECT_EXT_TYPES); - DataExtractor TUIndexData(CurTUIndexSection, Obj.isLittleEndian(), 0); - if (!TUIndex.parse(TUIndexData)) - return make_error<DWPError>("failed to parse tu_index"); - if (TUIndex.getVersion() != 2) - return make_error<DWPError>( - "unsupported tu_index version: " + utostr(TUIndex.getVersion()) + - " (only version 2 is supported)"); - - addAllTypesFromDWP( - Out, TypeIndexEntries, TUIndex, TypesSection, CurTypesSection.front(), - CurEntry, - ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)]); - } - } - - // Lie about there being no info contributions so the TU index only includes - // the type unit contribution - ContributionOffsets[0] = 0; - writeIndex(Out, MCOFI.getDwarfTUIndexSection(), ContributionOffsets, - TypeIndexEntries); - - // Lie about the type contribution - ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)] = 0; - // Unlie about the info contribution - ContributionOffsets[0] = 1; - - writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets, - IndexEntries); - - return Error::success(); -} - static int error(const Twine &Error, const Twine &Context) { errs() << Twine("while processing ") + Context + ":\n"; errs() << Twine("error: ") + Error + "\n"; @@ -707,6 +93,7 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); + cl::HideUnrelatedOptions({&DwpCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n"); llvm::InitializeAllTargetInfos(); @@ -756,15 +143,16 @@ if (!MAI) return error("no asm info for target " + TripleName, Context); - MCObjectFileInfo MOFI; - MCContext MC(MAI.get(), MRI.get(), &MOFI); - MOFI.InitMCObjectFileInfo(*ErrOrTriple, /*PIC*/ false, MC); - std::unique_ptr<MCSubtargetInfo> MSTI( TheTarget->createMCSubtargetInfo(TripleName, "", "")); if (!MSTI) return error("no subtarget info for target " + TripleName, Context); + MCContext MC(*ErrOrTriple, MAI.get(), MRI.get(), MSTI.get()); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(MC, /*PIC=*/false)); + MC.setObjectFileInfo(MOFI.get()); + MCTargetOptions Options; auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options); if (!MAB)
diff --git a/src/llvm-project/llvm/tools/llvm-elfabi/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-elfabi/CMakeLists.txt deleted file mode 100644 index 43b4b5b..0000000 --- a/src/llvm-project/llvm/tools/llvm-elfabi/CMakeLists.txt +++ /dev/null
@@ -1,11 +0,0 @@ -set(LLVM_LINK_COMPONENTS - InterfaceStub - Object - Support - TextAPI - ) - -add_llvm_tool(llvm-elfabi - ErrorCollector.cpp - llvm-elfabi.cpp - )
diff --git a/src/llvm-project/llvm/tools/llvm-elfabi/llvm-elfabi.cpp b/src/llvm-project/llvm/tools/llvm-elfabi/llvm-elfabi.cpp deleted file mode 100644 index 761c6a8..0000000 --- a/src/llvm-project/llvm/tools/llvm-elfabi/llvm-elfabi.cpp +++ /dev/null
@@ -1,176 +0,0 @@ -//===- llvm-elfabi.cpp ----------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===-----------------------------------------------------------------------===/ - -#include "ErrorCollector.h" -#include "llvm/InterfaceStub/ELFObjHandler.h" -#include "llvm/InterfaceStub/TBEHandler.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include <string> - -namespace llvm { -namespace elfabi { - -enum class FileFormat { TBE, ELF }; - -} // end namespace elfabi -} // end namespace llvm - -using namespace llvm; -using namespace llvm::elfabi; - -// Command line flags: -cl::opt<FileFormat> InputFileFormat( - cl::desc("Force input file format:"), - cl::values(clEnumValN(FileFormat::TBE, "tbe", - "Read `input` as text-based ELF stub"), - clEnumValN(FileFormat::ELF, "elf", - "Read `input` as ELF binary"))); -cl::opt<std::string> InputFilePath(cl::Positional, cl::desc("input"), - cl::Required); -cl::opt<std::string> - EmitTBE("emit-tbe", - cl::desc("Emit a text-based ELF stub (.tbe) from the input file"), - cl::value_desc("path")); -cl::opt<std::string> - SOName("soname", - cl::desc("Manually set the DT_SONAME entry of any emitted files"), - cl::value_desc("name")); -cl::opt<ELFTarget> BinaryOutputTarget( - "output-target", cl::desc("Create a binary stub for the specified target"), - cl::values(clEnumValN(ELFTarget::ELF32LE, "elf32-little", - "32-bit little-endian ELF stub"), - clEnumValN(ELFTarget::ELF32BE, "elf32-big", - "32-bit big-endian ELF stub"), - clEnumValN(ELFTarget::ELF64LE, "elf64-little", - "64-bit little-endian ELF stub"), - clEnumValN(ELFTarget::ELF64BE, "elf64-big", - "64-bit big-endian ELF stub"))); -cl::opt<std::string> BinaryOutputFilePath(cl::Positional, cl::desc("output")); -cl::opt<bool> WriteIfChanged( - "write-if-changed", - cl::desc("Write the output file only if it is new or has changed.")); - -/// writeTBE() writes a Text-Based ELF stub to a file using the latest version -/// of the YAML parser. -static Error writeTBE(StringRef FilePath, ELFStub &Stub) { - // Write TBE to memory first. - std::string TBEStr; - raw_string_ostream OutStr(TBEStr); - Error YAMLErr = writeTBEToOutputStream(OutStr, Stub); - if (YAMLErr) - return YAMLErr; - OutStr.flush(); - - if (WriteIfChanged) { - if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = - MemoryBuffer::getFile(FilePath)) { - // Compare TBE output with existing TBE file. - // If TBE file unchanged, abort updating. - if ((*BufOrError)->getBuffer() == TBEStr) - return Error::success(); - } - } - // Open TBE file for writing. - std::error_code SysErr; - raw_fd_ostream Out(FilePath, SysErr); - if (SysErr) - return createStringError(SysErr, "Couldn't open `%s` for writing", - FilePath.data()); - Out << TBEStr; - return Error::success(); -} - -/// readInputFile populates an ELFStub by attempting to read the -/// input file using both the TBE and binary ELF parsers. -static Expected<std::unique_ptr<ELFStub>> readInputFile(StringRef FilePath) { - // Read in file. - ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = - MemoryBuffer::getFile(FilePath); - if (!BufOrError) { - return createStringError(BufOrError.getError(), "Could not open `%s`", - FilePath.data()); - } - - std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError); - ErrorCollector EC(/*UseFatalErrors=*/false); - - // First try to read as a binary (fails fast if not binary). - if (InputFileFormat.getNumOccurrences() == 0 || - InputFileFormat == FileFormat::ELF) { - Expected<std::unique_ptr<ELFStub>> StubFromELF = - readELFFile(FileReadBuffer->getMemBufferRef()); - if (StubFromELF) { - return std::move(*StubFromELF); - } - EC.addError(StubFromELF.takeError(), "BinaryRead"); - } - - // Fall back to reading as a tbe. - if (InputFileFormat.getNumOccurrences() == 0 || - InputFileFormat == FileFormat::TBE) { - Expected<std::unique_ptr<ELFStub>> StubFromTBE = - readTBEFromBuffer(FileReadBuffer->getBuffer()); - if (StubFromTBE) { - return std::move(*StubFromTBE); - } - EC.addError(StubFromTBE.takeError(), "YamlParse"); - } - - // If both readers fail, build a new error that includes all information. - EC.addError(createStringError(errc::not_supported, - "No file readers succeeded reading `%s` " - "(unsupported/malformed file?)", - FilePath.data()), - "ReadInputFile"); - EC.escalateToFatal(); - return EC.makeError(); -} - -static void fatalError(Error Err) { - WithColor::defaultErrorHandler(std::move(Err)); - exit(1); -} - -int main(int argc, char *argv[]) { - // Parse arguments. - cl::ParseCommandLineOptions(argc, argv); - - Expected<std::unique_ptr<ELFStub>> StubOrErr = readInputFile(InputFilePath); - if (!StubOrErr) - fatalError(StubOrErr.takeError()); - - std::unique_ptr<ELFStub> TargetStub = std::move(StubOrErr.get()); - - // Change SoName before emitting stubs. - if (SOName.getNumOccurrences() == 1) - TargetStub->SoName = SOName; - - if (EmitTBE.getNumOccurrences() == 1) { - TargetStub->TbeVersion = TBEVersionCurrent; - Error TBEWriteError = writeTBE(EmitTBE, *TargetStub); - if (TBEWriteError) - fatalError(std::move(TBEWriteError)); - } - - // Write out binary ELF stub. - if (BinaryOutputFilePath.getNumOccurrences() == 1) { - if (BinaryOutputTarget.getNumOccurrences() == 0) - fatalError(createStringError(errc::not_supported, - "no binary output target specified.")); - Error BinaryWriteError = writeBinaryStub( - BinaryOutputFilePath, *TargetStub, BinaryOutputTarget, WriteIfChanged); - if (BinaryWriteError) - fatalError(std::move(BinaryWriteError)); - } -}
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-exegesis/CMakeLists.txt index 0575f2a..f1e1784 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-exegesis/CMakeLists.txt
@@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + MC MCParser Support native @@ -7,6 +8,9 @@ add_llvm_tool(llvm-exegesis DISABLE_LLVM_LINK_LLVM_DYLIB llvm-exegesis.cpp + + DEPENDS + intrinsics_gen ) add_subdirectory(lib)
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/AArch64/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-exegesis/lib/AArch64/CMakeLists.txt index a251b8f..e416d80 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/AArch64/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/AArch64/CMakeLists.txt
@@ -3,16 +3,19 @@ ${LLVM_BINARY_DIR}/lib/Target/AArch64 ) -add_library(LLVMExegesisAArch64 - STATIC - Target.cpp - ) - -llvm_update_compile_flags(LLVMExegesisAArch64) -llvm_map_components_to_libnames(libs +set(LLVM_LINK_COMPONENTS AArch64 Exegesis + Core + Support ) -target_link_libraries(LLVMExegesisAArch64 ${libs}) -set_target_properties(LLVMExegesisAArch64 PROPERTIES FOLDER "Libraries") +add_llvm_library(LLVMExegesisAArch64 + DISABLE_LLVM_LINK_LLVM_DYLIB + STATIC + Target.cpp + + DEPENDS + intrinsics_gen + AArch64CommonTableGen + )
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/Analysis.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/Analysis.cpp index ac21de1..be360b9 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/Analysis.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/Analysis.cpp
@@ -116,7 +116,7 @@ raw_svector_ostream OSS(InstPrinterStr); InstPrinter_->printInst(&MI, 0, "", *SubtargetInfo_, OSS); Bytes = Bytes.drop_front(MISize); - Lines.emplace_back(StringRef(InstPrinterStr).trim()); + Lines.emplace_back(InstPrinterStr.str().trim()); } writeEscaped<Tag>(OS, join(Lines, Separator)); } @@ -154,7 +154,8 @@ Analysis::Analysis(const Target &Target, std::unique_ptr<MCInstrInfo> InstrInfo, const InstructionBenchmarkClustering &Clustering, double AnalysisInconsistencyEpsilon, - bool AnalysisDisplayUnstableOpcodes) + bool AnalysisDisplayUnstableOpcodes, + const std::string &ForceCpuName) : Clustering_(Clustering), InstrInfo_(std::move(InstrInfo)), AnalysisInconsistencyEpsilonSquared_(AnalysisInconsistencyEpsilon * AnalysisInconsistencyEpsilon), @@ -163,18 +164,21 @@ return; const InstructionBenchmark &FirstPoint = Clustering.getPoints().front(); + const std::string CpuName = + ForceCpuName.empty() ? FirstPoint.CpuName : ForceCpuName; RegInfo_.reset(Target.createMCRegInfo(FirstPoint.LLVMTriple)); MCTargetOptions MCOptions; AsmInfo_.reset( Target.createMCAsmInfo(*RegInfo_, FirstPoint.LLVMTriple, MCOptions)); - SubtargetInfo_.reset(Target.createMCSubtargetInfo(FirstPoint.LLVMTriple, - FirstPoint.CpuName, "")); + SubtargetInfo_.reset( + Target.createMCSubtargetInfo(FirstPoint.LLVMTriple, CpuName, "")); InstPrinter_.reset(Target.createMCInstPrinter( Triple(FirstPoint.LLVMTriple), 0 /*default variant*/, *AsmInfo_, *InstrInfo_, *RegInfo_)); - Context_ = std::make_unique<MCContext>(AsmInfo_.get(), RegInfo_.get(), - &ObjectFileInfo_); + Context_ = + std::make_unique<MCContext>(Triple(FirstPoint.LLVMTriple), AsmInfo_.get(), + RegInfo_.get(), SubtargetInfo_.get()); Disasm_.reset(Target.createMCDisassembler(*SubtargetInfo_, *Context_)); assert(Disasm_ && "cannot create MCDisassembler. missing call to " "InitializeXXXTargetDisassembler ?"); @@ -195,9 +199,8 @@ OS << "\n"; // Write the points. - const auto &Clusters = Clustering_.getValidClusters(); - for (size_t I = 0, E = Clusters.size(); I < E; ++I) { - for (const size_t PointId : Clusters[I].PointIndices) { + for (const auto &ClusterIt : Clustering_.getValidClusters()) { + for (const size_t PointId : ClusterIt.PointIndices) { printInstructionRowCsv(PointId, OS); } OS << "\n\n";
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/Analysis.h b/src/llvm-project/llvm/tools/llvm-exegesis/lib/Analysis.h index 4c1c864..9d8b04c 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/Analysis.h +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/Analysis.h
@@ -39,7 +39,8 @@ Analysis(const Target &Target, std::unique_ptr<MCInstrInfo> InstrInfo, const InstructionBenchmarkClustering &Clustering, double AnalysisInconsistencyEpsilon, - bool AnalysisDisplayUnstableOpcodes); + bool AnalysisDisplayUnstableOpcodes, + const std::string &ForceCpuName = ""); // Prints a csv of instructions for each cluster. struct PrintClusters {}; @@ -111,7 +112,6 @@ const char *Separator) const; const InstructionBenchmarkClustering &Clustering_; - MCObjectFileInfo ObjectFileInfo_; std::unique_ptr<MCContext> Context_; std::unique_ptr<MCSubtargetInfo> SubtargetInfo_; std::unique_ptr<MCInstrInfo> InstrInfo_;
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp index 5f22e2a..dbf0769 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp
@@ -134,8 +134,8 @@ OS << getRegName(MCOperand.getReg()); } else if (MCOperand.isImm()) { serializeIntegerOperand(OS, MCOperand.getImm()); - } else if (MCOperand.isFPImm()) { - serializeFPOperand(OS, MCOperand.getFPImm()); + } else if (MCOperand.isDFPImm()) { + serializeFPOperand(OS, bit_cast<double>(MCOperand.getDFPImm())); } else { OS << kInvalidOperand; } @@ -148,7 +148,7 @@ if (tryDeserializeIntegerOperand(String, IntValue)) return MCOperand::createImm(IntValue); if (tryDeserializeFPOperand(String, DoubleValue)) - return MCOperand::createFPImm(DoubleValue); + return MCOperand::createDFPImm(bit_cast<uint64_t>(DoubleValue)); if (auto RegNo = getRegNo(String)) return MCOperand::createReg(*RegNo); if (String != kInvalidOperand) @@ -255,7 +255,7 @@ raw_ostream &Out) { YamlContext &Context = getTypedContext(Ctx); Out << Context.getRegName(RV.Register) << "=0x" - << RV.Value.toString(kRadix, kSigned); + << toString(RV.Value, kRadix, kSigned); } static StringRef input(StringRef String, void *Ctx, @@ -334,7 +334,7 @@ Expected<InstructionBenchmark> InstructionBenchmark::readYaml(const LLVMState &State, StringRef Filename) { if (auto ExpectedMemoryBuffer = - errorOrToExpected(MemoryBuffer::getFile(Filename))) { + errorOrToExpected(MemoryBuffer::getFile(Filename, /*IsText=*/true))) { yaml::Input Yin(*ExpectedMemoryBuffer.get()); YamlContext Context(State); InstructionBenchmark Benchmark; @@ -351,7 +351,7 @@ Expected<std::vector<InstructionBenchmark>> InstructionBenchmark::readYamls(const LLVMState &State, StringRef Filename) { if (auto ExpectedMemoryBuffer = - errorOrToExpected(MemoryBuffer::getFile(Filename))) { + errorOrToExpected(MemoryBuffer::getFile(Filename, /*IsText=*/true))) { yaml::Input Yin(*ExpectedMemoryBuffer.get()); YamlContext Context(State); std::vector<InstructionBenchmark> Benchmarks; @@ -401,8 +401,9 @@ return Err; } else { int ResultFD = 0; - if (auto E = errorCodeToError(openFileForWrite( - Filename, ResultFD, sys::fs::CD_CreateAlways, sys::fs::OF_Text))) { + if (auto E = errorCodeToError(openFileForWrite(Filename, ResultFD, + sys::fs::CD_CreateAlways, + sys::fs::OF_TextWithCRLF))) { return E; } raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/); @@ -422,5 +423,11 @@ MinValue = std::min(MinValue, BM.PerInstructionValue); } +bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) { + return std::tie(A.Key, A.PerInstructionValue, A.PerSnippetValue) == + std::tie(B.Key, B.PerInstructionValue, B.PerSnippetValue); +} + + } // namespace exegesis } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h b/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h index c883a34..436bd00 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h
@@ -67,7 +67,7 @@ const MCInst &keyInstruction() const { return Key.Instructions[0]; } // The number of instructions inside the repeated snippet. For example, if a // snippet of 3 instructions is repeated 4 times, this is 12. - int NumRepetitions = 0; + unsigned NumRepetitions = 0; enum RepetitionModeE { Duplicate, Loop, AggregateMin }; // Note that measurements are per instruction. std::vector<BenchmarkMeasure> Measurements; @@ -91,6 +91,8 @@ class Error writeYaml(const LLVMState &State, const StringRef Filename); }; +bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B); + //------------------------------------------------------------------------------ // Utilities to work with Benchmark measures.
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp index f3ce184..03e7ccc 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
@@ -133,7 +133,7 @@ } // namespace Expected<InstructionBenchmark> BenchmarkRunner::runConfiguration( - const BenchmarkCode &BC, unsigned NumRepetitions, + const BenchmarkCode &BC, unsigned NumRepetitions, unsigned LoopBodySize, ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors, bool DumpObjectToDisk) const { InstructionBenchmark InstrBenchmark; @@ -168,14 +168,16 @@ // Assemble at least kMinInstructionsForSnippet instructions by repeating // the snippet for debug/analysis. This is so that the user clearly // understands that the inside instructions are repeated. - constexpr const int kMinInstructionsForSnippet = 16; + const int MinInstructionsForSnippet = 4 * Instructions.size(); + const int LoopBodySizeForSnippet = 2 * Instructions.size(); { SmallString<0> Buffer; raw_svector_ostream OS(Buffer); if (Error E = assembleToStream( State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns, BC.Key.RegisterInitialValues, - Repetitor->Repeat(Instructions, kMinInstructionsForSnippet), + Repetitor->Repeat(Instructions, MinInstructionsForSnippet, + LoopBodySizeForSnippet), OS)) { return std::move(E); } @@ -187,8 +189,8 @@ // Assemble NumRepetitions instructions repetitions of the snippet for // measurements. - const auto Filler = - Repetitor->Repeat(Instructions, InstrBenchmark.NumRepetitions); + const auto Filler = Repetitor->Repeat( + Instructions, InstrBenchmark.NumRepetitions, LoopBodySize); object::OwningBinary<object::ObjectFile> ObjectFile; if (DumpObjectToDisk) {
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h b/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h index cc51b5b..b66902e 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h
@@ -41,6 +41,7 @@ Expected<InstructionBenchmark> runConfiguration(const BenchmarkCode &Configuration, unsigned NumRepetitions, + unsigned LoopUnrollFactor, ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors, bool DumpObjectToDisk) const;
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-exegesis/lib/CMakeLists.txt index 48896d4..2ca0ce4 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
@@ -19,7 +19,29 @@ set(LLVM_EXEGESIS_TARGETS "${LLVM_EXEGESIS_TARGETS} ${TARGETS_TO_APPEND}" PARENT_SCOPE) -add_library(LLVMExegesis +set(LLVM_LINK_COMPONENTS + Analysis + CodeGen + Core + ExecutionEngine + GlobalISel + MC + MCDisassembler + MCJIT + MCParser + Object + ObjectYAML + RuntimeDyld + Support + ) + +set(libs) +if(LLVM_ENABLE_LIBPFM AND HAVE_LIBPFM) + list(APPEND libs pfm) +endif() + +add_llvm_library(LLVMExegesis + DISABLE_LLVM_LINK_LLVM_DYLIB STATIC Analysis.cpp Assembler.cpp @@ -42,28 +64,9 @@ SnippetRepetitor.cpp Target.cpp UopsBenchmarkRunner.cpp + + LINK_LIBS ${libs} + + DEPENDS + intrinsics_gen ) - -llvm_update_compile_flags(LLVMExegesis) -llvm_map_components_to_libnames(libs - Analysis - CodeGen - Core - ExecutionEngine - GlobalISel - MC - MCDisassembler - MCJIT - MCParser - Object - ObjectYAML - RuntimeDyld - Support - ) - -if(LLVM_ENABLE_LIBPFM AND HAVE_LIBPFM) - list(APPEND libs pfm) -endif() - -target_link_libraries(LLVMExegesis ${libs}) -set_target_properties(LLVMExegesis PROPERTIES FOLDER "Libraries")
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/LlvmState.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/LlvmState.cpp index c8ef642..eb492a5 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/LlvmState.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/LlvmState.cpp
@@ -61,9 +61,10 @@ } bool LLVMState::canAssemble(const MCInst &Inst) const { - MCObjectFileInfo ObjectFileInfo; - MCContext Context(TheTargetMachine->getMCAsmInfo(), - TheTargetMachine->getMCRegisterInfo(), &ObjectFileInfo); + MCContext Context(TheTargetMachine->getTargetTriple(), + TheTargetMachine->getMCAsmInfo(), + TheTargetMachine->getMCRegisterInfo(), + TheTargetMachine->getMCSubtargetInfo()); std::unique_ptr<const MCCodeEmitter> CodeEmitter( TheTargetMachine->getTarget().createMCCodeEmitter( *TheTargetMachine->getMCInstrInfo(), *TheTargetMachine->getMCRegisterInfo(),
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp index 674db3f..049cc68 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp
@@ -374,8 +374,10 @@ OS << MCRegisterInfo.getName(Op.getReg()); else if (Op.isImm()) OS << Op.getImm(); - else if (Op.isFPImm()) - OS << Op.getFPImm(); + else if (Op.isDFPImm()) + OS << bit_cast<double>(Op.getDFPImm()); + else if (Op.isSFPImm()) + OS << bit_cast<float>(Op.getSFPImm()); else if (Op.isExpr()) OS << "Expr"; else if (Op.isInst())
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/Mips/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-exegesis/lib/Mips/CMakeLists.txt index 79bd563..ecf6590 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/Mips/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/Mips/CMakeLists.txt
@@ -3,16 +3,19 @@ ${LLVM_BINARY_DIR}/lib/Target/Mips ) -add_library(LLVMExegesisMips - STATIC - Target.cpp - ) - -llvm_update_compile_flags(LLVMExegesisMips) -llvm_map_components_to_libnames(libs +set(LLVM_LINK_COMPONENTS Mips Exegesis + Core + Support ) -target_link_libraries(LLVMExegesisMips ${libs}) -set_target_properties(LLVMExegesisMips PROPERTIES FOLDER "Libraries") +add_llvm_library(LLVMExegesisMips + DISABLE_LLVM_LINK_LLVM_DYLIB + STATIC + Target.cpp + + DEPENDS + intrinsics_gen + MipsCommonTableGen + )
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp index 58e1f4d..8a0b399 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp
@@ -12,9 +12,9 @@ #include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" #ifdef HAVE_LIBPFM -#include "perfmon/perf_event.h" -#include "perfmon/pfmlib.h" -#include "perfmon/pfmlib_perf_event.h" +#include <perfmon/perf_event.h> +#include <perfmon/pfmlib.h> +#include <perfmon/pfmlib_perf_event.h> #endif #include <cassert>
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt index 89e3343..29e7f57 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt
@@ -3,16 +3,19 @@ ${LLVM_BINARY_DIR}/lib/Target/PowerPC ) -add_library(LLVMExegesisPowerPC - STATIC - Target.cpp - ) - -llvm_update_compile_flags(LLVMExegesisPowerPC) -llvm_map_components_to_libnames(libs +set(LLVM_LINK_COMPONENTS PowerPC Exegesis + Core + Support ) -target_link_libraries(LLVMExegesisPowerPC ${libs}) -set_target_properties(LLVMExegesisPowerPC PROPERTIES FOLDER "Libraries") +add_llvm_library(LLVMExegesisPowerPC + DISABLE_LLVM_LINK_LLVM_DYLIB + STATIC + Target.cpp + + DEPENDS + intrinsics_gen + PowerPCCommonTableGen + )
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp index 504e7bd..962136a 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
@@ -42,7 +42,7 @@ std::vector<unsigned> Opcodes; Opcodes.resize(State.getInstrInfo().getNumOpcodes()); std::iota(Opcodes.begin(), Opcodes.end(), 0U); - std::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator()); + llvm::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator()); std::vector<const Instruction *> AliasingInstructions; for (const unsigned OtherOpcode : Opcodes) { @@ -51,7 +51,7 @@ const Instruction &OtherInstr = State.getIC().getInstr(OtherOpcode); const MCInstrDesc &OtherInstrDesc = OtherInstr.Description; // Ignore instructions that we cannot run. - if (OtherInstrDesc.isPseudo() || + if (OtherInstrDesc.isPseudo() || OtherInstrDesc.usesCustomInsertionHook() || OtherInstrDesc.isBranch() || OtherInstrDesc.isIndirectBranch() || OtherInstrDesc.isCall() || OtherInstrDesc.isReturn()) { continue;
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp index c71050c..f674beb 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp
@@ -130,11 +130,13 @@ BenchmarkCode Result; - MCObjectFileInfo ObjectFileInfo; const TargetMachine &TM = State.getTargetMachine(); - MCContext Context(TM.getMCAsmInfo(), TM.getMCRegisterInfo(), &ObjectFileInfo); - ObjectFileInfo.InitMCObjectFileInfo(TM.getTargetTriple(), /*PIC*/ false, - Context); + MCContext Context(TM.getTargetTriple(), TM.getMCAsmInfo(), + TM.getMCRegisterInfo(), TM.getMCSubtargetInfo()); + std::unique_ptr<MCObjectFileInfo> ObjectFileInfo( + TM.getTarget().createMCObjectFileInfo(Context, /*PIC=*/false)); + Context.setObjectFileInfo(ObjectFileInfo.get()); + Context.initInlineSourceManager(); BenchmarkCodeStreamer Streamer(&Context, TM.getMCRegisterInfo(), &Result); std::string Error;
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp index c866e97..1851cb4 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp
@@ -11,6 +11,7 @@ #include "SnippetRepetitor.h" #include "Target.h" +#include "llvm/ADT/Sequence.h" #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" @@ -24,8 +25,8 @@ // Repeats the snippet until there are at least MinInstructions in the // resulting code. - FillFunction Repeat(ArrayRef<MCInst> Instructions, - unsigned MinInstructions) const override { + FillFunction Repeat(ArrayRef<MCInst> Instructions, unsigned MinInstructions, + unsigned LoopBodySize) const override { return [Instructions, MinInstructions](FunctionFiller &Filler) { auto Entry = Filler.getEntry(); if (!Instructions.empty()) { @@ -53,17 +54,26 @@ State.getTargetMachine().getTargetTriple())) {} // Loop over the snippet ceil(MinInstructions / Instructions.Size()) times. - FillFunction Repeat(ArrayRef<MCInst> Instructions, - unsigned MinInstructions) const override { - return [this, Instructions, MinInstructions](FunctionFiller &Filler) { + FillFunction Repeat(ArrayRef<MCInst> Instructions, unsigned MinInstructions, + unsigned LoopBodySize) const override { + return [this, Instructions, MinInstructions, + LoopBodySize](FunctionFiller &Filler) { const auto &ET = State.getExegesisTarget(); auto Entry = Filler.getEntry(); auto Loop = Filler.addBasicBlock(); auto Exit = Filler.addBasicBlock(); + const unsigned LoopUnrollFactor = + LoopBodySize <= Instructions.size() + ? 1 + : divideCeil(LoopBodySize, Instructions.size()); + assert(LoopUnrollFactor >= 1 && "Should end up with at least 1 snippet."); + // Set loop counter to the right value: - const APInt LoopCount(32, (MinInstructions + Instructions.size() - 1) / - Instructions.size()); + const APInt LoopCount( + 32, + divideCeil(MinInstructions, LoopUnrollFactor * Instructions.size())); + assert(LoopCount.uge(1) && "Trip count should be at least 1."); for (const MCInst &Inst : ET.setRegTo(State.getSubtargetInfo(), LoopCounter, LoopCount)) Entry.addInstruction(Inst); @@ -78,7 +88,10 @@ Loop.MBB->addLiveIn(Reg); for (const auto &LiveIn : Entry.MBB->liveins()) Loop.MBB->addLiveIn(LiveIn); - Loop.addInstructions(Instructions); + for (auto _ : seq(0U, LoopUnrollFactor)) { + (void)_; + Loop.addInstructions(Instructions); + } ET.decrementLoopCounterAndJump(*Loop.MBB, *Loop.MBB, State.getInstrInfo());
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h b/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h index 038dcac..239fa25 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h
@@ -39,7 +39,8 @@ // Returns a functor that repeats `Instructions` so that the function executes // at least `MinInstructions` instructions. virtual FillFunction Repeat(ArrayRef<MCInst> Instructions, - unsigned MinInstructions) const = 0; + unsigned MinInstructions, + unsigned LoopBodySize) const = 0; explicit SnippetRepetitor(const LLVMState &State) : State(State) {}
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/CMakeLists.txt index ce3bbd5..da30b9b 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/CMakeLists.txt
@@ -3,17 +3,21 @@ ${LLVM_BINARY_DIR}/lib/Target/X86 ) -add_library(LLVMExegesisX86 +set(LLVM_LINK_COMPONENTS + X86 + Exegesis + Core + Support + CodeGen + ) + +add_llvm_library(LLVMExegesisX86 + DISABLE_LLVM_LINK_LLVM_DYLIB STATIC Target.cpp X86Counter.cpp - ) -llvm_update_compile_flags(LLVMExegesisX86) -llvm_map_components_to_libnames(libs - X86 - Exegesis + DEPENDS + intrinsics_gen + X86CommonTableGen ) - -target_link_libraries(LLVMExegesisX86 ${libs}) -set_target_properties(LLVMExegesisX86 PROPERTIES FOLDER "Libraries")
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/Target.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/Target.cpp index 8839f00..1be119a 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/Target.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
@@ -194,9 +194,25 @@ const auto OpcodeName = Instr.Name; if ((Instr.Description.TSFlags & X86II::FormMask) == X86II::Pseudo) return "unsupported opcode: pseudo instruction"; - if (OpcodeName.startswith("POP") || OpcodeName.startswith("PUSH") || - OpcodeName.startswith("ADJCALLSTACK") || OpcodeName.startswith("LEAVE")) + if ((OpcodeName.startswith("POP") && !OpcodeName.startswith("POPCNT")) || + OpcodeName.startswith("PUSH") || OpcodeName.startswith("ADJCALLSTACK") || + OpcodeName.startswith("LEAVE")) return "unsupported opcode: Push/Pop/AdjCallStack/Leave"; + switch (Instr.Description.Opcode) { + case X86::LFS16rm: + case X86::LFS32rm: + case X86::LFS64rm: + case X86::LGS16rm: + case X86::LGS32rm: + case X86::LGS64rm: + case X86::LSS16rm: + case X86::LSS32rm: + case X86::LSS64rm: + case X86::SYSENTER: + return "unsupported opcode"; + default: + break; + } if (const auto reason = isInvalidMemoryInstr(Instr)) return reason; // We do not handle instructions with OPERAND_PCREL. @@ -902,9 +918,9 @@ continue; case X86::OperandType::OPERAND_COND_CODE: { Exploration = true; - auto CondCodes = seq((int)X86::CondCode::COND_O, - 1 + (int)X86::CondCode::LAST_VALID_COND); - Choices.reserve(std::distance(CondCodes.begin(), CondCodes.end())); + auto CondCodes = + seq_inclusive(X86::CondCode::COND_O, X86::CondCode::LAST_VALID_COND); + Choices.reserve(CondCodes.size()); for (int CondCode : CondCodes) Choices.emplace_back(MCOperand::createImm(CondCode)); break;
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/X86Counter.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/X86Counter.cpp index 25ec4f8..3fee0e5 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/X86Counter.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/lib/X86/X86Counter.cpp
@@ -8,17 +8,18 @@ #include "X86Counter.h" +#if defined(__linux__) && defined(HAVE_LIBPFM) && \ + defined(LIBPFM_HAS_FIELD_CYCLES) + // FIXME: Use appropriate wrappers for poll.h and mman.h // to support Windows and remove this linux-only guard. -#ifdef __linux__ + #include "llvm/Support/Endian.h" #include "llvm/Support/Errc.h" -#ifdef HAVE_LIBPFM -#include "perfmon/perf_event.h" -#include "perfmon/pfmlib.h" -#include "perfmon/pfmlib_perf_event.h" -#endif // HAVE_LIBPFM +#include <perfmon/perf_event.h> +#include <perfmon/pfmlib.h> +#include <perfmon/pfmlib_perf_event.h> #include <atomic> #include <chrono> @@ -32,7 +33,6 @@ #include <sys/mman.h> #include <unistd.h> -#if defined(HAVE_LIBPFM) && defined(LIBPFM_HAS_FIELD_CYCLES) namespace llvm { namespace exegesis { @@ -41,6 +41,10 @@ static constexpr size_t kBufferPages = 8; static const size_t kDataBufferSize = kBufferPages * getpagesize(); +// First page is reserved for perf_event_mmap_page. Data buffer starts on +// the next page, so we allocate one more page. +static const size_t kMappedBufferSize = (kBufferPages + 1) * getpagesize(); + // Waits for the LBR perf events. static int pollLbrPerfEvent(const int FileDescriptor) { struct pollfd PollFd; @@ -137,15 +141,16 @@ X86LbrCounter::X86LbrCounter(pfm::PerfEvent &&NewEvent) : Counter(std::move(NewEvent)) { - // First page is reserved for perf_event_mmap_page. Data buffer starts on - // the next page, so we allocate one more page. - MMappedBuffer = mmap(nullptr, (kBufferPages + 1) * getpagesize(), - PROT_READ | PROT_WRITE, MAP_SHARED, FileDescriptor, 0); + MMappedBuffer = mmap(nullptr, kMappedBufferSize, PROT_READ | PROT_WRITE, + MAP_SHARED, FileDescriptor, 0); if (MMappedBuffer == MAP_FAILED) llvm::errs() << "Failed to mmap buffer."; } -X86LbrCounter::~X86LbrCounter() { close(FileDescriptor); } +X86LbrCounter::~X86LbrCounter() { + if (0 != munmap(MMappedBuffer, kMappedBufferSize)) + llvm::errs() << "Failed to munmap buffer."; +} void X86LbrCounter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_REFRESH, 1024 /* kMaxPollsPerFd */); @@ -173,6 +178,7 @@ } counter.stop(); + (void)Sum; auto ResultOrError = counter.doReadCounter(nullptr, nullptr); if (ResultOrError) @@ -251,5 +257,5 @@ } // namespace exegesis } // namespace llvm -#endif // defined(HAVE_LIBPFM) && defined(LIBPFM_HAS_FIELD_CYCLES) -#endif // __linux__ +#endif // defined(__linux__) && defined(HAVE_LIBPFM) && + // defined(LIBPFM_HAS_FIELD_CYCLES)
diff --git a/src/llvm-project/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/src/llvm-project/llvm/tools/llvm-exegesis/llvm-exegesis.cpp index 2d20d0e..220f404 100644 --- a/src/llvm-project/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/src/llvm-project/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
@@ -32,6 +32,7 @@ #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/Path.h" #include "llvm/Support/SourceMgr.h" @@ -115,6 +116,13 @@ cl::desc("number of time to repeat the asm snippet"), cl::cat(BenchmarkOptions), cl::init(10000)); +static cl::opt<unsigned> + LoopBodySize("loop-body-size", + cl::desc("when repeating the instruction snippet by looping " + "over it, duplicate the snippet until the loop body " + "contains at least this many instruction"), + cl::cat(BenchmarkOptions), cl::init(0)); + static cl::opt<unsigned> MaxConfigsPerOpcode( "max-configs-per-opcode", cl::desc( @@ -251,8 +259,9 @@ const Instruction &Instr = State.getIC().getInstr(Opcode); const MCInstrDesc &InstrDesc = Instr.Description; // Ignore instructions that we cannot run. - if (InstrDesc.isPseudo()) - return make_error<Failure>("Unsupported opcode: isPseudo"); + if (InstrDesc.isPseudo() || InstrDesc.usesCustomInsertionHook()) + return make_error<Failure>( + "Unsupported opcode: isPseudo/usesCustomInserter"); if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch()) return make_error<Failure>("Unsupported opcode: isBranch/isIndirectBranch"); if (InstrDesc.isCall() || InstrDesc.isReturn()) @@ -363,7 +372,7 @@ for (const BenchmarkCode &Conf : Configurations) { InstructionBenchmark Result = ExitOnErr(Runner->runConfiguration( - Conf, NumRepetitions, Repetitors, DumpObjectToDisk)); + Conf, NumRepetitions, LoopBodySize, Repetitors, DumpObjectToDisk)); ExitOnFileError(BenchmarkFile, Result.writeYaml(State, BenchmarkFile)); } exegesis::pfm::pfmTerminate(); @@ -435,7 +444,7 @@ const Analysis Analyzer(*TheTarget, std::move(InstrInfo), Clustering, AnalysisInconsistencyEpsilon, - AnalysisDisplayUnstableOpcodes); + AnalysisDisplayUnstableOpcodes, CpuName); maybeRunAnalysis<Analysis::PrintClusters>(Analyzer, "analysis clusters", AnalysisClustersOutputFile);
diff --git a/src/llvm-project/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp b/src/llvm-project/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp index 155eee0..1173ae9 100644 --- a/src/llvm-project/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp +++ b/src/llvm-project/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
@@ -30,6 +30,7 @@ #include <algorithm> #include <cstring> #include <inttypes.h> +#include <iostream> #include <map> #include <string> #include <system_error> @@ -107,12 +108,20 @@ "number of cores on the current machine."), cl::value_desc("n"), cat(ConversionOptions)); +static opt<bool> + Quiet("quiet", desc("Do not output warnings about the debug information"), + cat(ConversionOptions)); + static list<uint64_t> LookupAddresses("address", desc("Lookup an address in a GSYM file"), cl::value_desc("addr"), cat(LookupOptions)); - +static opt<bool> LookupAddressesFromStdin( + "addresses-from-stdin", + desc("Lookup addresses in a GSYM file that are read from stdin\nEach input " + "line is expected to be of the following format: <addr> <gsym-path>"), + cat(LookupOptions)); } // namespace /// @} @@ -133,7 +142,6 @@ exit(1); } - /// If the input path is a .dSYM bundle (as created by the dsymutil tool), /// replace it with individual entries for each of the object files inside the /// bundle otherwise return the input path. @@ -271,14 +279,13 @@ return llvm::None; } - static llvm::Error handleObjectFile(ObjectFile &Obj, const std::string &OutFile) { auto ThreadCount = NumThreads > 0 ? NumThreads : std::thread::hardware_concurrency(); auto &OS = outs(); - GsymCreator Gsym; + GsymCreator Gsym(Quiet); // See if we can figure out the base address for a given object file, and if // we can, then set the base address to use to this value. This will ease @@ -306,8 +313,7 @@ if (!DICtx) return createStringError(std::errc::invalid_argument, "unable to create DWARF context"); - logAllUnhandledErrors(DICtx->loadRegisterInfo(Obj), OS, - "DwarfTransformer: "); + logAllUnhandledErrors(DICtx->loadRegisterInfo(Obj), OS, "DwarfTransformer: "); // Make a DWARF transformer object and populate the ranges of the code // so we don't end up adding invalid functions to GSYM data. @@ -330,8 +336,8 @@ return Err; // Save the GSYM file to disk. - support::endianness Endian = Obj.makeTriple().isLittleEndian() ? - support::little : support::big; + support::endianness Endian = + Obj.makeTriple().isLittleEndian() ? support::little : support::big; if (auto Err = Gsym.save(OutFile.c_str(), Endian)) return Err; @@ -360,7 +366,7 @@ // Iterate over all contained architectures and filter out any that were // not specified with the "--arch <arch>" option. If the --arch option was // not specified on the command line, we will process all architectures. - std::vector< std::unique_ptr<MachOObjectFile> > FilterObjs; + std::vector<std::unique_ptr<MachOObjectFile>> FilterObjs; for (auto &ObjForArch : Fat->objects()) { if (auto MachOOrErr = ObjForArch.getAsObjectFile()) { auto &Obj = **MachOOrErr; @@ -375,7 +381,7 @@ "no matching architectures found")); // Now handle each architecture we need to convert. - for (auto &Obj: FilterObjs) { + for (auto &Obj : FilterObjs) { Triple ObjTriple(Obj->getArchTriple()); auto ArchName = ObjTriple.getArchName(); std::string ArchOutFile(OutFile); @@ -425,6 +431,27 @@ return Error::success(); } +static void doLookup(GsymReader &Gsym, uint64_t Addr, raw_ostream &OS) { + if (auto Result = Gsym.lookup(Addr)) { + // If verbose is enabled dump the full function info for the address. + if (Verbose) { + if (auto FI = Gsym.getFunctionInfo(Addr)) { + OS << "FunctionInfo for " << HEX64(Addr) << ":\n"; + Gsym.dump(OS, *FI); + OS << "\nLookupResult for " << HEX64(Addr) << ":\n"; + } + } + OS << Result.get(); + } else { + if (Verbose) + OS << "\nLookupResult for " << HEX64(Addr) << ":\n"; + OS << HEX64(Addr) << ": "; + logAllUnhandledErrors(Result.takeError(), OS, "error: "); + } + if (Verbose) + OS << "\n"; +} + int main(int argc, char const *argv[]) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(argv[0]); @@ -441,8 +468,7 @@ "lookup addresses within that GSYM file.\n" "Use the --convert option to specify a file with option --out-file " "option to convert to GSYM format.\n"; - HideUnrelatedOptions( - {&GeneralOptions, &ConversionOptions, &LookupOptions}); + HideUnrelatedOptions({&GeneralOptions, &ConversionOptions, &LookupOptions}); cl::ParseCommandLineOptions(argc, argv, Overview); if (Help) { @@ -465,6 +491,50 @@ return 0; } + if (LookupAddressesFromStdin) { + if (!LookupAddresses.empty() || !InputFilenames.empty()) { + OS << "error: no input files or addresses can be specified when using " + "the --addresses-from-stdin " + "option.\n"; + return 1; + } + + std::string InputLine; + std::string CurrentGSYMPath; + llvm::Optional<Expected<GsymReader>> CurrentGsym; + + while (std::getline(std::cin, InputLine)) { + // Strip newline characters. + std::string StrippedInputLine(InputLine); + llvm::erase_if(StrippedInputLine, + [](char c) { return c == '\r' || c == '\n'; }); + + StringRef AddrStr, GSYMPath; + std::tie(AddrStr, GSYMPath) = + llvm::StringRef{StrippedInputLine}.split(' '); + + if (GSYMPath != CurrentGSYMPath) { + CurrentGsym = GsymReader::openFile(GSYMPath); + if (!*CurrentGsym) + error(GSYMPath, CurrentGsym->takeError()); + } + + uint64_t Addr; + if (AddrStr.getAsInteger(0, Addr)) { + OS << "error: invalid address " << AddrStr + << ", expected: Address GsymFile.\n"; + return 1; + } + + doLookup(**CurrentGsym, Addr, OS); + + OS << "\n"; + OS.flush(); + } + + return EXIT_SUCCESS; + } + // Dump or access data inside GSYM files for (const auto &GSYMPath : InputFilenames) { auto Gsym = GsymReader::openFile(GSYMPath); @@ -478,25 +548,8 @@ // Lookup an address in a GSYM file and print any matches. OS << "Looking up addresses in \"" << GSYMPath << "\":\n"; - for (auto Addr: LookupAddresses) { - if (auto Result = Gsym->lookup(Addr)) { - // If verbose is enabled dump the full function info for the address. - if (Verbose) { - if (auto FI = Gsym->getFunctionInfo(Addr)) { - OS << "FunctionInfo for " << HEX64(Addr) << ":\n"; - Gsym->dump(OS, *FI); - OS << "\nLookupResult for " << HEX64(Addr) << ":\n"; - } - } - OS << Result.get(); - } else { - if (Verbose) - OS << "\nLookupResult for " << HEX64(Addr) << ":\n"; - OS << HEX64(Addr) << ": "; - logAllUnhandledErrors(Result.takeError(), OS, "error: "); - } - if (Verbose) - OS << "\n"; + for (auto Addr : LookupAddresses) { + doLookup(*Gsym, Addr, OS); } } return EXIT_SUCCESS;
diff --git a/src/llvm-project/llvm/tools/llvm-ifs/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-ifs/CMakeLists.txt index 544b0e4..5a0cfb9 100644 --- a/src/llvm-project/llvm/tools/llvm-ifs/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-ifs/CMakeLists.txt
@@ -1,4 +1,6 @@ set(LLVM_LINK_COMPONENTS + BinaryFormat + InterfaceStub Object Support TextAPI @@ -6,5 +8,6 @@ ) add_llvm_tool(llvm-ifs + ErrorCollector.cpp llvm-ifs.cpp )
diff --git a/src/llvm-project/llvm/tools/llvm-elfabi/ErrorCollector.cpp b/src/llvm-project/llvm/tools/llvm-ifs/ErrorCollector.cpp similarity index 98% rename from src/llvm-project/llvm/tools/llvm-elfabi/ErrorCollector.cpp rename to src/llvm-project/llvm/tools/llvm-ifs/ErrorCollector.cpp index a52aeff..087a30c 100644 --- a/src/llvm-project/llvm/tools/llvm-elfabi/ErrorCollector.cpp +++ b/src/llvm-project/llvm/tools/llvm-ifs/ErrorCollector.cpp
@@ -14,7 +14,7 @@ #include <vector> using namespace llvm; -using namespace llvm::elfabi; +using namespace llvm::ifs; void ErrorCollector::escalateToFatal() { ErrorsAreFatal = true; }
diff --git a/src/llvm-project/llvm/tools/llvm-elfabi/ErrorCollector.h b/src/llvm-project/llvm/tools/llvm-ifs/ErrorCollector.h similarity index 93% rename from src/llvm-project/llvm/tools/llvm-elfabi/ErrorCollector.h rename to src/llvm-project/llvm/tools/llvm-ifs/ErrorCollector.h index e2eb1c5..67996f4 100644 --- a/src/llvm-project/llvm/tools/llvm-elfabi/ErrorCollector.h +++ b/src/llvm-project/llvm/tools/llvm-ifs/ErrorCollector.h
@@ -18,14 +18,14 @@ /// //===-----------------------------------------------------------------------===/ -#ifndef LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H -#define LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H +#ifndef LLVM_TOOLS_LLVM_IFS_ERRORCOLLECTOR_H +#define LLVM_TOOLS_LLVM_IFS_ERRORCOLLECTOR_H #include "llvm/Support/Error.h" #include <vector> namespace llvm { -namespace elfabi { +namespace ifs { class ErrorCollector { public: @@ -68,7 +68,7 @@ std::vector<std::string> Tags; }; -} // end namespace elfabi +} // end namespace ifs } // end namespace llvm -#endif // LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H +#endif // LLVM_TOOLS_LLVM_IFS_ERRORCOLLECTOR_H
diff --git a/src/llvm-project/llvm/tools/llvm-ifs/llvm-ifs.cpp b/src/llvm-project/llvm/tools/llvm-ifs/llvm-ifs.cpp index 4d9a5c3..7726996 100644 --- a/src/llvm-project/llvm/tools/llvm-ifs/llvm-ifs.cpp +++ b/src/llvm-project/llvm/tools/llvm-ifs/llvm-ifs.cpp
@@ -6,9 +6,13 @@ // //===-----------------------------------------------------------------------===/ +#include "ErrorCollector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" +#include "llvm/InterfaceStub/ELFObjHandler.h" +#include "llvm/InterfaceStub/IFSHandler.h" +#include "llvm/InterfaceStub/IFSStub.h" #include "llvm/ObjectYAML/yaml2obj.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -21,9 +25,9 @@ #include "llvm/Support/WithColor.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/TextAPI/MachO/InterfaceFile.h" -#include "llvm/TextAPI/MachO/TextAPIReader.h" -#include "llvm/TextAPI/MachO/TextAPIWriter.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/TextAPIReader.h" +#include "llvm/TextAPI/TextAPIWriter.h" #include <set> #include <string> #include <vector> @@ -31,36 +35,82 @@ using namespace llvm; using namespace llvm::yaml; using namespace llvm::MachO; +using namespace llvm::ifs; #define DEBUG_TYPE "llvm-ifs" namespace { -const VersionTuple IFSVersionCurrent(2, 0); +const VersionTuple IfsVersionCurrent(3, 0); + +enum class FileFormat { IFS, ELF, TBD }; } // end anonymous namespace -static cl::opt<std::string> Action("action", cl::desc("<llvm-ifs action>"), - cl::value_desc("write-ifs | write-bin"), - cl::init("write-ifs")); +cl::OptionCategory IfsCategory("Ifs Options"); -static cl::opt<std::string> ForceFormat("force-format", - cl::desc("<force object format>"), - cl::value_desc("ELF | TBD"), - cl::init("")); - -static cl::list<std::string> InputFilenames(cl::Positional, - cl::desc("<input ifs files>"), - cl::ZeroOrMore); - -static cl::opt<std::string> OutputFilename("o", cl::desc("<output file>"), - cl::value_desc("path")); - -enum class IFSSymbolType { - NoType = 0, - Object, - Func, - // Type information is 4 bits, so 16 is safely out of range. - Unknown = 16, -}; +// TODO: Use OptTable for option parsing in the future. +// Command line flags: +cl::list<std::string> InputFilePaths(cl::Positional, cl::desc("input"), + cl::ZeroOrMore, cl::cat(IfsCategory)); +cl::opt<FileFormat> InputFormat( + "input-format", cl::desc("Specify the input file format"), + cl::values(clEnumValN(FileFormat::IFS, "IFS", "Text based ELF stub file"), + clEnumValN(FileFormat::ELF, "ELF", "ELF object file")), + cl::cat(IfsCategory)); +cl::opt<FileFormat> OutputFormat( + "output-format", cl::desc("Specify the output file format"), + cl::values(clEnumValN(FileFormat::IFS, "IFS", "Text based ELF stub file"), + clEnumValN(FileFormat::ELF, "ELF", "ELF stub file"), + clEnumValN(FileFormat::TBD, "TBD", "Apple TBD text stub file")), + cl::Required, cl::cat(IfsCategory)); +cl::opt<std::string> OptArch("arch", + cl::desc("Specify the architecture, e.g. x86_64"), + cl::cat(IfsCategory)); +cl::opt<IFSBitWidthType> + OptBitWidth("bitwidth", cl::desc("Specify the bit width"), + cl::values(clEnumValN(IFSBitWidthType::IFS32, "32", "32 bits"), + clEnumValN(IFSBitWidthType::IFS64, "64", "64 bits")), + cl::cat(IfsCategory)); +cl::opt<IFSEndiannessType> OptEndianness( + "endianness", cl::desc("Specify the endianness"), + cl::values(clEnumValN(IFSEndiannessType::Little, "little", "Little Endian"), + clEnumValN(IFSEndiannessType::Big, "big", "Big Endian")), + cl::cat(IfsCategory)); +cl::opt<std::string> OptTargetTriple( + "target", cl::desc("Specify the target triple, e.g. x86_64-linux-gnu"), + cl::cat(IfsCategory)); +cl::opt<std::string> OptTargetTripleHint( + "hint-ifs-target", + cl::desc("When --output-format is 'IFS', this flag will hint the expected " + "target triple for IFS output"), + cl::cat(IfsCategory)); +cl::opt<bool> StripIFSArch( + "strip-ifs-arch", + cl::desc("Strip target architecture information away from IFS output"), + cl::cat(IfsCategory)); +cl::opt<bool> StripIFSBitWidth( + "strip-ifs-bitwidth", + cl::desc("Strip target bit width information away from IFS output"), + cl::cat(IfsCategory)); +cl::opt<bool> StripIFSEndiannessWidth( + "strip-ifs-endianness", + cl::desc("Strip target endianness information away from IFS output"), + cl::cat(IfsCategory)); +cl::opt<bool> StripIFSTarget( + "strip-ifs-target", + cl::desc("Strip all target information away from IFS output"), + cl::cat(IfsCategory)); +cl::opt<std::string> + SoName("soname", + cl::desc("Manually set the DT_SONAME entry of any emitted files"), + cl::value_desc("name"), cl::cat(IfsCategory)); +cl::opt<std::string> OutputFilePath("output", cl::desc("Output file"), + cl::cat(IfsCategory)); +cl::alias OutputFilePathA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilePath), cl::cat(IfsCategory)); +cl::opt<bool> WriteIfChanged( + "write-if-changed", + cl::desc("Write the output file only if it is new or has changed."), + cl::cat(IfsCategory)); static std::string getTypeName(IFSSymbolType Type) { switch (Type) { @@ -70,131 +120,63 @@ return "Func"; case IFSSymbolType::Object: return "Object"; + case IFSSymbolType::TLS: + return "TLS"; case IFSSymbolType::Unknown: return "Unknown"; } llvm_unreachable("Unexpected ifs symbol type."); } -struct IFSSymbol { - IFSSymbol() = default; - IFSSymbol(std::string SymbolName) : Name(SymbolName) {} - std::string Name; - uint64_t Size; - IFSSymbolType Type; - bool Weak; - Optional<std::string> Warning; - bool operator<(const IFSSymbol &RHS) const { return Name < RHS.Name; } -}; - -LLVM_YAML_IS_SEQUENCE_VECTOR(IFSSymbol) - -namespace llvm { -namespace yaml { -/// YAML traits for IFSSymbolType. -template <> struct ScalarEnumerationTraits<IFSSymbolType> { - static void enumeration(IO &IO, IFSSymbolType &SymbolType) { - IO.enumCase(SymbolType, "NoType", IFSSymbolType::NoType); - IO.enumCase(SymbolType, "Func", IFSSymbolType::Func); - IO.enumCase(SymbolType, "Object", IFSSymbolType::Object); - IO.enumCase(SymbolType, "Unknown", IFSSymbolType::Unknown); - // Treat other symbol types as noise, and map to Unknown. - if (!IO.outputting() && IO.matchEnumFallback()) - SymbolType = IFSSymbolType::Unknown; - } -}; - -/// YAML traits for IFSSymbol. -template <> struct MappingTraits<IFSSymbol> { - static void mapping(IO &IO, IFSSymbol &Symbol) { - IO.mapRequired("Name", Symbol.Name); - IO.mapRequired("Type", Symbol.Type); - // The need for symbol size depends on the symbol type. - if (Symbol.Type == IFSSymbolType::NoType) - IO.mapOptional("Size", Symbol.Size, (uint64_t)0); - else if (Symbol.Type == IFSSymbolType::Func) - Symbol.Size = 0; - else - IO.mapRequired("Size", Symbol.Size); - IO.mapOptional("Weak", Symbol.Weak, false); - IO.mapOptional("Warning", Symbol.Warning); - } - - // Compacts symbol information into a single line. - static const bool flow = true; -}; - -} // namespace yaml -} // namespace llvm - -// A cumulative representation of ELF stubs. -// Both textual and binary stubs will read into and write from this object. -class IFSStub { - // TODO: Add support for symbol versioning. -public: - VersionTuple IfsVersion; - std::string Triple; - std::string ObjectFileFormat; - Optional<std::string> SOName; - std::vector<std::string> NeededLibs; - std::vector<IFSSymbol> Symbols; - - IFSStub() = default; - IFSStub(const IFSStub &Stub) - : IfsVersion(Stub.IfsVersion), Triple(Stub.Triple), - ObjectFileFormat(Stub.ObjectFileFormat), SOName(Stub.SOName), - NeededLibs(Stub.NeededLibs), Symbols(Stub.Symbols) {} - IFSStub(IFSStub &&Stub) - : IfsVersion(std::move(Stub.IfsVersion)), Triple(std::move(Stub.Triple)), - ObjectFileFormat(std::move(Stub.ObjectFileFormat)), - SOName(std::move(Stub.SOName)), NeededLibs(std::move(Stub.NeededLibs)), - Symbols(std::move(Stub.Symbols)) {} -}; - -namespace llvm { -namespace yaml { -/// YAML traits for IFSStub objects. -template <> struct MappingTraits<IFSStub> { - static void mapping(IO &IO, IFSStub &Stub) { - if (!IO.mapTag("!experimental-ifs-v2", true)) - IO.setError("Not a .ifs YAML file."); - - auto OldContext = IO.getContext(); - IO.setContext(&Stub); - IO.mapRequired("IfsVersion", Stub.IfsVersion); - IO.mapOptional("Triple", Stub.Triple); - IO.mapOptional("ObjectFileFormat", Stub.ObjectFileFormat); - IO.mapOptional("SOName", Stub.SOName); - IO.mapOptional("NeededLibs", Stub.NeededLibs); - IO.mapRequired("Symbols", Stub.Symbols); - IO.setContext(&OldContext); - } -}; -} // namespace yaml -} // namespace llvm - static Expected<std::unique_ptr<IFSStub>> readInputFile(StringRef FilePath) { // Read in file. ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = - MemoryBuffer::getFileOrSTDIN(FilePath); + MemoryBuffer::getFileOrSTDIN(FilePath, /*IsText=*/true); if (!BufOrError) return createStringError(BufOrError.getError(), "Could not open `%s`", FilePath.data()); std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError); - yaml::Input YamlIn(FileReadBuffer->getBuffer()); - std::unique_ptr<IFSStub> Stub(new IFSStub()); - YamlIn >> *Stub; + ErrorCollector EC(/*UseFatalErrors=*/false); - if (std::error_code Err = YamlIn.error()) - return createStringError(Err, "Failed reading Interface Stub File."); + // First try to read as a binary (fails fast if not binary). + if (InputFormat.getNumOccurrences() == 0 || InputFormat == FileFormat::ELF) { + Expected<std::unique_ptr<IFSStub>> StubFromELF = + readELFFile(FileReadBuffer->getMemBufferRef()); + if (StubFromELF) { + (*StubFromELF)->IfsVersion = IfsVersionCurrent; + return std::move(*StubFromELF); + } + EC.addError(StubFromELF.takeError(), "BinaryRead"); + } - if (Stub->IfsVersion > IFSVersionCurrent) - return make_error<StringError>( - "IFS version " + Stub->IfsVersion.getAsString() + " is unsupported.", - std::make_error_code(std::errc::invalid_argument)); + // Fall back to reading as a ifs. + if (InputFormat.getNumOccurrences() == 0 || InputFormat == FileFormat::IFS) { + Expected<std::unique_ptr<IFSStub>> StubFromIFS = + readIFSFromBuffer(FileReadBuffer->getBuffer()); + if (StubFromIFS) { + if ((*StubFromIFS)->IfsVersion > IfsVersionCurrent) + EC.addError( + createStringError(errc::not_supported, + "IFS version " + + (*StubFromIFS)->IfsVersion.getAsString() + + " is unsupported."), + "ReadInputFile"); + else + return std::move(*StubFromIFS); + } else { + EC.addError(StubFromIFS.takeError(), "YamlParse"); + } + } - return std::move(Stub); + // If both readers fail, build a new error that includes all information. + EC.addError(createStringError(errc::not_supported, + "No file readers succeeded reading `%s` " + "(unsupported/malformed file?)", + FilePath.data()), + "ReadInputFile"); + EC.escalateToFatal(); + return EC.makeError(); } static int writeTbdStub(const Triple &T, const std::vector<IFSSymbol> &Symbols, @@ -213,10 +195,6 @@ if (T.isiOS()) return llvm::MachO::PlatformKind::iOS; - // TODO: Add an option for ForceTriple, but keep ForceFormat for now. - if (ForceFormat == "TBD") - return llvm::MachO::PlatformKind::macOS; - return createStringError(errc::not_supported, "Invalid Platform.\n"); }(T); @@ -259,154 +237,70 @@ return 0; } -static int writeElfStub(const Triple &T, const std::vector<IFSSymbol> &Symbols, - const StringRef Format, raw_ostream &Out) { - SmallString<0> Storage; - Storage.clear(); - raw_svector_ostream OS(Storage); - - OS << "--- !ELF\n"; - OS << "FileHeader:\n"; - OS << " Class: ELFCLASS"; - OS << (T.isArch64Bit() ? "64" : "32"); - OS << "\n"; - OS << " Data: ELFDATA2"; - OS << (T.isLittleEndian() ? "LSB" : "MSB"); - OS << "\n"; - OS << " Type: ET_DYN\n"; - OS << " Machine: " - << llvm::StringSwitch<llvm::StringRef>(T.getArchName()) - .Case("x86_64", "EM_X86_64") - .Case("i386", "EM_386") - .Case("i686", "EM_386") - .Case("aarch64", "EM_AARCH64") - .Case("amdgcn", "EM_AMDGPU") - .Case("r600", "EM_AMDGPU") - .Case("arm", "EM_ARM") - .Case("thumb", "EM_ARM") - .Case("avr", "EM_AVR") - .Case("mips", "EM_MIPS") - .Case("mipsel", "EM_MIPS") - .Case("mips64", "EM_MIPS") - .Case("mips64el", "EM_MIPS") - .Case("msp430", "EM_MSP430") - .Case("ppc", "EM_PPC") - .Case("ppc64", "EM_PPC64") - .Case("ppc64le", "EM_PPC64") - .Case("x86", T.isOSIAMCU() ? "EM_IAMCU" : "EM_386") - .Case("x86_64", "EM_X86_64") - .Default("EM_NONE") - << "\nSections:" - << "\n - Name: .text" - << "\n Type: SHT_PROGBITS" - << "\n - Name: .data" - << "\n Type: SHT_PROGBITS" - << "\n - Name: .rodata" - << "\n Type: SHT_PROGBITS" - << "\nSymbols:\n"; - for (const auto &Symbol : Symbols) { - OS << " - Name: " << Symbol.Name << "\n" - << " Type: STT_"; - switch (Symbol.Type) { - default: - case IFSSymbolType::NoType: - OS << "NOTYPE"; - break; - case IFSSymbolType::Object: - OS << "OBJECT"; - break; - case IFSSymbolType::Func: - OS << "FUNC"; - break; - } - OS << "\n Section: .text" - << "\n Binding: STB_" << (Symbol.Weak ? "WEAK" : "GLOBAL") - << "\n"; - } - OS << "...\n"; - - std::string YamlStr = std::string(OS.str()); - - // Only or debugging. Not an offical format. - LLVM_DEBUG({ - if (ForceFormat == "ELFOBJYAML") { - Out << YamlStr; - return 0; - } - }); - - yaml::Input YIn(YamlStr); - auto ErrHandler = [](const Twine &Msg) { - WithColor::error(errs(), "llvm-ifs") << Msg << "\n"; - }; - return convertYAML(YIn, Out, ErrHandler) ? 0 : 1; +static void fatalError(Error Err) { + WithColor::defaultErrorHandler(std::move(Err)); + exit(1); } -static int writeIfso(const IFSStub &Stub, bool IsWriteIfs, raw_ostream &Out) { - if (IsWriteIfs) { - yaml::Output YamlOut(Out, NULL, /*WrapColumn =*/0); - YamlOut << const_cast<IFSStub &>(Stub); - return 0; +/// writeIFS() writes a Text-Based ELF stub to a file using the latest version +/// of the YAML parser. +static Error writeIFS(StringRef FilePath, IFSStub &Stub) { + // Write IFS to memory first. + std::string IFSStr; + raw_string_ostream OutStr(IFSStr); + Error YAMLErr = writeIFSToOutputStream(OutStr, Stub); + if (YAMLErr) + return YAMLErr; + OutStr.flush(); + + if (WriteIfChanged) { + if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = + MemoryBuffer::getFile(FilePath)) { + // Compare IFS output with the existing IFS file. If unchanged, avoid changing the file. + if ((*BufOrError)->getBuffer() == IFSStr) + return Error::success(); + } } - - std::string ObjectFileFormat = - ForceFormat.empty() ? Stub.ObjectFileFormat : ForceFormat; - - if (ObjectFileFormat == "ELF" || ForceFormat == "ELFOBJYAML") - return writeElfStub(llvm::Triple(Stub.Triple), Stub.Symbols, - Stub.ObjectFileFormat, Out); - if (ObjectFileFormat == "TBD") - return writeTbdStub(llvm::Triple(Stub.Triple), Stub.Symbols, - Stub.ObjectFileFormat, Out); - - WithColor::error() - << "Invalid ObjectFileFormat: Only ELF and TBD are supported.\n"; - return -1; + // Open IFS file for writing. + std::error_code SysErr; + raw_fd_ostream Out(FilePath, SysErr); + if (SysErr) + return createStringError(SysErr, "Couldn't open `%s` for writing", + FilePath.data()); + Out << IFSStr; + return Error::success(); } -// TODO: Drop ObjectFileFormat, it can be subsumed from the triple. -// New Interface Stubs Yaml Format: -// --- !experimental-ifs-v2 -// IfsVersion: 2.0 -// Triple: <llvm triple> -// ObjectFileFormat: <ELF | others not yet supported> -// Symbols: -// _ZSymbolName: { Type: <type> } -// ... - int main(int argc, char *argv[]) { // Parse arguments. + cl::HideUnrelatedOptions({&IfsCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv); - if (InputFilenames.empty()) - InputFilenames.push_back("-"); + if (InputFilePaths.empty()) + InputFilePaths.push_back("-"); + // If input files are more than one, they can only be IFS files. + if (InputFilePaths.size() > 1) + InputFormat.setValue(FileFormat::IFS); + + // Attempt to merge input. IFSStub Stub; std::map<std::string, IFSSymbol> SymbolMap; - std::string PreviousInputFilePath; - for (const std::string &InputFilePath : InputFilenames) { + for (const std::string &InputFilePath : InputFilePaths) { Expected<std::unique_ptr<IFSStub>> StubOrErr = readInputFile(InputFilePath); - if (!StubOrErr) { - WithColor::error() << StubOrErr.takeError() << "\n"; - return -1; - } - std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get()); + if (!StubOrErr) + fatalError(StubOrErr.takeError()); - if (Stub.Triple.empty()) { - PreviousInputFilePath = InputFilePath; + std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get()); + if (PreviousInputFilePath.empty()) { Stub.IfsVersion = TargetStub->IfsVersion; - Stub.Triple = TargetStub->Triple; - Stub.ObjectFileFormat = TargetStub->ObjectFileFormat; - Stub.SOName = TargetStub->SOName; + Stub.Target = TargetStub->Target; + Stub.SoName = TargetStub->SoName; Stub.NeededLibs = TargetStub->NeededLibs; } else { - Stub.ObjectFileFormat = !Stub.ObjectFileFormat.empty() - ? Stub.ObjectFileFormat - : TargetStub->ObjectFileFormat; - if (Stub.IfsVersion != TargetStub->IfsVersion) { - if (Stub.IfsVersion.getMajor() != IFSVersionCurrent.getMajor()) { + if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) { WithColor::error() << "Interface Stub: IfsVersion Mismatch." << "\nFilenames: " << PreviousInputFilePath << " " @@ -417,29 +311,20 @@ if (TargetStub->IfsVersion > Stub.IfsVersion) Stub.IfsVersion = TargetStub->IfsVersion; } - if (Stub.ObjectFileFormat != TargetStub->ObjectFileFormat && - !TargetStub->ObjectFileFormat.empty()) { - WithColor::error() << "Interface Stub: ObjectFileFormat Mismatch." + if (Stub.Target != TargetStub->Target && !TargetStub->Target.empty()) { + WithColor::error() << "Interface Stub: Target Mismatch." << "\nFilenames: " << PreviousInputFilePath << " " - << InputFilePath << "\nObjectFileFormat Values: " - << Stub.ObjectFileFormat << " " - << TargetStub->ObjectFileFormat << "\n"; + << InputFilePath; + // << "\nTriple Values: " << Stub.Triple << " " + // << TargetStub->Triple << "\n"; return -1; } - if (Stub.Triple != TargetStub->Triple && !TargetStub->Triple.empty()) { - WithColor::error() << "Interface Stub: Triple Mismatch." + if (Stub.SoName != TargetStub->SoName) { + WithColor::error() << "Interface Stub: SoName Mismatch." << "\nFilenames: " << PreviousInputFilePath << " " << InputFilePath - << "\nTriple Values: " << Stub.Triple << " " - << TargetStub->Triple << "\n"; - return -1; - } - if (Stub.SOName != TargetStub->SOName) { - WithColor::error() << "Interface Stub: SOName Mismatch." - << "\nFilenames: " << PreviousInputFilePath << " " - << InputFilePath - << "\nSOName Values: " << Stub.SOName << " " - << TargetStub->SOName << "\n"; + << "\nSoName Values: " << Stub.SoName << " " + << TargetStub->SoName << "\n"; return -1; } if (Stub.NeededLibs != TargetStub->NeededLibs) { @@ -487,26 +372,92 @@ PreviousInputFilePath = InputFilePath; } - if (Stub.IfsVersion != IFSVersionCurrent) - if (Stub.IfsVersion.getMajor() != IFSVersionCurrent.getMajor()) { + if (Stub.IfsVersion != IfsVersionCurrent) + if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) { WithColor::error() << "Interface Stub: Bad IfsVersion: " << Stub.IfsVersion << ", llvm-ifs supported version: " - << IFSVersionCurrent << ".\n"; + << IfsVersionCurrent << ".\n"; return -1; } for (auto &Entry : SymbolMap) Stub.Symbols.push_back(Entry.second); - std::error_code SysErr; + // Change SoName before emitting stubs. + if (SoName.getNumOccurrences() == 1) + Stub.SoName = SoName; + Optional<IFSArch> OverrideArch; + Optional<IFSEndiannessType> OverrideEndianness; + Optional<IFSBitWidthType> OverrideBitWidth; + Optional<std::string> OverrideTriple; + if (OptArch.getNumOccurrences() == 1) + OverrideArch = ELF::convertArchNameToEMachine(OptArch.getValue()); + if (OptEndianness.getNumOccurrences() == 1) + OverrideEndianness = OptEndianness.getValue(); + if (OptBitWidth.getNumOccurrences() == 1) + OverrideBitWidth = OptBitWidth.getValue(); + if (OptTargetTriple.getNumOccurrences() == 1) + OverrideTriple = OptTargetTriple.getValue(); + Error OverrideError = overrideIFSTarget( + Stub, OverrideArch, OverrideEndianness, OverrideBitWidth, OverrideTriple); + if (OverrideError) + fatalError(std::move(OverrideError)); - // Open file for writing. - raw_fd_ostream Out(OutputFilename, SysErr); - if (SysErr) { - WithColor::error() << "Couldn't open " << OutputFilename - << " for writing.\n"; - return -1; + switch (OutputFormat.getValue()) { + case FileFormat::TBD: { + std::error_code SysErr; + raw_fd_ostream Out(OutputFilePath, SysErr); + if (SysErr) { + WithColor::error() << "Couldn't open " << OutputFilePath + << " for writing.\n"; + return -1; + } + if (!Stub.Target.Triple) { + WithColor::error() + << "Triple should be defined when output format is TBD"; + return -1; + } + return writeTbdStub(llvm::Triple(Stub.Target.Triple.getValue()), + Stub.Symbols, "TBD", Out); } + case FileFormat::IFS: { + Stub.IfsVersion = IfsVersionCurrent; + if (InputFormat.getValue() == FileFormat::ELF && + OptTargetTripleHint.getNumOccurrences() == 1) { + std::error_code HintEC(1, std::generic_category()); + IFSTarget HintTarget = parseTriple(OptTargetTripleHint); + if (Stub.Target.Arch.getValue() != HintTarget.Arch.getValue()) + fatalError(make_error<StringError>( + "Triple hint does not match the actual architecture", HintEC)); + if (Stub.Target.Endianness.getValue() != + HintTarget.Endianness.getValue()) + fatalError(make_error<StringError>( + "Triple hint does not match the actual endianness", HintEC)); + if (Stub.Target.BitWidth.getValue() != HintTarget.BitWidth.getValue()) + fatalError(make_error<StringError>( + "Triple hint does not match the actual bit width", HintEC)); - return writeIfso(Stub, (Action == "write-ifs"), Out); + stripIFSTarget(Stub, true, false, false, false); + Stub.Target.Triple = OptTargetTripleHint.getValue(); + } else { + stripIFSTarget(Stub, StripIFSTarget, StripIFSArch, + StripIFSEndiannessWidth, StripIFSBitWidth); + } + Error IFSWriteError = writeIFS(OutputFilePath.getValue(), Stub); + if (IFSWriteError) + fatalError(std::move(IFSWriteError)); + break; + } + case FileFormat::ELF: { + Error TargetError = validateIFSTarget(Stub, true); + if (TargetError) + fatalError(std::move(TargetError)); + Error BinaryWriteError = + writeBinaryStub(OutputFilePath, Stub, WriteIfChanged); + if (BinaryWriteError) + fatalError(std::move(BinaryWriteError)); + break; + } + } + return 0; }
diff --git a/src/llvm-project/llvm/tools/llvm-jitlink/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-jitlink/CMakeLists.txt index 90b4ca6..c9f9a53 100644 --- a/src/llvm-project/llvm/tools/llvm-jitlink/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-jitlink/CMakeLists.txt
@@ -24,6 +24,10 @@ llvm-jitlink-macho.cpp ) +if(${CMAKE_SYSTEM_NAME} MATCHES "Haiku") + target_link_libraries(llvm-jitlink PRIVATE network) +endif() + if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") target_link_libraries(llvm-jitlink PRIVATE socket nsl) endif()
diff --git a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp index beb73fb..5efdff6 100644 --- a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp +++ b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
@@ -116,11 +116,15 @@ return make_error<StringError>("zero-fill atom in GOT section", inconvertibleErrorCode()); - if (auto TS = getELFGOTTarget(G, Sym->getBlock())) - FileInfo.GOTEntryInfos[TS->getName()] = {Sym->getSymbolContent(), - Sym->getAddress()}; - else - return TS.takeError(); + // If this is a GOT symbol with size (i.e. not the GOT start symbol) + // then add it to the GOT entry info table. + if (Sym->getSize() != 0) { + if (auto TS = getELFGOTTarget(G, Sym->getBlock())) + FileInfo.GOTEntryInfos[TS->getName()] = {Sym->getSymbolContent(), + Sym->getAddress()}; + else + return TS.takeError(); + } SectionContainsContent = true; } else if (isStubsSection) { if (Sym->isSymbolZeroFill()) @@ -133,8 +137,9 @@ else return TS.takeError(); SectionContainsContent = true; - } else if (Sym->hasName()) { - dbgs() << "Symbol: " << Sym->getName() << "\n"; + } + + if (Sym->hasName()) { if (Sym->isSymbolZeroFill()) { S.SymbolInfos[Sym->getName()] = {Sym->getSize(), Sym->getAddress()}; SectionContainsZeroFill = true; @@ -159,7 +164,7 @@ FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr}; else FileInfo.SectionInfos[Sec.getName()] = { - StringRef(FirstSym->getBlock().getContent().data(), SecSize), + ArrayRef<char>(FirstSym->getBlock().getContent().data(), SecSize), SecAddr}; }
diff --git a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/CMakeLists.txt index a183857..7e4570c 100644 --- a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/CMakeLists.txt
@@ -11,7 +11,10 @@ intrinsics_gen ) -message(${CMAKE_SYSTEM_NAME}) +if(${CMAKE_SYSTEM_NAME} MATCHES "Haiku") + target_link_libraries(llvm-jitlink-executor PRIVATE network) +endif() + if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") target_link_libraries(llvm-jitlink-executor PRIVATE socket) endif()
diff --git a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp index fd7e30f..7f197a5 100644 --- a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp +++ b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
@@ -12,15 +12,19 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" +#include <cstring> #include <sstream> #ifdef LLVM_ON_UNIX +#include <netdb.h> #include <netinet/in.h> #include <sys/socket.h> @@ -33,7 +37,8 @@ LLVM_ATTRIBUTE_USED void linkComponents() { errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper - << (void *)&llvm_orc_deregisterEHFrameSectionWrapper; + << (void *)&llvm_orc_deregisterEHFrameSectionWrapper + << (void *)&llvm_orc_registerJITLoaderGDBWrapper; } void printErrorAndExit(Twine ErrMsg) { @@ -44,37 +49,58 @@ exit(1); } -int openListener(std::string Host, int Port) { +int openListener(std::string Host, std::string PortStr) { #ifndef LLVM_ON_UNIX // FIXME: Add TCP support for Windows. printErrorAndExit("listen option not supported"); return 0; #else - int SockFD = socket(PF_INET, SOCK_STREAM, 0); - struct sockaddr_in ServerAddr, ClientAddr; - socklen_t ClientAddrLen = sizeof(ClientAddr); - memset(&ServerAddr, 0, sizeof(ServerAddr)); - ServerAddr.sin_family = PF_INET; - ServerAddr.sin_family = INADDR_ANY; - ServerAddr.sin_port = htons(Port); + addrinfo Hints{}; + Hints.ai_family = AF_INET; + Hints.ai_socktype = SOCK_STREAM; + Hints.ai_flags = AI_PASSIVE; - { - // lose the "Address already in use" error message - int Yes = 1; - if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) { - errs() << "Error calling setsockopt.\n"; - exit(1); - } - } - - if (bind(SockFD, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr)) < 0) { - errs() << "Error on binding.\n"; + addrinfo *AI; + if (int EC = getaddrinfo(nullptr, PortStr.c_str(), &Hints, &AI)) { + errs() << "Error setting up bind address: " << gai_strerror(EC) << "\n"; exit(1); } - listen(SockFD, 1); - return accept(SockFD, (struct sockaddr *)&ClientAddr, &ClientAddrLen); + // Create a socket from first addrinfo structure returned by getaddrinfo. + int SockFD; + if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) { + errs() << "Error creating socket: " << std::strerror(errno) << "\n"; + exit(1); + } + + // Avoid "Address already in use" errors. + const int Yes = 1; + if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) { + errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n"; + exit(1); + } + + // Bind the socket to the desired port. + if (bind(SockFD, AI->ai_addr, AI->ai_addrlen) < 0) { + errs() << "Error on binding: " << std::strerror(errno) << "\n"; + exit(1); + } + + // Listen for incomming connections. + static constexpr int ConnectionQueueLen = 1; + listen(SockFD, ConnectionQueueLen); + + outs() << "Listening at " << Host << ":" << PortStr << "\n"; + +#if defined(_AIX) + assert(Hi_32(AI->ai_addrlen) == 0 && "Field is a size_t on 64-bit AIX"); + socklen_t AddrLen = Lo_32(AI->ai_addrlen); + return accept(SockFD, AI->ai_addr, &AddrLen); +#else + return accept(SockFD, AI->ai_addr, &AI->ai_addrlen); #endif + +#endif // LLVM_ON_UNIX } int main(int argc, char *argv[]) { @@ -103,9 +129,11 @@ int Port = 0; if (PortStr.getAsInteger(10, Port)) - printErrorAndExit("port" + PortStr + " is not a valid integer"); + printErrorAndExit("port number '" + PortStr + + "' is not a valid integer"); - InFD = OutFD = openListener(Host.str(), Port); + InFD = OutFD = openListener(Host.str(), PortStr.str()); + outs() << "Connection established. Running OrcRPCTPCServer...\n"; } else printErrorAndExit("invalid specifier type \"" + SpecifierType + "\""); } @@ -113,7 +141,7 @@ ExitOnErr.setBanner(std::string(argv[0]) + ":"); using JITLinkExecutorEndpoint = - shared::MultiThreadedRPCEndpoint<shared::FDRawByteChannel>; + shared::SingleThreadedRPCEndpoint<shared::FDRawByteChannel>; shared::registerStringError<shared::FDRawByteChannel>();
diff --git a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp index fc70934..7bd6bde 100644 --- a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp +++ b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp
@@ -159,7 +159,7 @@ FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr}; else FileInfo.SectionInfos[Sec.getName()] = { - StringRef(FirstSym->getBlock().getContent().data(), SecSize), + ArrayRef<char>(FirstSym->getBlock().getContent().data(), SecSize), SecAddr}; }
diff --git a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink.cpp index da4a164..8bd384e 100644 --- a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink.cpp +++ b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -15,9 +15,14 @@ #include "llvm-jitlink.h" #include "llvm/BinaryFormat/Magic.h" +#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h" +#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" +#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" +#include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" -#include "llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.h" -#include "llvm/ExecutionEngine/Orc/TPCEHFrameRegistrar.h" +#include "llvm/ExecutionEngine/Orc/MachOPlatform.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" @@ -33,11 +38,13 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/Timer.h" +#include <cstring> #include <list> #include <string> @@ -54,107 +61,138 @@ using namespace llvm::jitlink; using namespace llvm::orc; +static cl::OptionCategory JITLinkCategory("JITLink Options"); + static cl::list<std::string> InputFiles(cl::Positional, cl::OneOrMore, - cl::desc("input files")); + cl::desc("input files"), + cl::cat(JITLinkCategory)); static cl::opt<bool> NoExec("noexec", cl::desc("Do not execute loaded code"), - cl::init(false)); + cl::init(false), cl::cat(JITLinkCategory)); static cl::list<std::string> CheckFiles("check", cl::desc("File containing verifier checks"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(JITLinkCategory)); static cl::opt<std::string> CheckName("check-name", cl::desc("Name of checks to match against"), - cl::init("jitlink-check")); + cl::init("jitlink-check"), cl::cat(JITLinkCategory)); static cl::opt<std::string> EntryPointName("entry", cl::desc("Symbol to call as main entry point"), - cl::init("")); + cl::init(""), cl::cat(JITLinkCategory)); static cl::list<std::string> JITLinkDylibs( - "jld", cl::desc("Specifies the JITDylib to be used for any subsequent " - "input file arguments")); + "jld", + cl::desc("Specifies the JITDylib to be used for any subsequent " + "input file arguments"), + cl::cat(JITLinkCategory)); static cl::list<std::string> Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(JITLinkCategory)); static cl::list<std::string> InputArgv("args", cl::Positional, cl::desc("<program arguments>..."), - cl::ZeroOrMore, cl::PositionalEatsArgs); + cl::ZeroOrMore, cl::PositionalEatsArgs, + cl::cat(JITLinkCategory)); static cl::opt<bool> NoProcessSymbols("no-process-syms", cl::desc("Do not resolve to llvm-jitlink process symbols"), - cl::init(false)); + cl::init(false), cl::cat(JITLinkCategory)); static cl::list<std::string> AbsoluteDefs( "define-abs", cl::desc("Inject absolute symbol definitions (syntax: <name>=<addr>)"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(JITLinkCategory)); static cl::list<std::string> TestHarnesses("harness", cl::Positional, cl::desc("Test harness files"), cl::ZeroOrMore, - cl::PositionalEatsArgs); + cl::PositionalEatsArgs, + cl::cat(JITLinkCategory)); static cl::opt<bool> ShowInitialExecutionSessionState( "show-init-es", cl::desc("Print ExecutionSession state before resolving entry point"), - cl::init(false)); + cl::init(false), cl::cat(JITLinkCategory)); static cl::opt<bool> ShowAddrs( "show-addrs", cl::desc("Print registered symbol, section, got and stub addresses"), - cl::init(false)); + cl::init(false), cl::cat(JITLinkCategory)); static cl::opt<bool> ShowLinkGraph( "show-graph", cl::desc("Print the link graph after fixups have been applied"), - cl::init(false)); + cl::init(false), cl::cat(JITLinkCategory)); static cl::opt<bool> ShowSizes( "show-sizes", cl::desc("Show sizes pre- and post-dead stripping, and allocations"), - cl::init(false)); + cl::init(false), cl::cat(JITLinkCategory)); static cl::opt<bool> ShowTimes("show-times", cl::desc("Show times for llvm-jitlink phases"), - cl::init(false)); + cl::init(false), cl::cat(JITLinkCategory)); static cl::opt<std::string> SlabAllocateSizeString( "slab-allocate", cl::desc("Allocate from a slab of the given size " "(allowable suffixes: Kb, Mb, Gb. default = " "Kb)"), - cl::init("")); + cl::init(""), cl::cat(JITLinkCategory)); static cl::opt<uint64_t> SlabAddress( "slab-address", cl::desc("Set slab target address (requires -slab-allocate and -noexec)"), - cl::init(~0ULL)); + cl::init(~0ULL), cl::cat(JITLinkCategory)); static cl::opt<bool> ShowRelocatedSectionContents( "show-relocated-section-contents", cl::desc("show section contents after fixups have been applied"), - cl::init(false)); + cl::init(false), cl::cat(JITLinkCategory)); static cl::opt<bool> PhonyExternals( "phony-externals", cl::desc("resolve all otherwise unresolved externals to null"), - cl::init(false)); + cl::init(false), cl::cat(JITLinkCategory)); static cl::opt<std::string> OutOfProcessExecutor( "oop-executor", cl::desc("Launch an out-of-process executor to run code"), - cl::ValueOptional); + cl::ValueOptional, cl::cat(JITLinkCategory)); static cl::opt<std::string> OutOfProcessExecutorConnect( "oop-executor-connect", - cl::desc("Connect to an out-of-process executor via TCP")); + cl::desc("Connect to an out-of-process executor via TCP"), + cl::cat(JITLinkCategory)); + +// TODO: Default to false if compiler-rt is not built. +static cl::opt<bool> UseOrcRuntime("use-orc-runtime", + cl::desc("Do not required/load ORC runtime"), + cl::init(true), cl::cat(JITLinkCategory)); + +static cl::opt<std::string> + OrcRuntimePath("orc-runtime-path", cl::desc("Add orc runtime to session"), + cl::init(""), cl::cat(JITLinkCategory)); ExitOnError ExitOnErr; +LLVM_ATTRIBUTE_USED void linkComponents() { + errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper + << (void *)&llvm_orc_deregisterEHFrameSectionWrapper + << (void *)&llvm_orc_registerJITLoaderGDBWrapper; +} + +static bool UseTestResultOverride = false; +static int64_t TestResultOverride = 0; + +extern "C" void llvm_jitlink_setTestResultOverride(int64_t Value) { + TestResultOverride = Value; + UseTestResultOverride = true; +} + namespace llvm { static raw_ostream & @@ -290,8 +328,9 @@ JITTargetAddress SymStart = Sym->getAddress(); JITTargetAddress SymSize = Sym->getSize(); JITTargetAddress SymEnd = SymStart + SymSize; - const uint8_t *SymData = - IsZeroFill ? nullptr : Sym->getSymbolContent().bytes_begin(); + const uint8_t *SymData = IsZeroFill ? nullptr + : reinterpret_cast<const uint8_t *>( + Sym->getSymbolContent().data()); // Pad any space before the symbol starts. while (NextAddr != SymStart) { @@ -466,12 +505,12 @@ uint64_t Units = 1024; - if (SizeString.endswith_lower("kb")) + if (SizeString.endswith_insensitive("kb")) SizeString = SizeString.drop_back(2).rtrim(); - else if (SizeString.endswith_lower("mb")) { + else if (SizeString.endswith_insensitive("mb")) { Units = 1024 * 1024; SizeString = SizeString.drop_back(2).rtrim(); - } else if (SizeString.endswith_lower("gb")) { + } else if (SizeString.endswith_insensitive("gb")) { Units = 1024 * 1024 * 1024; SizeString = SizeString.drop_back(2).rtrim(); } @@ -575,8 +614,33 @@ return JD.define(std::move(MU), std::move(RT)); } -Expected<std::unique_ptr<TargetProcessControl>> -LLVMJITLinkRemoteTargetProcessControl::LaunchExecutor() { +static Error loadProcessSymbols(Session &S) { + auto FilterMainEntryPoint = + [EPName = S.ES.intern(EntryPointName)](SymbolStringPtr Name) { + return Name != EPName; + }; + S.MainJD->addGenerator( + ExitOnErr(orc::EPCDynamicLibrarySearchGenerator::GetForTargetProcess( + S.ES, std::move(FilterMainEntryPoint)))); + + return Error::success(); +} + +static Error loadDylibs(Session &S) { + LLVM_DEBUG(dbgs() << "Loading dylibs...\n"); + for (const auto &Dylib : Dylibs) { + LLVM_DEBUG(dbgs() << " " << Dylib << "\n"); + auto G = orc::EPCDynamicLibrarySearchGenerator::Load(S.ES, Dylib.c_str()); + if (!G) + return G.takeError(); + S.MainJD->addGenerator(std::move(*G)); + } + + return Error::success(); +} + +Expected<std::unique_ptr<ExecutorProcessControl>> +LLVMJITLinkRemoteExecutorProcessControl::LaunchExecutor() { #ifndef LLVM_ON_UNIX // FIXME: Add support for Windows. return make_error<StringError>("-" + OutOfProcessExecutor.ArgStr + @@ -648,18 +712,64 @@ }; Error Err = Error::success(); - std::unique_ptr<LLVMJITLinkRemoteTargetProcessControl> RTPC( - new LLVMJITLinkRemoteTargetProcessControl( + std::unique_ptr<LLVMJITLinkRemoteExecutorProcessControl> REPC( + new LLVMJITLinkRemoteExecutorProcessControl( std::move(SSP), std::move(Channel), std::move(Endpoint), std::move(ReportError), Err)); if (Err) return std::move(Err); - return std::move(RTPC); + return std::move(REPC); #endif } -Expected<std::unique_ptr<TargetProcessControl>> -LLVMJITLinkRemoteTargetProcessControl::ConnectToExecutor() { +#ifdef LLVM_ON_UNIX +static Error createTCPSocketError(Twine Details) { + return make_error<StringError>( + formatv("Failed to connect TCP socket '{0}': {1}", + OutOfProcessExecutorConnect, Details), + inconvertibleErrorCode()); +} + +static Expected<int> connectTCPSocket(std::string Host, std::string PortStr) { + addrinfo *AI; + addrinfo Hints{}; + Hints.ai_family = AF_INET; + Hints.ai_socktype = SOCK_STREAM; + Hints.ai_flags = AI_NUMERICSERV; + + if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI)) + return createTCPSocketError("Address resolution failed (" + + StringRef(gai_strerror(EC)) + ")"); + + // Cycle through the returned addrinfo structures and connect to the first + // reachable endpoint. + int SockFD; + addrinfo *Server; + for (Server = AI; Server != nullptr; Server = Server->ai_next) { + // socket might fail, e.g. if the address family is not supported. Skip to + // the next addrinfo structure in such a case. + if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) + continue; + + // If connect returns null, we exit the loop with a working socket. + if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0) + break; + + close(SockFD); + } + freeaddrinfo(AI); + + // If we reached the end of the loop without connecting to a valid endpoint, + // dump the last error that was logged in socket() or connect(). + if (Server == nullptr) + return createTCPSocketError(std::strerror(errno)); + + return SockFD; +} +#endif + +Expected<std::unique_ptr<ExecutorProcessControl>> +LLVMJITLinkRemoteExecutorProcessControl::ConnectToExecutor() { #ifndef LLVM_ON_UNIX // FIXME: Add TCP support for Windows. return make_error<StringError>("-" + OutOfProcessExecutorConnect.ArgStr + @@ -669,42 +779,27 @@ shared::registerStringError<LLVMJITLinkChannel>(); - StringRef HostNameStr, PortStr; - std::tie(HostNameStr, PortStr) = - StringRef(OutOfProcessExecutorConnect).split(':'); - - if (HostNameStr.empty()) - return make_error<StringError>("host name for -" + - OutOfProcessExecutorConnect.ArgStr + - " can not be empty", - inconvertibleErrorCode()); + StringRef Host, PortStr; + std::tie(Host, PortStr) = StringRef(OutOfProcessExecutorConnect).split(':'); + if (Host.empty()) + return createTCPSocketError("Host name for -" + + OutOfProcessExecutorConnect.ArgStr + + " can not be empty"); if (PortStr.empty()) - return make_error<StringError>( - "port for -" + OutOfProcessExecutorConnect.ArgStr + " can not be empty", - inconvertibleErrorCode()); - - std::string HostName = HostNameStr.str(); + return createTCPSocketError("Port number in -" + + OutOfProcessExecutorConnect.ArgStr + + " can not be empty"); int Port = 0; if (PortStr.getAsInteger(10, Port)) - return make_error<StringError>("port number " + PortStr + - " is not a valid integer", - inconvertibleErrorCode()); + return createTCPSocketError("Port number '" + PortStr + + "' is not a valid integer"); - int SockFD = socket(PF_INET, SOCK_STREAM, 0); - hostent *Server = gethostbyname(HostName.c_str()); - sockaddr_in ServAddr; - memset(&ServAddr, 0, sizeof(ServAddr)); - ServAddr.sin_family = PF_INET; - memmove(&Server->h_addr, &ServAddr.sin_addr.s_addr, Server->h_length); - ServAddr.sin_port = htons(Port); - if (connect(SockFD, reinterpret_cast<sockaddr *>(&ServAddr), - sizeof(ServAddr)) < 0) - return make_error<StringError>("Failed to connect to " + HostName + ":" + - Twine(Port), - inconvertibleErrorCode()); + Expected<int> SockFD = connectTCPSocket(Host.str(), PortStr.str()); + if (!SockFD) + return SockFD.takeError(); auto SSP = std::make_shared<SymbolStringPool>(); - auto Channel = std::make_unique<shared::FDRawByteChannel>(SockFD, SockFD); + auto Channel = std::make_unique<shared::FDRawByteChannel>(*SockFD, *SockFD); auto Endpoint = std::make_unique<LLVMJITLinkRPCEndpoint>(*Channel, true); auto ReportError = [](Error Err) { @@ -712,17 +807,17 @@ }; Error Err = Error::success(); - std::unique_ptr<LLVMJITLinkRemoteTargetProcessControl> RTPC( - new LLVMJITLinkRemoteTargetProcessControl( + std::unique_ptr<LLVMJITLinkRemoteExecutorProcessControl> REPC( + new LLVMJITLinkRemoteExecutorProcessControl( std::move(SSP), std::move(Channel), std::move(Endpoint), std::move(ReportError), Err)); if (Err) return std::move(Err); - return std::move(RTPC); + return std::move(REPC); #endif } -Error LLVMJITLinkRemoteTargetProcessControl::disconnect() { +Error LLVMJITLinkRemoteExecutorProcessControl::disconnect() { std::promise<MSVCPError> P; auto F = P.get_future(); auto Err = closeConnection([&](Error Err) -> Error { @@ -752,27 +847,33 @@ if (!PageSize) return PageSize.takeError(); - /// If -oop-executor is passed then launch the executor. - std::unique_ptr<TargetProcessControl> TPC; + std::unique_ptr<ExecutorProcessControl> EPC; if (OutOfProcessExecutor.getNumOccurrences()) { - if (auto RTPC = LLVMJITLinkRemoteTargetProcessControl::LaunchExecutor()) - TPC = std::move(*RTPC); + /// If -oop-executor is passed then launch the executor. + if (auto REPC = LLVMJITLinkRemoteExecutorProcessControl::LaunchExecutor()) + EPC = std::move(*REPC); else - return RTPC.takeError(); + return REPC.takeError(); } else if (OutOfProcessExecutorConnect.getNumOccurrences()) { - if (auto RTPC = LLVMJITLinkRemoteTargetProcessControl::ConnectToExecutor()) - TPC = std::move(*RTPC); + /// If -oop-executor-connect is passed then connect to the executor. + if (auto REPC = + LLVMJITLinkRemoteExecutorProcessControl::ConnectToExecutor()) + EPC = std::move(*REPC); else - return RTPC.takeError(); - } else - TPC = std::make_unique<SelfTargetProcessControl>( + return REPC.takeError(); + } else { + /// Otherwise use SelfExecutorProcessControl to target the current process. + EPC = std::make_unique<SelfExecutorProcessControl>( std::make_shared<SymbolStringPool>(), std::move(TT), *PageSize, createMemoryManager()); + } Error Err = Error::success(); - std::unique_ptr<Session> S(new Session(std::move(TPC), Err)); - if (Err) - return std::move(Err); + std::unique_ptr<Session> S(new Session(std::move(EPC), Err)); + + // FIXME: Errors destroy the session, leaving the SymbolStringPtrs dangling, + // so just exit here. We could fix this by having errors keep the pool alive. + ExitOnErr(std::move(Err)); return std::move(S); } @@ -781,19 +882,18 @@ ES.reportError(std::move(Err)); } -// FIXME: Move to createJITDylib if/when we start using Platform support in -// llvm-jitlink. -Session::Session(std::unique_ptr<TargetProcessControl> TPC, Error &Err) - : TPC(std::move(TPC)), ObjLayer(*this, this->TPC->getMemMgr()) { +Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err) + : ES(std::move(EPC)), + ObjLayer(*this, ES.getExecutorProcessControl().getMemMgr()) { /// Local ObjectLinkingLayer::Plugin class to forward modifyPassConfig to the /// Session. class JITLinkSessionPlugin : public ObjectLinkingLayer::Plugin { public: JITLinkSessionPlugin(Session &S) : S(S) {} - void modifyPassConfig(MaterializationResponsibility &MR, const Triple &TT, + void modifyPassConfig(MaterializationResponsibility &MR, LinkGraph &G, PassConfiguration &PassConfig) override { - S.modifyPassConfig(TT, PassConfig); + S.modifyPassConfig(G.getTargetTriple(), PassConfig); } Error notifyFailed(MaterializationResponsibility &MR) override { @@ -818,9 +918,26 @@ return; } - if (!NoExec && !this->TPC->getTargetTriple().isOSWindows()) + if (!NoProcessSymbols) + ExitOnErr(loadProcessSymbols(*this)); + ExitOnErr(loadDylibs(*this)); + + // Set up the platform. + auto &TT = ES.getExecutorProcessControl().getTargetTriple(); + if (TT.isOSBinFormatMachO() && UseOrcRuntime) { + if (auto P = MachOPlatform::Create(ES, ObjLayer, *MainJD, + OrcRuntimePath.c_str())) + ES.setPlatform(std::move(*P)); + else { + Err = P.takeError(); + return; + } + } else if (!NoExec && !TT.isOSWindows() && !TT.isOSBinFormatMachO()) { ObjLayer.addPlugin(std::make_unique<EHFrameRegistrationPlugin>( - ES, ExitOnErr(TPCEHFrameRegistrar::Create(*this->TPC)))); + ES, ExitOnErr(EPCEHFrameRegistrar::Create(this->ES)))); + ObjLayer.addPlugin(std::make_unique<DebugObjectManagerPlugin>( + ES, ExitOnErr(createJITLoaderGDBRegistrar(this->ES)))); + } ObjLayer.addPlugin(std::make_unique<JITLinkSessionPlugin>(*this)); @@ -866,10 +983,11 @@ PassConfiguration &PassConfig) { if (!CheckFiles.empty()) PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) { - if (TPC->getTargetTriple().getObjectFormat() == Triple::ELF) + auto &EPC = ES.getExecutorProcessControl(); + if (EPC.getTargetTriple().getObjectFormat() == Triple::ELF) return registerELFGraphInfo(*this, G); - if (TPC->getTargetTriple().getObjectFormat() == Triple::MachO) + if (EPC.getTargetTriple().getObjectFormat() == Triple::MachO) return registerMachOGraphInfo(*this, G); return make_error<StringError>("Unsupported object format for GOT/stub " @@ -975,28 +1093,62 @@ static Triple getFirstFileTriple() { static Triple FirstTT = []() { assert(!InputFiles.empty() && "InputFiles can not be empty"); - auto ObjBuffer = - ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFiles.front()))); - auto Obj = ExitOnErr( - object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef())); - return Obj->makeTriple(); + for (auto InputFile : InputFiles) { + auto ObjBuffer = + ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFile))); + switch (identify_magic(ObjBuffer->getBuffer())) { + case file_magic::elf_relocatable: + case file_magic::macho_object: + case file_magic::coff_object: { + auto Obj = ExitOnErr( + object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef())); + return Obj->makeTriple(); + } + default: + break; + } + } + return Triple(); }(); return FirstTT; } -static Error sanitizeArguments(const Triple &TT, const char *ArgV0) { - // Set the entry point name if not specified. - if (EntryPointName.empty()) { - if (TT.getObjectFormat() == Triple::MachO) - EntryPointName = "_main"; - else - EntryPointName = "main"; +static bool isOrcRuntimeSupported(const Triple &TT) { + switch (TT.getObjectFormat()) { + case Triple::MachO: + switch (TT.getArch()) { + case Triple::x86_64: + return true; + default: + return false; + } + default: + return false; } +} + +static Error sanitizeArguments(const Triple &TT, const char *ArgV0) { + + // If we're in noexec mode and the user didn't explicitly specify + // -use-orc-runtime then don't use it. + if (NoExec && UseOrcRuntime.getNumOccurrences() == 0) + UseOrcRuntime = false; // -noexec and --args should not be used together. if (NoExec && !InputArgv.empty()) - outs() << "Warning: --args passed to -noexec run will be ignored.\n"; + errs() << "Warning: --args passed to -noexec run will be ignored.\n"; + + // Turn off UseOrcRuntime on platforms where it's not supported. + if (UseOrcRuntime && !isOrcRuntimeSupported(TT)) { + errs() << "Warning: Orc runtime not available for target platform. " + "Use -use-orc-runtime=false to suppress this warning.\n"; + UseOrcRuntime = false; + } + + // Set the entry point name if not specified. + if (EntryPointName.empty()) + EntryPointName = TT.getObjectFormat() == Triple::MachO ? "_main" : "main"; // If -slab-address is passed, require -slab-allocate and -noexec if (SlabAddress != ~0ULL) { @@ -1021,35 +1173,31 @@ SmallString<256> OOPExecutorPath(sys::fs::getMainExecutable( ArgV0, reinterpret_cast<void *>(&sanitizeArguments))); sys::path::remove_filename(OOPExecutorPath); - if (OOPExecutorPath.back() != '/') - OOPExecutorPath += '/'; - OOPExecutorPath += "llvm-jitlink-executor"; + sys::path::append(OOPExecutorPath, "llvm-jitlink-executor"); OutOfProcessExecutor = OOPExecutorPath.str().str(); } - return Error::success(); -} - -static Error loadProcessSymbols(Session &S) { - auto FilterMainEntryPoint = - [EPName = S.ES.intern(EntryPointName)](SymbolStringPtr Name) { - return Name != EPName; - }; - S.MainJD->addGenerator( - ExitOnErr(orc::TPCDynamicLibrarySearchGenerator::GetForTargetProcess( - *S.TPC, std::move(FilterMainEntryPoint)))); - - return Error::success(); -} - -static Error loadDylibs(Session &S) { - for (const auto &Dylib : Dylibs) { - auto G = orc::TPCDynamicLibrarySearchGenerator::Load(*S.TPC, Dylib.c_str()); - if (!G) - return G.takeError(); - S.MainJD->addGenerator(std::move(*G)); + // If we're loading the Orc runtime then determine the path for it. + if (UseOrcRuntime) { + if (OrcRuntimePath.empty()) { + SmallString<256> DefaultOrcRuntimePath(sys::fs::getMainExecutable( + ArgV0, reinterpret_cast<void *>(&sanitizeArguments))); + sys::path::remove_filename( + DefaultOrcRuntimePath); // remove 'llvm-jitlink' + while (!DefaultOrcRuntimePath.empty() && + DefaultOrcRuntimePath.back() == '/') + DefaultOrcRuntimePath.pop_back(); + if (DefaultOrcRuntimePath.endswith("bin")) + sys::path::remove_filename(DefaultOrcRuntimePath); // remove 'bin' + sys::path::append(DefaultOrcRuntimePath, + ("lib/clang/" + Twine(LLVM_VERSION_MAJOR) + "." + + Twine(LLVM_VERSION_MINOR) + "." + + Twine(LLVM_VERSION_PATCH) + + "/lib/darwin/libclang_rt.orc_osx.a") + .str()); + OrcRuntimePath = DefaultOrcRuntimePath.str().str(); + } } - return Error::success(); } @@ -1120,7 +1268,8 @@ if (Magic == file_magic::archive || Magic == file_magic::macho_universal_binary) JD.addGenerator(ExitOnErr(StaticLibraryDefinitionGenerator::Load( - S.ObjLayer, InputFile.c_str(), S.TPC->getTargetTriple()))); + S.ObjLayer, InputFile.c_str(), + S.ES.getExecutorProcessControl().getTargetTriple()))); else ExitOnErr(S.ObjLayer.add(JD, std::move(ObjBuffer))); } @@ -1153,7 +1302,7 @@ return Err; // Register the absolute symbol with the session symbol infos. - S.SymbolInfos[Name] = { StringRef(), Addr }; + S.SymbolInfos[Name] = {ArrayRef<char>(), Addr}; } LLVM_DEBUG({ @@ -1167,8 +1316,14 @@ } static Error runChecks(Session &S) { + const auto &TT = S.ES.getExecutorProcessControl().getTargetTriple(); - auto TripleName = S.TPC->getTargetTriple().str(); + if (CheckFiles.empty()) + return Error::success(); + + LLVM_DEBUG(dbgs() << "Running checks...\n"); + + auto TripleName = TT.str(); std::string ErrorStr; const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, ErrorStr); if (!TheTarget) @@ -1198,7 +1353,7 @@ TripleName, inconvertibleErrorCode())); - MCContext Ctx(MAI.get(), MRI.get(), nullptr); + MCContext Ctx(Triple(TripleName), MAI.get(), MRI.get(), STI.get()); std::unique_ptr<MCDisassembler> Disassembler( TheTarget->createMCDisassembler(*STI, Ctx)); @@ -1234,9 +1389,8 @@ RuntimeDyldChecker Checker( IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, GetGOTInfo, - S.TPC->getTargetTriple().isLittleEndian() ? support::little - : support::big, - Disassembler.get(), InstPrinter.get(), dbgs()); + TT.isLittleEndian() ? support::little : support::big, Disassembler.get(), + InstPrinter.get(), dbgs()); std::string CheckLineStart = "# " + CheckName + ":"; for (auto &CheckFile : CheckFiles) { @@ -1251,9 +1405,15 @@ } static void dumpSessionStats(Session &S) { + if (!ShowSizes) + return; + if (UseOrcRuntime) + outs() << "Note: Session stats include runtime and entry point lookup, but " + "not JITDylib initialization/deinitialization.\n"; if (ShowSizes) - outs() << "Total size of all blocks before pruning: " << S.SizeBeforePruning - << "\nTotal size of all blocks after fixups: " << S.SizeAfterFixups + outs() << " Total size of all blocks before pruning: " + << S.SizeBeforePruning + << "\n Total size of all blocks after fixups: " << S.SizeAfterFixups << "\n"; } @@ -1261,6 +1421,37 @@ return S.ES.lookup(S.JDSearchOrder, EntryPointName); } +static Expected<JITEvaluatedSymbol> getOrcRuntimeEntryPoint(Session &S) { + std::string RuntimeEntryPoint = "__orc_rt_run_program_wrapper"; + const auto &TT = S.ES.getExecutorProcessControl().getTargetTriple(); + if (TT.getObjectFormat() == Triple::MachO) + RuntimeEntryPoint = '_' + RuntimeEntryPoint; + return S.ES.lookup(S.JDSearchOrder, RuntimeEntryPoint); +} + +static Expected<int> runWithRuntime(Session &S, + JITTargetAddress EntryPointAddress) { + StringRef DemangledEntryPoint = EntryPointName; + const auto &TT = S.ES.getExecutorProcessControl().getTargetTriple(); + if (TT.getObjectFormat() == Triple::MachO && + DemangledEntryPoint.front() == '_') + DemangledEntryPoint = DemangledEntryPoint.drop_front(); + using SPSRunProgramSig = + int64_t(SPSString, SPSString, SPSSequence<SPSString>); + int64_t Result; + if (auto Err = S.ES.callSPSWrapper<SPSRunProgramSig>( + EntryPointAddress, Result, S.MainJD->getName(), DemangledEntryPoint, + static_cast<std::vector<std::string> &>(InputArgv))) + return std::move(Err); + return Result; +} + +static Expected<int> runWithoutRuntime(Session &S, + JITTargetAddress EntryPointAddress) { + return S.ES.getExecutorProcessControl().runAsMain(EntryPointAddress, + InputArgv); +} + namespace { struct JITLinkTimers { TimerGroup JITLinkTG{"llvm-jitlink timers", "timers for llvm-jitlink phases"}; @@ -1277,6 +1468,7 @@ InitializeAllTargetMCs(); InitializeAllDisassemblers(); + cl::HideUnrelatedOptions({&JITLinkCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm jitlink tool"); ExitOnErr.setBanner(std::string(argv[0]) + ": "); @@ -1293,21 +1485,23 @@ ExitOnErr(loadObjects(*S)); } - if (!NoProcessSymbols) - ExitOnErr(loadProcessSymbols(*S)); - ExitOnErr(loadDylibs(*S)); - if (PhonyExternals) addPhonyExternalsGenerator(*S); - if (ShowInitialExecutionSessionState) S->ES.dump(outs()); JITEvaluatedSymbol EntryPoint = 0; { TimeRegion TR(Timers ? &Timers->LinkTimer : nullptr); + // Find the entry-point function unconditionally, since we want to force + // it to be materialized to collect stats. EntryPoint = ExitOnErr(getMainEntryPoint(*S)); + + // If we're running with the ORC runtime then replace the entry-point + // with the __orc_rt_run_program symbol. + if (UseOrcRuntime) + EntryPoint = ExitOnErr(getOrcRuntimeEntryPoint(*S)); } if (ShowAddrs) @@ -1322,12 +1516,20 @@ int Result = 0; { + LLVM_DEBUG(dbgs() << "Running \"" << EntryPointName << "\"...\n"); TimeRegion TR(Timers ? &Timers->RunTimer : nullptr); - Result = ExitOnErr(S->TPC->runAsMain(EntryPoint.getAddress(), InputArgv)); + if (UseOrcRuntime) + Result = ExitOnErr(runWithRuntime(*S, EntryPoint.getAddress())); + else + Result = ExitOnErr(runWithoutRuntime(*S, EntryPoint.getAddress())); } - ExitOnErr(S->ES.endSession()); - ExitOnErr(S->TPC->disconnect()); + // Destroy the session. + S.reset(); + + // If the executing code set a test result override then use that. + if (UseTestResultOverride) + Result = TestResultOverride; return Result; }
diff --git a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink.h b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink.h index 13b5592..acb64a9 100644 --- a/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink.h +++ b/src/llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink.h
@@ -17,11 +17,11 @@ #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" #include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" -#include "llvm/ExecutionEngine/Orc/OrcRPCTargetProcessControl.h" +#include "llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h" #include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h" #include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h" -#include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" #include "llvm/ExecutionEngine/RuntimeDyldChecker.h" #include "llvm/Support/Error.h" #include "llvm/Support/Regex.h" @@ -51,29 +51,27 @@ using LLVMJITLinkChannel = orc::shared::FDRawByteChannel; using LLVMJITLinkRPCEndpoint = orc::shared::MultiThreadedRPCEndpoint<LLVMJITLinkChannel>; -using LLVMJITLinkRemoteMemoryManager = - orc::OrcRPCTPCJITLinkMemoryManager<LLVMJITLinkRPCEndpoint>; using LLVMJITLinkRemoteMemoryAccess = - orc::OrcRPCTPCMemoryAccess<LLVMJITLinkRPCEndpoint>; + orc::OrcRPCEPCMemoryAccess<LLVMJITLinkRPCEndpoint>; -class LLVMJITLinkRemoteTargetProcessControl - : public orc::OrcRPCTargetProcessControlBase<LLVMJITLinkRPCEndpoint> { +class LLVMJITLinkRemoteExecutorProcessControl + : public orc::OrcRPCExecutorProcessControlBase<LLVMJITLinkRPCEndpoint> { public: - using BaseT = orc::OrcRPCTargetProcessControlBase<LLVMJITLinkRPCEndpoint>; - static Expected<std::unique_ptr<TargetProcessControl>> LaunchExecutor(); + using BaseT = orc::OrcRPCExecutorProcessControlBase<LLVMJITLinkRPCEndpoint>; + static Expected<std::unique_ptr<ExecutorProcessControl>> LaunchExecutor(); - static Expected<std::unique_ptr<TargetProcessControl>> ConnectToExecutor(); + static Expected<std::unique_ptr<ExecutorProcessControl>> ConnectToExecutor(); Error disconnect() override; private: using LLVMJITLinkRemoteMemoryAccess = - orc::OrcRPCTPCMemoryAccess<LLVMJITLinkRemoteTargetProcessControl>; + orc::OrcRPCEPCMemoryAccess<LLVMJITLinkRemoteExecutorProcessControl>; - using LLVMJITLinkRemoteMemoryManager = - orc::OrcRPCTPCJITLinkMemoryManager<LLVMJITLinkRemoteTargetProcessControl>; + using LLVMJITLinkRemoteMemoryManager = orc::OrcRPCEPCJITLinkMemoryManager< + LLVMJITLinkRemoteExecutorProcessControl>; - LLVMJITLinkRemoteTargetProcessControl( + LLVMJITLinkRemoteExecutorProcessControl( std::shared_ptr<orc::SymbolStringPool> SSP, std::unique_ptr<LLVMJITLinkChannel> Channel, std::unique_ptr<LLVMJITLinkRPCEndpoint> Endpoint, @@ -91,7 +89,7 @@ } }); - if (auto Err2 = initializeORCRPCTPCBase()) { + if (auto Err2 = initializeORCRPCEPCBase()) { Err = joinErrors(std::move(Err2), disconnect()); return; } @@ -104,16 +102,15 @@ std::unique_ptr<LLVMJITLinkChannel> Channel; std::unique_ptr<LLVMJITLinkRPCEndpoint> Endpoint; - std::unique_ptr<TargetProcessControl::MemoryAccess> OwnedMemAccess; + std::unique_ptr<ExecutorProcessControl::MemoryAccess> OwnedMemAccess; std::unique_ptr<jitlink::JITLinkMemoryManager> OwnedMemMgr; std::atomic<bool> Finished{false}; std::thread ListenerThread; }; struct Session { - std::unique_ptr<orc::TargetProcessControl> TPC; orc::ExecutionSession ES; - orc::JITDylib *MainJD; + orc::JITDylib *MainJD = nullptr; LLVMJITLinkObjectLinkingLayer ObjLayer; std::vector<orc::JITDylib *> JDSearchOrder; @@ -158,7 +155,7 @@ DenseMap<StringRef, StringRef> CanonicalWeakDefs; private: - Session(std::unique_ptr<orc::TargetProcessControl> TPC, Error &Err); + Session(std::unique_ptr<orc::ExecutorProcessControl> EPC, Error &Err); }; /// Record symbols, GOT entries, stubs, and sections for ELF file.
diff --git a/src/llvm-project/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp b/src/llvm-project/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp index 1ff6328..ef4aec5 100644 --- a/src/llvm-project/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp +++ b/src/llvm-project/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp
@@ -21,8 +21,9 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/LineIterator.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/WithColor.h" -#include "llvm/TextAPI/MachO/Architecture.h" +#include "llvm/TextAPI/Architecture.h" #include <map> using namespace llvm; @@ -89,6 +90,11 @@ VersionOption("V", cl::desc("Print the version number and exit"), cl::cat(LibtoolCategory)); +static cl::opt<bool> NoWarningForNoSymbols( + "no_warning_for_no_symbols", + cl::desc("Do not warn about files that have no symbols"), + cl::cat(LibtoolCategory), cl::init(false)); + static const std::array<std::string, 3> StandardSearchDirs{ "/lib", "/usr/lib", @@ -142,7 +148,7 @@ std::tie(FileName, DirName) = StringRef(FileList).rsplit(","); ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = - MemoryBuffer::getFileOrSTDIN(FileName, /*FileSize=*/-1, + MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (std::error_code EC = FileOrErr.getError()) return createFileError(FileName, errorCodeToError(EC)); @@ -251,6 +257,9 @@ return Error::success(); } + if (!NoWarningForNoSymbols && O->symbols().empty()) + WithColor::warning() << Member.MemberName + " has no symbols\n"; + uint64_t FileCPUID = getCPUID(FileCPUType, FileCPUSubtype); Members[FileCPUID].push_back(std::move(Member)); return Error::success(); @@ -562,7 +571,7 @@ int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); - cl::HideUnrelatedOptions({&LibtoolCategory, &ColorCategory}); + cl::HideUnrelatedOptions({&LibtoolCategory, &getColorCategory()}); Expected<Config> ConfigOrErr = parseCommandLine(Argc, Argv); if (!ConfigOrErr) { WithColor::defaultErrorHandler(ConfigOrErr.takeError());
diff --git a/src/llvm-project/llvm/tools/llvm-link/llvm-link.cpp b/src/llvm-project/llvm/tools/llvm-link/llvm-link.cpp index eed49c4..9abe8ef 100644 --- a/src/llvm-project/llvm/tools/llvm-link/llvm-link.cpp +++ b/src/llvm-project/llvm/tools/llvm-link/llvm-link.cpp
@@ -41,21 +41,25 @@ #include <utility> using namespace llvm; -static cl::list<std::string> -InputFilenames(cl::Positional, cl::OneOrMore, - cl::desc("<input bitcode files>")); +static cl::OptionCategory LinkCategory("Link Options"); + +static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, + cl::desc("<input bitcode files>"), + cl::cat(LinkCategory)); static cl::list<std::string> OverridingInputs( "override", cl::ZeroOrMore, cl::value_desc("filename"), cl::desc( - "input bitcode file which can override previously defined symbol(s)")); + "input bitcode file which can override previously defined symbol(s)"), + cl::cat(LinkCategory)); // Option to simulate function importing for testing. This enables using // llvm-link to simulate ThinLTO backend processes. static cl::list<std::string> Imports( "import", cl::ZeroOrMore, cl::value_desc("function:filename"), cl::desc("Pair of function name and filename, where function should be " - "imported from bitcode in filename")); + "imported from bitcode in filename"), + cl::cat(LinkCategory)); // Option to support testing of function importing. The module summary // must be specified in the case were we request imports via the -import @@ -64,51 +68,61 @@ // consistent promotion and renaming of locals. static cl::opt<std::string> SummaryIndex("summary-index", cl::desc("Module summary index filename"), - cl::init(""), cl::value_desc("filename")); + cl::init(""), cl::value_desc("filename"), + cl::cat(LinkCategory)); static cl::opt<std::string> -OutputFilename("o", cl::desc("Override output filename"), cl::init("-"), - cl::value_desc("filename")); + OutputFilename("o", cl::desc("Override output filename"), cl::init("-"), + cl::value_desc("filename"), cl::cat(LinkCategory)); -static cl::opt<bool> -Internalize("internalize", cl::desc("Internalize linked symbols")); +static cl::opt<bool> Internalize("internalize", + cl::desc("Internalize linked symbols"), + cl::cat(LinkCategory)); static cl::opt<bool> DisableDITypeMap("disable-debug-info-type-map", - cl::desc("Don't use a uniquing type map for debug info")); + cl::desc("Don't use a uniquing type map for debug info"), + cl::cat(LinkCategory)); -static cl::opt<bool> -OnlyNeeded("only-needed", cl::desc("Link only needed symbols")); +static cl::opt<bool> OnlyNeeded("only-needed", + cl::desc("Link only needed symbols"), + cl::cat(LinkCategory)); -static cl::opt<bool> -Force("f", cl::desc("Enable binary output on terminals")); +static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"), + cl::cat(LinkCategory)); -static cl::opt<bool> - DisableLazyLoad("disable-lazy-loading", - cl::desc("Disable lazy module loading")); +static cl::opt<bool> DisableLazyLoad("disable-lazy-loading", + cl::desc("Disable lazy module loading"), + cl::cat(LinkCategory)); -static cl::opt<bool> - OutputAssembly("S", cl::desc("Write output as LLVM assembly"), cl::Hidden); +static cl::opt<bool> OutputAssembly("S", + cl::desc("Write output as LLVM assembly"), + cl::Hidden, cl::cat(LinkCategory)); -static cl::opt<bool> -Verbose("v", cl::desc("Print information about actions taken")); +static cl::opt<bool> Verbose("v", + cl::desc("Print information about actions taken"), + cl::cat(LinkCategory)); -static cl::opt<bool> -DumpAsm("d", cl::desc("Print assembly as linked"), cl::Hidden); +static cl::opt<bool> DumpAsm("d", cl::desc("Print assembly as linked"), + cl::Hidden, cl::cat(LinkCategory)); -static cl::opt<bool> -SuppressWarnings("suppress-warnings", cl::desc("Suppress all linking warnings"), - cl::init(false)); +static cl::opt<bool> SuppressWarnings("suppress-warnings", + cl::desc("Suppress all linking warnings"), + cl::init(false), cl::cat(LinkCategory)); static cl::opt<bool> PreserveBitcodeUseListOrder( "preserve-bc-uselistorder", cl::desc("Preserve use-list order when writing LLVM bitcode."), - cl::init(true), cl::Hidden); + cl::init(true), cl::Hidden, cl::cat(LinkCategory)); static cl::opt<bool> PreserveAssemblyUseListOrder( "preserve-ll-uselistorder", cl::desc("Preserve use-list order when writing LLVM assembly."), - cl::init(false), cl::Hidden); + cl::init(false), cl::Hidden, cl::cat(LinkCategory)); + +static cl::opt<bool> NoVerify("disable-verify", + cl::desc("Do not run the verifier"), cl::Hidden, + cl::cat(LinkCategory)); static ExitOnError ExitOnErr; @@ -158,9 +172,8 @@ Expected<StringRef> Ename = C.getName(); if (Error E = Ename.takeError()) { errs() << Argv0 << ": "; - WithColor::error() - << " failed to read name of archive member" - << ArchiveName << "'\n"; + WithColor::error() << " failed to read name of archive member" + << ArchiveName << "'\n"; return nullptr; } std::string ChildName = Ename.get().str(); @@ -177,10 +190,10 @@ return nullptr; }; - if (!isBitcode(reinterpret_cast<const unsigned char *> - (MemBuf.get().getBufferStart()), - reinterpret_cast<const unsigned char *> - (MemBuf.get().getBufferEnd()))) { + if (!isBitcode(reinterpret_cast<const unsigned char *>( + MemBuf.get().getBufferStart()), + reinterpret_cast<const unsigned char *>( + MemBuf.get().getBufferEnd()))) { errs() << Argv0 << ": "; WithColor::error() << " member of archive is not a bitcode file: '" << ChildName << "'\n"; @@ -246,8 +259,10 @@ Module &ModuleLazyLoaderCache::operator()(const char *argv0, const std::string &Identifier) { auto &Module = ModuleMap[Identifier]; - if (!Module) + if (!Module) { Module = createLazyModule(argv0, Identifier); + assert(Module && "Failed to create lazy module!"); + } return *Module; } } // anonymous namespace @@ -276,7 +291,7 @@ return true; } }; -} +} // namespace /// Import any functions requested via the -import option. static bool importFunctions(const char *argv0, Module &DestModule) { @@ -309,7 +324,7 @@ // Load the specified source module. auto &SrcModule = ModuleLoaderCache(argv0, FileName); - if (verifyModule(SrcModule, &errs())) { + if (!NoVerify && verifyModule(SrcModule, &errs())) { errs() << argv0 << ": " << FileName; WithColor::error() << "input module is broken!\n"; return false; @@ -347,8 +362,7 @@ } static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, - const cl::list<std::string> &Files, - unsigned Flags) { + const cl::list<std::string> &Files, unsigned Flags) { // Filter out flags that don't apply to the first file we load. unsigned ApplicableFlags = Flags & Linker::Flags::OverrideFromSrc; // Similar to some flags, internalization doesn't apply to the first file. @@ -370,7 +384,7 @@ // Note that when ODR merging types cannot verify input files in here When // doing that debug metadata in the src module might already be pointing to // the destination. - if (DisableDITypeMap && verifyModule(*M, &errs())) { + if (DisableDITypeMap && !NoVerify && verifyModule(*M, &errs())) { errs() << argv0 << ": " << File << ": "; WithColor::error() << "input module is broken!\n"; return false; @@ -431,8 +445,9 @@ ExitOnErr.setBanner(std::string(argv[0]) + ": "); LLVMContext Context; - Context.setDiagnosticHandler( - std::make_unique<LLVMLinkDiagnosticHandler>(), true); + Context.setDiagnosticHandler(std::make_unique<LLVMLinkDiagnosticHandler>(), + true); + cl::HideUnrelatedOptions({&LinkCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm linker\n"); if (!DisableDITypeMap) @@ -463,13 +478,14 @@ std::error_code EC; ToolOutputFile Out(OutputFilename, EC, - OutputAssembly ? sys::fs::OF_Text : sys::fs::OF_None); + OutputAssembly ? sys::fs::OF_TextWithCRLF + : sys::fs::OF_None); if (EC) { WithColor::error() << EC.message() << '\n'; return 1; } - if (verifyModule(*Composite, &errs())) { + if (!NoVerify && verifyModule(*Composite, &errs())) { errs() << argv[0] << ": "; WithColor::error() << "linked module is broken!\n"; return 1;
diff --git a/src/llvm-project/llvm/tools/llvm-lipo/llvm-lipo.cpp b/src/llvm-project/llvm/tools/llvm-lipo/llvm-lipo.cpp index 7fbe489..d099ea1 100644 --- a/src/llvm-project/llvm/tools/llvm-lipo/llvm-lipo.cpp +++ b/src/llvm-project/llvm/tools/llvm-lipo/llvm-lipo.cpp
@@ -28,7 +28,7 @@ #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/WithColor.h" -#include "llvm/TextAPI/MachO/Architecture.h" +#include "llvm/TextAPI/Architecture.h" using namespace llvm; using namespace llvm::object; @@ -159,14 +159,14 @@ " option"); if (InputArgs.size() == 0) { - // PrintHelp does not accept Twine. - T.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo"); + // printHelp does not accept Twine. + T.printHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo"); exit(EXIT_FAILURE); } if (InputArgs.hasArg(LIPO_help)) { - // PrintHelp does not accept Twine. - T.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo"); + // printHelp does not accept Twine. + T.printHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo"); exit(EXIT_SUCCESS); }
diff --git a/src/llvm-project/llvm/tools/llvm-lto/llvm-lto.cpp b/src/llvm-project/llvm/tools/llvm-lto/llvm-lto.cpp index 1745b74..45bfa84 100644 --- a/src/llvm-project/llvm/tools/llvm-lto/llvm-lto.cpp +++ b/src/llvm-project/llvm/tools/llvm-lto/llvm-lto.cpp
@@ -64,31 +64,38 @@ static codegen::RegisterCodeGenFlags CGF; +static cl::OptionCategory LTOCategory("LTO Options"); + static cl::opt<char> - OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " - "(default = '-O2')"), - cl::Prefix, cl::ZeroOrMore, cl::init('2')); + OptLevel("O", + cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " + "(default = '-O2')"), + cl::Prefix, cl::ZeroOrMore, cl::init('2'), cl::cat(LTOCategory)); static cl::opt<bool> IndexStats("thinlto-index-stats", cl::desc("Print statistic for the index in every input files"), - cl::init(false)); + cl::init(false), cl::cat(LTOCategory)); static cl::opt<bool> DisableVerify( "disable-verify", cl::init(false), - cl::desc("Do not run the verifier during the optimization pipeline")); + cl::desc("Do not run the verifier during the optimization pipeline"), + cl::cat(LTOCategory)); static cl::opt<bool> EnableFreestanding( "lto-freestanding", cl::init(false), - cl::desc("Enable Freestanding (disable builtins / TLI) during LTO")); + cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"), + cl::cat(LTOCategory)); static cl::opt<bool> UseDiagnosticHandler( "use-diagnostic-handler", cl::init(false), - cl::desc("Use a diagnostic handler to test the handler interface")); + cl::desc("Use a diagnostic handler to test the handler interface"), + cl::cat(LTOCategory)); static cl::opt<bool> ThinLTO("thinlto", cl::init(false), - cl::desc("Only write combined global index for ThinLTO backends")); + cl::desc("Only write combined global index for ThinLTO backends"), + cl::cat(LTOCategory)); enum ThinLTOModes { THINLINK, @@ -114,113 +121,144 @@ "Emit imports files for distributed backends."), clEnumValN(THINPROMOTE, "promote", "Perform pre-import promotion (requires -thinlto-index)."), - clEnumValN(THINIMPORT, "import", "Perform both promotion and " - "cross-module importing (requires " - "-thinlto-index)."), + clEnumValN(THINIMPORT, "import", + "Perform both promotion and " + "cross-module importing (requires " + "-thinlto-index)."), clEnumValN(THININTERNALIZE, "internalize", "Perform internalization driven by -exported-symbol " "(requires -thinlto-index)."), clEnumValN(THINOPT, "optimize", "Perform ThinLTO optimizations."), clEnumValN(THINCODEGEN, "codegen", "CodeGen (expected to match llc)"), - clEnumValN(THINALL, "run", "Perform ThinLTO end-to-end"))); + clEnumValN(THINALL, "run", "Perform ThinLTO end-to-end")), + cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOIndex("thinlto-index", cl::desc("Provide the index produced by a ThinLink, required " - "to perform the promotion and/or importing.")); + "to perform the promotion and/or importing."), + cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOPrefixReplace( "thinlto-prefix-replace", cl::desc("Control where files for distributed backends are " "created. Expects 'oldprefix;newprefix' and if path " "prefix of output file is oldprefix it will be " - "replaced with newprefix.")); + "replaced with newprefix."), + cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOModuleId( "thinlto-module-id", cl::desc("For the module ID for the file to process, useful to " - "match what is in the index.")); + "match what is in the index."), + cl::cat(LTOCategory)); -static cl::opt<std::string> - ThinLTOCacheDir("thinlto-cache-dir", cl::desc("Enable ThinLTO caching.")); +static cl::opt<std::string> ThinLTOCacheDir("thinlto-cache-dir", + cl::desc("Enable ThinLTO caching."), + cl::cat(LTOCategory)); -static cl::opt<int> - ThinLTOCachePruningInterval("thinlto-cache-pruning-interval", - cl::init(1200), cl::desc("Set ThinLTO cache pruning interval.")); +static cl::opt<int> ThinLTOCachePruningInterval( + "thinlto-cache-pruning-interval", cl::init(1200), + cl::desc("Set ThinLTO cache pruning interval."), cl::cat(LTOCategory)); static cl::opt<uint64_t> ThinLTOCacheMaxSizeBytes( "thinlto-cache-max-size-bytes", - cl::desc("Set ThinLTO cache pruning directory maximum size in bytes.")); + cl::desc("Set ThinLTO cache pruning directory maximum size in bytes."), + cl::cat(LTOCategory)); -static cl::opt<int> - ThinLTOCacheMaxSizeFiles("thinlto-cache-max-size-files", cl::init(1000000), - cl::desc("Set ThinLTO cache pruning directory maximum number of files.")); +static cl::opt<int> ThinLTOCacheMaxSizeFiles( + "thinlto-cache-max-size-files", cl::init(1000000), + cl::desc("Set ThinLTO cache pruning directory maximum number of files."), + cl::cat(LTOCategory)); -static cl::opt<unsigned> - ThinLTOCacheEntryExpiration("thinlto-cache-entry-expiration", cl::init(604800) /* 1w */, - cl::desc("Set ThinLTO cache entry expiration time.")); +static cl::opt<unsigned> ThinLTOCacheEntryExpiration( + "thinlto-cache-entry-expiration", cl::init(604800) /* 1w */, + cl::desc("Set ThinLTO cache entry expiration time."), cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOSaveTempsPrefix( "thinlto-save-temps", cl::desc("Save ThinLTO temp files using filenames created by adding " - "suffixes to the given file path prefix.")); + "suffixes to the given file path prefix."), + cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOGeneratedObjectsDir( "thinlto-save-objects", cl::desc("Save ThinLTO generated object files using filenames created in " - "the given directory.")); + "the given directory."), + cl::cat(LTOCategory)); static cl::opt<bool> SaveLinkedModuleFile( "save-linked-module", cl::init(false), - cl::desc("Write linked LTO module to file before optimize")); + cl::desc("Write linked LTO module to file before optimize"), + cl::cat(LTOCategory)); static cl::opt<bool> SaveModuleFile("save-merged-module", cl::init(false), - cl::desc("Write merged LTO module to file before CodeGen")); + cl::desc("Write merged LTO module to file before CodeGen"), + cl::cat(LTOCategory)); static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, - cl::desc("<input bitcode files>")); + cl::desc("<input bitcode files>"), + cl::cat(LTOCategory)); static cl::opt<std::string> OutputFilename("o", cl::init(""), cl::desc("Override output filename"), - cl::value_desc("filename")); + cl::value_desc("filename"), + cl::cat(LTOCategory)); static cl::list<std::string> ExportedSymbols( "exported-symbol", cl::desc("List of symbols to export from the resulting object file"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(LTOCategory)); static cl::list<std::string> DSOSymbols("dso-symbol", cl::desc("Symbol to put in the symtab in the resulting dso"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(LTOCategory)); static cl::opt<bool> ListSymbolsOnly( "list-symbols-only", cl::init(false), - cl::desc("Instead of running LTO, list the symbols in each IR file")); + cl::desc("Instead of running LTO, list the symbols in each IR file"), + cl::cat(LTOCategory)); static cl::opt<bool> ListDependentLibrariesOnly( "list-dependent-libraries-only", cl::init(false), - cl::desc("Instead of running LTO, list the dependent libraries in each IR file")); + cl::desc( + "Instead of running LTO, list the dependent libraries in each IR file"), + cl::cat(LTOCategory)); -static cl::opt<bool> SetMergedModule( - "set-merged-module", cl::init(false), - cl::desc("Use the first input module as the merged module")); +static cl::opt<bool> + SetMergedModule("set-merged-module", cl::init(false), + cl::desc("Use the first input module as the merged module"), + cl::cat(LTOCategory)); static cl::opt<unsigned> Parallelism("j", cl::Prefix, cl::init(1), - cl::desc("Number of backend threads")); + cl::desc("Number of backend threads"), + cl::cat(LTOCategory)); static cl::opt<bool> RestoreGlobalsLinkage( "restore-linkage", cl::init(false), - cl::desc("Restore original linkage of globals prior to CodeGen")); + cl::desc("Restore original linkage of globals prior to CodeGen"), + cl::cat(LTOCategory)); static cl::opt<bool> CheckHasObjC( "check-for-objc", cl::init(false), - cl::desc("Only check if the module has objective-C defined in it")); + cl::desc("Only check if the module has objective-C defined in it"), + cl::cat(LTOCategory)); static cl::opt<bool> PrintMachOCPUOnly( "print-macho-cpu-only", cl::init(false), - cl::desc("Instead of running LTO, print the mach-o cpu in each IR file")); + cl::desc("Instead of running LTO, print the mach-o cpu in each IR file"), + cl::cat(LTOCategory)); + +static cl::opt<bool> UseNewPM( + "use-new-pm", cl::desc("Run LTO passes using the new pass manager"), + cl::init(LLVM_ENABLE_NEW_PASS_MANAGER), cl::Hidden, cl::cat(LTOCategory)); + +static cl::opt<bool> + DebugPassManager("debug-pass-manager", cl::init(false), cl::Hidden, + cl::desc("Print pass management debugging information"), + cl::cat(LTOCategory)); namespace { @@ -552,6 +590,8 @@ ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles); ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes); ThinGenerator.setFreestanding(EnableFreestanding); + ThinGenerator.setUseNewPM(UseNewPM); + ThinGenerator.setDebugPassManager(DebugPassManager); // Add all the exported symbols to the table of symbols to preserve. for (unsigned i = 0; i < ExportedSymbols.size(); ++i) @@ -884,6 +924,7 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); + cl::HideUnrelatedOptions({<OCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm LTO linker\n"); if (OptLevel < '0' || OptLevel > '3') @@ -1014,6 +1055,8 @@ CodeGen.setOptLevel(OptLevel - '0'); CodeGen.setAttrs(codegen::getMAttrs()); + CodeGen.setUseNewPM(UseNewPM); + if (auto FT = codegen::getExplicitFileType()) CodeGen.setFileType(FT.getValue()); @@ -1041,25 +1084,24 @@ error("writing merged module failed."); } - std::list<ToolOutputFile> OSs; - std::vector<raw_pwrite_stream *> OSPtrs; - for (unsigned I = 0; I != Parallelism; ++I) { + auto AddStream = + [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> { std::string PartFilename = OutputFilename; if (Parallelism != 1) - PartFilename += "." + utostr(I); + PartFilename += "." + utostr(Task); + std::error_code EC; - OSs.emplace_back(PartFilename, EC, sys::fs::OF_None); + auto S = + std::make_unique<raw_fd_ostream>(PartFilename, EC, sys::fs::OF_None); if (EC) error("error opening the file '" + PartFilename + "': " + EC.message()); - OSPtrs.push_back(&OSs.back().os()); - } + return std::make_unique<lto::NativeObjectStream>(std::move(S)); + }; - if (!CodeGen.compileOptimized(OSPtrs)) + if (!CodeGen.compileOptimized(AddStream, Parallelism)) // Diagnostic messages should have been printed by the handler. error("error compiling the code"); - for (ToolOutputFile &OS : OSs) - OS.keep(); } else { if (Parallelism != 1) error("-j must be specified together with -o");
diff --git a/src/llvm-project/llvm/tools/llvm-lto2/llvm-lto2.cpp b/src/llvm-project/llvm/tools/llvm-lto2/llvm-lto2.cpp index ca4278f..c0bff1e 100644 --- a/src/llvm-project/llvm/tools/llvm-lto2/llvm-lto2.cpp +++ b/src/llvm-project/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -325,12 +325,12 @@ std::vector<SymbolResolution> Res; for (const InputFile::Symbol &Sym : Input->symbols()) { auto I = CommandLineResolutions.find({F, std::string(Sym.getName())}); - // If it isn't found, look for "$", which would have been added + // If it isn't found, look for ".", which would have been added // (followed by a hash) when the symbol was promoted during module // splitting if it was defined in one part and used in the other. - // Try looking up the symbol name before the "$". + // Try looking up the symbol name before the suffix. if (I == CommandLineResolutions.end()) { - auto SplitName = Sym.getName().rsplit("$"); + auto SplitName = Sym.getName().rsplit("."); I = CommandLineResolutions.find({F, std::string(SplitName.first)}); } if (I == CommandLineResolutions.end()) { @@ -418,7 +418,8 @@ outs() << '\n'; } - std::vector<StringRef> ComdatTable = Input->getComdatTable(); + ArrayRef<std::pair<StringRef, Comdat::SelectionKind>> ComdatTable = + Input->getComdatTable(); for (const InputFile::Symbol &Sym : Input->symbols()) { switch (Sym.getVisibility()) { case GlobalValue::HiddenVisibility: @@ -447,8 +448,27 @@ << Sym.getCommonAlignment() << '\n'; int Comdat = Sym.getComdatIndex(); - if (Comdat != -1) - outs() << " comdat " << ComdatTable[Comdat] << '\n'; + if (Comdat != -1) { + outs() << " comdat "; + switch (ComdatTable[Comdat].second) { + case Comdat::Any: + outs() << "any"; + break; + case Comdat::ExactMatch: + outs() << "exactmatch"; + break; + case Comdat::Largest: + outs() << "largest"; + break; + case Comdat::NoDeduplicate: + outs() << "nodeduplicate"; + break; + case Comdat::SameSize: + outs() << "samesize"; + break; + } + outs() << ' ' << ComdatTable[Comdat].first << '\n'; + } if (TT.isOSBinFormatCOFF() && Sym.isWeak() && Sym.isIndirect()) outs() << " fallback " << Sym.getCOFFWeakExternalFallback() << '\n';
diff --git a/src/llvm-project/llvm/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp b/src/llvm-project/llvm/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp index 513ddc3..e32eb11 100644 --- a/src/llvm-project/llvm/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp +++ b/src/llvm-project/llvm/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp
@@ -37,6 +37,8 @@ using namespace llvm; +static mc::RegisterMCTargetOptionsFlags MOF; + static cl::opt<std::string> TripleName("triple", cl::desc("Target triple to assemble for, " "see -version for available targets")); @@ -170,12 +172,13 @@ abort(); } + std::unique_ptr<MCSubtargetInfo> STI( + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); - MCObjectFileInfo MOFI; - MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); - - static const bool UsePIC = false; - MOFI.InitMCObjectFileInfo(TheTriple, UsePIC, Ctx); + MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); + Ctx.setObjectFileInfo(MOFI.get()); const unsigned OutputAsmVariant = 0; std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); @@ -191,8 +194,6 @@ } const char *ProgName = "llvm-mc-fuzzer"; - std::unique_ptr<MCSubtargetInfo> STI( - TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); std::unique_ptr<MCCodeEmitter> CE = nullptr; std::unique_ptr<MCAsmBackend> MAB = nullptr;
diff --git a/src/llvm-project/llvm/tools/llvm-mc/llvm-mc.cpp b/src/llvm-project/llvm/tools/llvm-mc/llvm-mc.cpp index f3a6618..24c601b 100644 --- a/src/llvm-project/llvm/tools/llvm-mc/llvm-mc.cpp +++ b/src/llvm-project/llvm/tools/llvm-mc/llvm-mc.cpp
@@ -43,23 +43,33 @@ static mc::RegisterMCTargetOptionsFlags MOF; -static cl::opt<std::string> -InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); +static cl::OptionCategory MCCategory("MC Options"); + +static cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input file>"), + cl::init("-"), cl::cat(MCCategory)); + +static cl::list<std::string> + DisassemblerOptions("M", cl::desc("Disassembler options"), + cl::cat(MCCategory)); static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), - cl::init("-")); + cl::init("-"), cl::cat(MCCategory)); static cl::opt<std::string> SplitDwarfFile("split-dwarf-file", cl::desc("DWO output filename"), - cl::value_desc("filename")); + cl::value_desc("filename"), + cl::cat(MCCategory)); -static cl::opt<bool> -ShowEncoding("show-encoding", cl::desc("Show instruction encodings")); +static cl::opt<bool> ShowEncoding("show-encoding", + cl::desc("Show instruction encodings"), + cl::cat(MCCategory)); static cl::opt<bool> RelaxELFRel( "relax-relocations", cl::init(true), - cl::desc("Emit R_X86_64_GOTPCRELX instead of R_X86_64_GOTPCREL")); + cl::desc("Emit R_X86_64_GOTPCRELX instead of R_X86_64_GOTPCREL"), + cl::cat(MCCategory)); static cl::opt<DebugCompressionType> CompressDebugSections( "compress-debug-sections", cl::ValueOptional, @@ -69,29 +79,37 @@ clEnumValN(DebugCompressionType::Z, "zlib", "Use zlib compression"), clEnumValN(DebugCompressionType::GNU, "zlib-gnu", - "Use zlib-gnu compression (deprecated)"))); + "Use zlib-gnu compression (deprecated)")), + cl::cat(MCCategory)); static cl::opt<bool> -ShowInst("show-inst", cl::desc("Show internal instruction representation")); + ShowInst("show-inst", cl::desc("Show internal instruction representation"), + cl::cat(MCCategory)); static cl::opt<bool> -ShowInstOperands("show-inst-operands", - cl::desc("Show instructions operands as parsed")); + ShowInstOperands("show-inst-operands", + cl::desc("Show instructions operands as parsed"), + cl::cat(MCCategory)); static cl::opt<unsigned> -OutputAsmVariant("output-asm-variant", - cl::desc("Syntax variant to use for output printing")); + OutputAsmVariant("output-asm-variant", + cl::desc("Syntax variant to use for output printing"), + cl::cat(MCCategory)); static cl::opt<bool> -PrintImmHex("print-imm-hex", cl::init(false), - cl::desc("Prefer hex format for immediate values")); + PrintImmHex("print-imm-hex", cl::init(false), + cl::desc("Prefer hex format for immediate values"), + cl::cat(MCCategory)); static cl::list<std::string> -DefineSymbol("defsym", cl::desc("Defines a symbol to be an integer constant")); + DefineSymbol("defsym", + cl::desc("Defines a symbol to be an integer constant"), + cl::cat(MCCategory)); static cl::opt<bool> PreserveComments("preserve-comments", - cl::desc("Preserve Comments in outputted assembly")); + cl::desc("Preserve Comments in outputted assembly"), + cl::cat(MCCategory)); enum OutputFileType { OFT_Null, @@ -99,82 +117,101 @@ OFT_ObjectFile }; static cl::opt<OutputFileType> -FileType("filetype", cl::init(OFT_AssemblyFile), - cl::desc("Choose an output file type:"), - cl::values( - clEnumValN(OFT_AssemblyFile, "asm", - "Emit an assembly ('.s') file"), - clEnumValN(OFT_Null, "null", - "Don't emit anything (for timing purposes)"), - clEnumValN(OFT_ObjectFile, "obj", - "Emit a native object ('.o') file"))); + FileType("filetype", cl::init(OFT_AssemblyFile), + cl::desc("Choose an output file type:"), + cl::values(clEnumValN(OFT_AssemblyFile, "asm", + "Emit an assembly ('.s') file"), + clEnumValN(OFT_Null, "null", + "Don't emit anything (for timing purposes)"), + clEnumValN(OFT_ObjectFile, "obj", + "Emit a native object ('.o') file")), + cl::cat(MCCategory)); + +static cl::list<std::string> IncludeDirs("I", + cl::desc("Directory of include files"), + cl::value_desc("directory"), + cl::Prefix, cl::cat(MCCategory)); + +static cl::opt<std::string> + ArchName("arch", + cl::desc("Target arch to assemble for, " + "see -version for available targets"), + cl::cat(MCCategory)); + +static cl::opt<std::string> + TripleName("triple", + cl::desc("Target triple to assemble for, " + "see -version for available targets"), + cl::cat(MCCategory)); + +static cl::opt<std::string> + MCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), cl::init(""), cl::cat(MCCategory)); static cl::list<std::string> -IncludeDirs("I", cl::desc("Directory of include files"), - cl::value_desc("directory"), cl::Prefix); - -static cl::opt<std::string> -ArchName("arch", cl::desc("Target arch to assemble for, " - "see -version for available targets")); - -static cl::opt<std::string> -TripleName("triple", cl::desc("Target triple to assemble for, " - "see -version for available targets")); - -static cl::opt<std::string> -MCPU("mcpu", - cl::desc("Target a specific cpu type (-mcpu=help for details)"), - cl::value_desc("cpu-name"), - cl::init("")); - -static cl::list<std::string> -MAttrs("mattr", - cl::CommaSeparated, - cl::desc("Target specific attributes (-mattr=help for details)"), - cl::value_desc("a1,+a2,-a3,...")); + MAttrs("mattr", cl::CommaSeparated, + cl::desc("Target specific attributes (-mattr=help for details)"), + cl::value_desc("a1,+a2,-a3,..."), cl::cat(MCCategory)); static cl::opt<bool> PIC("position-independent", - cl::desc("Position independent"), cl::init(false)); + cl::desc("Position independent"), cl::init(false), + cl::cat(MCCategory)); static cl::opt<bool> LargeCodeModel("large-code-model", cl::desc("Create cfi directives that assume the code might " - "be more than 2gb away")); + "be more than 2gb away"), + cl::cat(MCCategory)); static cl::opt<bool> -NoInitialTextSection("n", cl::desc("Don't assume assembly file starts " - "in the text section")); + NoInitialTextSection("n", + cl::desc("Don't assume assembly file starts " + "in the text section"), + cl::cat(MCCategory)); static cl::opt<bool> -GenDwarfForAssembly("g", cl::desc("Generate dwarf debugging info for assembly " - "source files")); + GenDwarfForAssembly("g", + cl::desc("Generate dwarf debugging info for assembly " + "source files"), + cl::cat(MCCategory)); static cl::opt<std::string> -DebugCompilationDir("fdebug-compilation-dir", - cl::desc("Specifies the debug info's compilation dir")); + DebugCompilationDir("fdebug-compilation-dir", + cl::desc("Specifies the debug info's compilation dir"), + cl::cat(MCCategory)); -static cl::list<std::string> -DebugPrefixMap("fdebug-prefix-map", - cl::desc("Map file source paths in debug info"), - cl::value_desc("= separated key-value pairs")); +static cl::list<std::string> DebugPrefixMap( + "fdebug-prefix-map", cl::desc("Map file source paths in debug info"), + cl::value_desc("= separated key-value pairs"), cl::cat(MCCategory)); -static cl::opt<std::string> -MainFileName("main-file-name", - cl::desc("Specifies the name we should consider the input file")); +static cl::opt<std::string> MainFileName( + "main-file-name", + cl::desc("Specifies the name we should consider the input file"), + cl::cat(MCCategory)); static cl::opt<bool> SaveTempLabels("save-temp-labels", - cl::desc("Don't discard temporary labels")); + cl::desc("Don't discard temporary labels"), + cl::cat(MCCategory)); static cl::opt<bool> LexMasmIntegers( "masm-integers", - cl::desc("Enable binary and hex masm integers (0b110 and 0ABCh)")); + cl::desc("Enable binary and hex masm integers (0b110 and 0ABCh)"), + cl::cat(MCCategory)); static cl::opt<bool> LexMasmHexFloats( "masm-hexfloats", - cl::desc("Enable MASM-style hex float initializers (3F800000r)")); + cl::desc("Enable MASM-style hex float initializers (3F800000r)"), + cl::cat(MCCategory)); + +static cl::opt<bool> LexMotorolaIntegers( + "motorola-integers", + cl::desc("Enable binary and hex Motorola integers (%110 and $ABC)"), + cl::cat(MCCategory)); static cl::opt<bool> NoExecStack("no-exec-stack", - cl::desc("File doesn't need an exec stack")); + cl::desc("File doesn't need an exec stack"), + cl::cat(MCCategory)); enum ActionType { AC_AsLex, @@ -183,17 +220,16 @@ AC_MDisassemble, }; -static cl::opt<ActionType> -Action(cl::desc("Action to perform:"), - cl::init(AC_Assemble), - cl::values(clEnumValN(AC_AsLex, "as-lex", - "Lex tokens from a .s file"), - clEnumValN(AC_Assemble, "assemble", - "Assemble a .s file (default)"), - clEnumValN(AC_Disassemble, "disassemble", - "Disassemble strings of hex bytes"), - clEnumValN(AC_MDisassemble, "mdis", - "Marked up disassembly of strings of hex bytes"))); +static cl::opt<ActionType> Action( + cl::desc("Action to perform:"), cl::init(AC_Assemble), + cl::values(clEnumValN(AC_AsLex, "as-lex", "Lex tokens from a .s file"), + clEnumValN(AC_Assemble, "assemble", + "Assemble a .s file (default)"), + clEnumValN(AC_Disassemble, "disassemble", + "Disassemble strings of hex bytes"), + clEnumValN(AC_MDisassemble, "mdis", + "Marked up disassembly of strings of hex bytes")), + cl::cat(MCCategory)); static const Target *GetTarget(const char *ProgName) { // Figure out the target triple. @@ -305,6 +341,7 @@ Parser->setTargetParser(*TAP); Parser->getLexer().setLexMasmIntegers(LexMasmIntegers); Parser->getLexer().setLexMasmHexFloats(LexMasmHexFloats); + Parser->getLexer().setLexMotorolaIntegers(LexMotorolaIntegers); int Res = Parser->Run(NoInitialTextSection); @@ -323,6 +360,7 @@ // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + cl::HideUnrelatedOptions({&MCCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n"); const MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); setDwarfDebugFlags(argc, argv); @@ -338,7 +376,7 @@ Triple TheTriple(TripleName); ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = - MemoryBuffer::getFileOrSTDIN(InputFilename); + MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true); if (std::error_code EC = BufferPtr.getError()) { WithColor::error(errs(), ProgName) << InputFilename << ": " << EC.message() << '\n'; @@ -374,11 +412,26 @@ } MAI->setPreserveAsmComments(PreserveComments); + // Package up features to be passed to target/subtarget + std::string FeaturesStr; + if (MAttrs.size()) { + SubtargetFeatures Features; + for (unsigned i = 0; i != MAttrs.size(); ++i) + Features.AddFeature(MAttrs[i]); + FeaturesStr = Features.getString(); + } + + std::unique_ptr<MCSubtargetInfo> STI( + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); + assert(STI && "Unable to create subtarget info!"); + // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and // MCObjectFileInfo needs a MCContext reference in order to initialize itself. - MCObjectFileInfo MOFI; - MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr, &MCOptions); - MOFI.InitMCObjectFileInfo(TheTriple, PIC, Ctx, LargeCodeModel); + MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr, + &MCOptions); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(Ctx, PIC, LargeCodeModel)); + Ctx.setObjectFileInfo(MOFI.get()); if (SaveTempLabels) Ctx.setAllowTemporaryLabels(false); @@ -438,17 +491,9 @@ if (GenDwarfForAssembly) Ctx.setGenDwarfRootFile(InputFilename, Buffer->getBuffer()); - // Package up features to be passed to target/subtarget - std::string FeaturesStr; - if (MAttrs.size()) { - SubtargetFeatures Features; - for (unsigned i = 0; i != MAttrs.size(); ++i) - Features.AddFeature(MAttrs[i]); - FeaturesStr = Features.getString(); - } - - sys::fs::OpenFlags Flags = (FileType == OFT_AssemblyFile) ? sys::fs::OF_Text - : sys::fs::OF_None; + sys::fs::OpenFlags Flags = (FileType == OFT_AssemblyFile) + ? sys::fs::OF_TextWithCRLF + : sys::fs::OF_None; std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename, Flags); if (!Out) return 1; @@ -471,10 +516,6 @@ std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); assert(MCII && "Unable to create instruction info!"); - std::unique_ptr<MCSubtargetInfo> STI( - TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); - assert(STI && "Unable to create subtarget info!"); - MCInstPrinter *IP = nullptr; if (FileType == OFT_AssemblyFile) { IP = TheTarget->createMCInstPrinter(Triple(TripleName), OutputAsmVariant, @@ -488,6 +529,12 @@ return 1; } + for (StringRef Opt : DisassemblerOptions) + if (!IP->applyTargetSpecificCLOption(Opt)) { + WithColor::error() << "invalid disassembler option '" << Opt << "'\n"; + return 1; + } + // Set the display preference for hex vs. decimal immediates. IP->setPrintImmHex(PrintImmHex);
diff --git a/src/llvm-project/llvm/tools/llvm-mca/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-mca/CMakeLists.txt index 9df1923..7cfabe7 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-mca/CMakeLists.txt
@@ -1,5 +1,7 @@ include_directories(include) +add_subdirectory(lib) + set(LLVM_LINK_COMPONENTS AllTargetsAsmParsers AllTargetsDescs @@ -30,3 +32,9 @@ ) set(LLVM_MCA_SOURCE_DIR ${CURRENT_SOURCE_DIR}) + +target_link_libraries(llvm-mca PRIVATE + ${LLVM_MCA_CUSTOMBEHAVIOUR_TARGETS} + ) + +add_definitions(${LLVM_MCA_MACROS_TO_DEFINE})
diff --git a/src/llvm-project/llvm/tools/llvm-mca/CodeRegion.h b/src/llvm-project/llvm/tools/llvm-mca/CodeRegion.h index d2b05fa..0b25907 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/CodeRegion.h +++ b/src/llvm-project/llvm/tools/llvm-mca/CodeRegion.h
@@ -53,7 +53,7 @@ // An optional descriptor for this region. llvm::StringRef Description; // Instructions that form this region. - llvm::SmallVector<llvm::MCInst, 8> Instructions; + llvm::SmallVector<llvm::MCInst, 16> Instructions; // Source location range. llvm::SMLoc RangeStart; llvm::SMLoc RangeEnd;
diff --git a/src/llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.cpp b/src/llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.cpp index 831b76a..6ad2a65 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.cpp
@@ -106,11 +106,21 @@ Regions.beginRegion(Comment, Loc); } -Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions() { +Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions( + const std::unique_ptr<MCInstPrinter> &IP) { MCTargetOptions Opts; Opts.PreserveAsmComments = false; MCStreamerWrapper Str(Ctx, Regions); + // Need to initialize an MCTargetStreamer otherwise + // certain asm directives will cause a segfault. + // Using nulls() so that anything emitted by the MCTagetStreamer + // doesn't show up in the llvm-mca output. + raw_ostream &OSRef = nulls(); + formatted_raw_ostream FOSRef(OSRef); + TheTarget.createAsmTargetStreamer(Str, FOSRef, IP.get(), + /*IsVerboseAsm=*/true); + // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM // comments. std::unique_ptr<MCAsmParser> Parser(
diff --git a/src/llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.h b/src/llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.h index 9a10aa2..1c11784 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.h +++ b/src/llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.h
@@ -39,7 +39,8 @@ public: CodeRegionGenerator(SourceMgr &SM) : Regions(SM) {} virtual ~CodeRegionGenerator(); - virtual Expected<const CodeRegions &> parseCodeRegions() = 0; + virtual Expected<const CodeRegions &> + parseCodeRegions(const std::unique_ptr<MCInstPrinter> &IP) = 0; }; /// This class is responsible for parsing input ASM and generating @@ -60,7 +61,8 @@ AssemblerDialect(0) {} unsigned getAssemblerDialect() const { return AssemblerDialect; } - Expected<const CodeRegions &> parseCodeRegions() override; + Expected<const CodeRegions &> + parseCodeRegions(const std::unique_ptr<MCInstPrinter> &IP) override; }; } // namespace mca
diff --git a/src/llvm-project/llvm/tools/llvm-mca/PipelinePrinter.cpp b/src/llvm-project/llvm/tools/llvm-mca/PipelinePrinter.cpp index e7dfbfd..955b825 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/PipelinePrinter.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/PipelinePrinter.cpp
@@ -12,14 +12,119 @@ //===----------------------------------------------------------------------===// #include "PipelinePrinter.h" +#include "CodeRegion.h" +#include "Views/InstructionView.h" #include "Views/View.h" namespace llvm { namespace mca { -void PipelinePrinter::printReport(llvm::raw_ostream &OS) const { - for (const auto &V : Views) - V->printView(OutputKind, OS); +void PipelinePrinter::printRegionHeader(llvm::raw_ostream &OS) const { + StringRef RegionName; + if (!Region.getDescription().empty()) + RegionName = Region.getDescription(); + + OS << "\n[" << RegionIdx << "] Code Region"; + if (!RegionName.empty()) + OS << " - " << RegionName; + OS << "\n\n"; } -} // namespace mca. + +json::Object PipelinePrinter::getJSONReportRegion() const { + json::Object JO; + + StringRef RegionName = ""; + if (!Region.getDescription().empty()) + RegionName = Region.getDescription(); + + JO.try_emplace("Name", RegionName); + for (const auto &V : Views) + if (V->isSerializable()) + JO.try_emplace(V->getNameAsString().str(), V->toJSON()); + + return JO; +} + +json::Object PipelinePrinter::getJSONSimulationParameters() const { + json::Object SimParameters({{"-mcpu", STI.getCPU()}, + {"-mtriple", STI.getTargetTriple().getTriple()}, + {"-march", STI.getTargetTriple().getArchName()}}); + + const MCSchedModel &SM = STI.getSchedModel(); + if (!SM.isOutOfOrder()) + return SimParameters; + + if (PO.RegisterFileSize) + SimParameters.try_emplace("-register-file-size", PO.RegisterFileSize); + + if (!PO.AssumeNoAlias) + SimParameters.try_emplace("-noalias", PO.AssumeNoAlias); + + if (PO.DecodersThroughput) + SimParameters.try_emplace("-decoder-throughput", PO.DecodersThroughput); + + if (PO.MicroOpQueueSize) + SimParameters.try_emplace("-micro-op-queue-size", PO.MicroOpQueueSize); + + if (PO.DispatchWidth) + SimParameters.try_emplace("-dispatch", PO.DispatchWidth); + + if (PO.LoadQueueSize) + SimParameters.try_emplace("-lqueue", PO.LoadQueueSize); + + if (PO.StoreQueueSize) + SimParameters.try_emplace("-squeue", PO.StoreQueueSize); + + return SimParameters; +} + +json::Object PipelinePrinter::getJSONTargetInfo() const { + json::Array Resources; + const MCSchedModel &SM = STI.getSchedModel(); + StringRef MCPU = STI.getCPU(); + + for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) { + const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); + unsigned NumUnits = ProcResource.NumUnits; + if (ProcResource.SubUnitsIdxBegin || !NumUnits) + continue; + + for (unsigned J = 0; J < NumUnits; ++J) { + std::string ResourceName = ProcResource.Name; + if (NumUnits > 1) { + ResourceName += "."; + ResourceName += J; + } + + Resources.push_back(ResourceName); + } + } + + return json::Object({{"CPUName", MCPU}, {"Resources", std::move(Resources)}}); +} + +void PipelinePrinter::printReport(json::Object &JO) const { + if (!RegionIdx) { + JO.try_emplace("TargetInfo", getJSONTargetInfo()); + JO.try_emplace("SimulationParameters", getJSONSimulationParameters()); + // Construct an array of regions. + JO.try_emplace("CodeRegions", json::Array()); + } + + json::Array *Regions = JO.getArray("CodeRegions"); + assert(Regions && "This array must exist!"); + Regions->push_back(getJSONReportRegion()); +} + +void PipelinePrinter::printReport(llvm::raw_ostream &OS) const { + // Don't print the header of this region if it is the default region, and if + // it doesn't have an end location. + if (Region.startLoc().isValid() || Region.endLoc().isValid()) + printRegionHeader(OS); + + for (const auto &V : Views) + V->printView(OS); +} + +} // namespace mca } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/PipelinePrinter.h b/src/llvm-project/llvm/tools/llvm-mca/PipelinePrinter.h index ae18140..1365f75 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/PipelinePrinter.h +++ b/src/llvm-project/llvm/tools/llvm-mca/PipelinePrinter.h
@@ -18,6 +18,8 @@ #include "Views/View.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MCA/Context.h" #include "llvm/MCA/Pipeline.h" #include "llvm/Support/raw_ostream.h" @@ -26,6 +28,8 @@ namespace llvm { namespace mca { +class CodeRegion; + /// A printer class that knows how to collects statistics on the /// code analyzed by the llvm-mca tool. /// @@ -35,12 +39,21 @@ /// resource pressure. class PipelinePrinter { Pipeline &P; + const CodeRegion &Region; + unsigned RegionIdx; + const MCSubtargetInfo &STI; + const PipelineOptions &PO; llvm::SmallVector<std::unique_ptr<View>, 8> Views; - View::OutputKind OutputKind; + + void printRegionHeader(llvm::raw_ostream &OS) const; + json::Object getJSONReportRegion() const; + json::Object getJSONTargetInfo() const; + json::Object getJSONSimulationParameters() const; public: - PipelinePrinter(Pipeline &pipeline, View::OutputKind OutputKind) - : P(pipeline), OutputKind(OutputKind) {} + PipelinePrinter(Pipeline &Pipe, const CodeRegion &R, unsigned Idx, + const MCSubtargetInfo &STI, const PipelineOptions &PO) + : P(Pipe), Region(R), RegionIdx(Idx), STI(STI), PO(PO), Views() {} void addView(std::unique_ptr<View> V) { P.addEventListener(V.get()); @@ -48,6 +61,7 @@ } void printReport(llvm::raw_ostream &OS) const; + void printReport(json::Object &JO) const; }; } // namespace mca } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp b/src/llvm-project/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp index 38a8e2e..5b110d6 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp
@@ -66,8 +66,6 @@ void PressureTracker::handleInstructionIssuedEvent( const HWInstructionIssuedEvent &Event) { unsigned IID = Event.IR.getSourceIndex(); - using ResourceRef = HWInstructionIssuedEvent::ResourceRef; - using ResourceUse = std::pair<ResourceRef, ResourceCycles>; for (const ResourceUse &Use : Event.UsedResources) { const ResourceRef &RR = Use.first; unsigned Index = ProcResID2ResourceUsersIndex[RR.first]; @@ -200,8 +198,8 @@ } } -void DependencyGraph::propagateThroughEdges( - SmallVectorImpl<unsigned> &RootSet, unsigned Iterations) { +void DependencyGraph::propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet, + unsigned Iterations) { SmallVector<unsigned, 8> ToVisit; // A critical sequence is computed as the longest path from a node of the @@ -223,14 +221,14 @@ // The `unvisited nodes` set initially contains all the nodes from the // RootSet. A node N is added to the `unvisited nodes` if all its // predecessors have been visited already. - // + // // For simplicity, every node tracks the number of unvisited incoming edges in // field `NumVisitedPredecessors`. When the value of that field drops to // zero, then the corresponding node is added to a `ToVisit` set. // // At the end of every iteration of the outer loop, set `ToVisit` becomes our // new `unvisited nodes` set. - // + // // The algorithm terminates when the set of unvisited nodes (i.e. our RootSet) // is empty. This algorithm works under the assumption that the graph is // acyclic. @@ -269,8 +267,9 @@ // that node is the last instruction of our critical sequence. // Field N.Depth would tell us the total length of the sequence. // - // To obtain the sequence of critical edges, we simply follow the chain of critical - // predecessors starting from node N (field DGNode::CriticalPredecessor). + // To obtain the sequence of critical edges, we simply follow the chain of + // critical predecessors starting from node N (field + // DGNode::CriticalPredecessor). const auto It = std::max_element( Nodes.begin(), Nodes.end(), [](const DGNode &Lhs, const DGNode &Rhs) { return Lhs.Cost < Rhs.Cost; });
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h b/src/llvm-project/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h index 427937d..cd5af0a 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h
@@ -33,9 +33,9 @@ /// In particular, this occurs when there is a delta between the number of uOps /// dispatched and the number of uOps issued to the underlying pipelines. /// -/// The bottleneck analysis view is also responsible for identifying and printing -/// the most "critical" sequence of dependent instructions according to the -/// simulated run. +/// The bottleneck analysis view is also responsible for identifying and +/// printing the most "critical" sequence of dependent instructions according to +/// the simulated run. /// /// Below is the critical sequence computed for the dot-product example on /// btver2: @@ -62,13 +62,14 @@ /// and edges of the graph represent data dependencies or processor resource /// interferences. /// -/// Edges are dynamically 'discovered' by observing instruction state transitions -/// and backend pressure increase events. Edges are internally ranked based on -/// their "criticality". A dependency is considered to be critical if it takes a -/// long time to execute, and if it contributes to backend pressure increases. -/// Criticality is internally measured in terms of cycles; it is computed for -/// every edge in the graph as a function of the edge latency and the number of -/// backend pressure increase cycles contributed by that edge. +/// Edges are dynamically 'discovered' by observing instruction state +/// transitions and backend pressure increase events. Edges are internally +/// ranked based on their "criticality". A dependency is considered to be +/// critical if it takes a long time to execute, and if it contributes to +/// backend pressure increases. Criticality is internally measured in terms of +/// cycles; it is computed for every edge in the graph as a function of the edge +/// latency and the number of backend pressure increase cycles contributed by +/// that edge. /// /// At the end of simulation, costs are propagated to nodes through the edges of /// the graph, and the most expensive path connecting the root-set (a @@ -217,8 +218,8 @@ // Loop carried dependencies are carefully expanded by the bottleneck analysis // to guarantee that the graph stays acyclic. To this end, extra nodes are // pre-allocated at construction time to describe instructions from "past and -// future" iterations. The graph is kept acyclic mainly because it simplifies the -// complexity of the algorithm that computes the critical sequence. +// future" iterations. The graph is kept acyclic mainly because it simplifies +// the complexity of the algorithm that computes the critical sequence. class DependencyGraph { struct DGNode { unsigned NumPredecessors; @@ -239,7 +240,8 @@ void pruneEdges(unsigned Iterations); void initializeRootSet(SmallVectorImpl<unsigned> &RootSet) const; - void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet, unsigned Iterations); + void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet, + unsigned Iterations); #ifndef NDEBUG void dumpDependencyEdge(raw_ostream &OS, const DependencyEdge &DE, @@ -333,7 +335,7 @@ void printView(raw_ostream &OS) const override; StringRef getNameAsString() const override { return "BottleneckAnalysis"; } - json::Value toJSON() const override { return "not implemented"; } + bool isSerializable() const override { return false; } #ifndef NDEBUG void dump(raw_ostream &OS, MCInstPrinter &MCIP) const { DG.dump(OS, MCIP); }
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp b/src/llvm-project/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp index a1c0cf2..3dc17c8 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp
@@ -1,5 +1,4 @@ -//===--------------------- DispatchStatistics.cpp ---------------------*- C++ -//-*-===// +//===--------------------- DispatchStatistics.cpp ---------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -77,10 +76,23 @@ printStalls(SS, HWStalls[HWStallEvent::StoreQueueFull], NumCycles); SS << "\nGROUP - Static restrictions on the dispatch group: "; printStalls(SS, HWStalls[HWStallEvent::DispatchGroupStall], NumCycles); + SS << "\nUSH - Uncategorised Structural Hazard: "; + printStalls(SS, HWStalls[HWStallEvent::CustomBehaviourStall], NumCycles); SS << '\n'; SS.flush(); OS << Buffer; } +json::Value DispatchStatistics::toJSON() const { + json::Object JO({{"RAT", HWStalls[HWStallEvent::RegisterFileStall]}, + {"RCU", HWStalls[HWStallEvent::RetireControlUnitStall]}, + {"SCHEDQ", HWStalls[HWStallEvent::SchedulerQueueFull]}, + {"LQ", HWStalls[HWStallEvent::LoadQueueFull]}, + {"SQ", HWStalls[HWStallEvent::StoreQueueFull]}, + {"GROUP", HWStalls[HWStallEvent::DispatchGroupStall]}, + {"USH", HWStalls[HWStallEvent::CustomBehaviourStall]}}); + return JO; +} + } // namespace mca } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/DispatchStatistics.h b/src/llvm-project/llvm/tools/llvm-mca/Views/DispatchStatistics.h index 8d999fb..81b582f 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/DispatchStatistics.h +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/DispatchStatistics.h
@@ -79,6 +79,7 @@ printDispatchHistogram(OS); } StringRef getNameAsString() const override { return "DispatchStatistics"; } + json::Value toJSON() const override; }; } // namespace mca } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp b/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp index 2248f63..3f6abf4 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp
@@ -93,7 +93,7 @@ MutableArrayRef<InstructionInfoViewData> IIVD) const { const llvm::MCSubtargetInfo &STI = getSubTargetInfo(); const MCSchedModel &SM = STI.getSchedModel(); - for (const auto &I : zip(getSource(), IIVD)) { + for (const auto I : zip(getSource(), IIVD)) { const MCInst &Inst = std::get<0>(I); InstructionInfoViewData &IIVDEntry = std::get<1>(I); const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode()); @@ -147,7 +147,7 @@ JO.try_emplace("Instruction", (unsigned)I.index()); InstInfo.push_back(std::move(JO)); } - return json::Value(std::move(InstInfo)); + return json::Object({{"InstructionList", json::Value(std::move(InstInfo))}}); } } // namespace mca. } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionView.cpp b/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionView.cpp index 7f7a5b7..3b174a0 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionView.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionView.cpp
@@ -1,4 +1,4 @@ -//===----------------------- View.cpp ---------------------------*- C++ -*-===// +//===----------------------- InstructionView.cpp ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,15 +11,18 @@ /// //===----------------------------------------------------------------------===// -#include <sstream> #include "Views/InstructionView.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCSubtargetInfo.h" namespace llvm { namespace mca { -StringRef InstructionView::printInstructionString(const llvm::MCInst &MCI) const { +InstructionView::~InstructionView() = default; + +StringRef +InstructionView::printInstructionString(const llvm::MCInst &MCI) const { InstructionString = ""; MCIP.printInst(&MCI, 0, "", STI, InstrStream); InstrStream.flush(); @@ -28,33 +31,13 @@ } json::Value InstructionView::toJSON() const { - json::Object JO; json::Array SourceInfo; for (const auto &MCI : getSource()) { StringRef Instruction = printInstructionString(MCI); SourceInfo.push_back(Instruction.str()); } - JO.try_emplace("Instructions", std::move(SourceInfo)); - - json::Array Resources; - const MCSchedModel &SM = STI.getSchedModel(); - for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) { - const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); - unsigned NumUnits = ProcResource.NumUnits; - // Skip groups and invalid resources with zero units. - if (ProcResource.SubUnitsIdxBegin || !NumUnits) - continue; - for (unsigned J = 0; J < NumUnits; ++J) { - std::stringstream ResNameStream; - ResNameStream << ProcResource.Name; - if (NumUnits > 1) - ResNameStream << "." << J; - Resources.push_back(ResNameStream.str()); - } - } - JO.try_emplace("Resources", json::Object({{"CPUName", MCPU}, {"Resources", std::move(Resources)}})); - - return JO; + return SourceInfo; } + } // namespace mca } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionView.h b/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionView.h index 2a260b9..1843b05 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionView.h +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/InstructionView.h
@@ -1,4 +1,4 @@ -//===----------------------- InstrucionView.h -----------------------------*- C++ -*-===// +//===----------------------- InstructionView.h ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -16,9 +16,8 @@ #define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONVIEW_H #include "Views/View.h" -#include "llvm/MC/MCInstPrinter.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" namespace llvm { namespace mca { @@ -28,7 +27,6 @@ const llvm::MCSubtargetInfo &STI; llvm::MCInstPrinter &MCIP; llvm::ArrayRef<llvm::MCInst> Source; - StringRef MCPU; mutable std::string InstructionString; mutable raw_string_ostream InstrStream; @@ -36,17 +34,13 @@ public: void printView(llvm::raw_ostream &) const override {} InstructionView(const llvm::MCSubtargetInfo &STI, - llvm::MCInstPrinter &Printer, - llvm::ArrayRef<llvm::MCInst> S, - StringRef MCPU = StringRef()) - : STI(STI), MCIP(Printer), Source(S), MCPU(MCPU), - InstrStream(InstructionString) {} + llvm::MCInstPrinter &Printer, llvm::ArrayRef<llvm::MCInst> S) + : STI(STI), MCIP(Printer), Source(S), InstrStream(InstructionString) {} - virtual ~InstructionView() = default; + virtual ~InstructionView(); - StringRef getNameAsString() const override { - return "Instructions and CPU resources"; - } + StringRef getNameAsString() const override { return "Instructions"; } + // Return a reference to a string representing a given machine instruction. // The result should be used or copied before the next call to // printInstructionString() as it will overwrite the previous result. @@ -55,12 +49,10 @@ llvm::MCInstPrinter &getInstPrinter() const { return MCIP; } llvm::ArrayRef<llvm::MCInst> getSource() const { return Source; } + json::Value toJSON() const override; - virtual void printViewJSON(llvm::raw_ostream &OS) override { - json::Value JV = toJSON(); - OS << formatv("{0:2}", JV) << "\n"; - } }; + } // namespace mca } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp b/src/llvm-project/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp index 58736ee..4ef8053 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp
@@ -60,18 +60,21 @@ if (!Inst.isOptimizableMove()) return; - assert(Inst.getDefs().size() == 1 && "Expected a single definition!"); - assert(Inst.getUses().size() == 1 && "Expected a single register use!"); - const WriteState &WS = Inst.getDefs()[0]; - const ReadState &RS = Inst.getUses()[0]; + if (Inst.getDefs().size() != Inst.getUses().size()) + return; - MoveEliminationInfo &Info = - MoveElimInfo[Inst.getDefs()[0].getRegisterFileID()]; - Info.TotalMoveEliminationCandidates++; - if (WS.isEliminated()) - Info.CurrentMovesEliminated++; - if (WS.isWriteZero() && RS.isReadZero()) - Info.TotalMovesThatPropagateZero++; + for (size_t I = 0, E = Inst.getDefs().size(); I < E; ++I) { + const WriteState &WS = Inst.getDefs()[I]; + const ReadState &RS = Inst.getUses()[E - (I + 1)]; + + MoveEliminationInfo &Info = + MoveElimInfo[Inst.getDefs()[0].getRegisterFileID()]; + Info.TotalMoveEliminationCandidates++; + if (WS.isEliminated()) + Info.CurrentMovesEliminated++; + if (WS.isWriteZero() && RS.isReadZero()) + Info.TotalMovesThatPropagateZero++; + } } void RegisterFileStatistics::onEvent(const HWInstructionEvent &Event) {
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h b/src/llvm-project/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h index cf384db..ec5c5f4 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h
@@ -76,6 +76,7 @@ StringRef getNameAsString() const override { return "RegisterFileStatistics"; } + bool isSerializable() const override { return false; } }; } // namespace mca } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp b/src/llvm-project/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp index 61c115b..1c40428 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp
@@ -71,7 +71,8 @@ } unsigned AvgUsage = (double)SumOfUsedEntries / NumCycles; - double MaxUsagePercentage = ((double)MaxUsedEntries / TotalROBEntries) * 100.0; + double MaxUsagePercentage = + ((double)MaxUsedEntries / TotalROBEntries) * 100.0; double NormalizedMaxPercentage = floor((MaxUsagePercentage * 10) + 0.5) / 10; double AvgUsagePercentage = ((double)AvgUsage / TotalROBEntries) * 100.0; double NormalizedAvgPercentage = floor((AvgUsagePercentage * 10) + 0.5) / 10;
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h b/src/llvm-project/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h index 662a223..86b46e9 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h
@@ -55,6 +55,7 @@ StringRef getNameAsString() const override { return "RetireControlUnitStatistics"; } + bool isSerializable() const override { return false; } }; } // namespace mca
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/SchedulerStatistics.h b/src/llvm-project/llvm/tools/llvm-mca/Views/SchedulerStatistics.h index 734046c..66f4b00 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/SchedulerStatistics.h +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/SchedulerStatistics.h
@@ -89,6 +89,7 @@ void printView(llvm::raw_ostream &OS) const override; StringRef getNameAsString() const override { return "SchedulerStatistics"; } + bool isSerializable() const override { return false; } }; } // namespace mca } // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/SummaryView.cpp b/src/llvm-project/llvm/tools/llvm-mca/Views/SummaryView.cpp index c0fe3b5..bf258b4 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/SummaryView.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/SummaryView.cpp
@@ -1,4 +1,4 @@ -//===--------------------- SummaryView.cpp -------------------*- C++ -*-===// +//===--------------------- SummaryView.cpp ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -24,9 +24,8 @@ SummaryView::SummaryView(const MCSchedModel &Model, ArrayRef<MCInst> S, unsigned Width) - : SM(Model), Source(S), DispatchWidth(Width?Width: Model.IssueWidth), - LastInstructionIdx(0), - TotalCycles(0), NumMicroOps(0), + : SM(Model), Source(S), DispatchWidth(Width ? Width : Model.IssueWidth), + LastInstructionIdx(0), TotalCycles(0), NumMicroOps(0), ProcResourceUsage(Model.getNumProcResourceKinds(), 0), ProcResourceMasks(Model.getNumProcResourceKinds()), ResIdx2ProcResID(Model.getNumProcResourceKinds(), 0) {
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/SummaryView.h b/src/llvm-project/llvm/tools/llvm-mca/Views/SummaryView.h index 2622e86..e2c7cfd 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/SummaryView.h +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/SummaryView.h
@@ -1,4 +1,4 @@ -//===--------------------- SummaryView.h ---------------------*- C++ -*-===// +//===--------------------- SummaryView.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information.
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/TimelineView.cpp b/src/llvm-project/llvm/tools/llvm-mca/Views/TimelineView.cpp index c8b481b..4ecc301 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/TimelineView.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/TimelineView.cpp
@@ -21,8 +21,8 @@ llvm::ArrayRef<llvm::MCInst> S, unsigned Iterations, unsigned Cycles) : InstructionView(sti, Printer, S), CurrentCycle(0), - MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0), WaitTime(S.size()), - UsedBuffer(S.size()) { + MaxCycle(Cycles == 0 ? std::numeric_limits<unsigned>::max() : Cycles), + LastCycle(0), WaitTime(S.size()), UsedBuffer(S.size()) { unsigned NumInstructions = getSource().size(); assert(Iterations && "Invalid number of iterations specified!"); NumInstructions *= Iterations; @@ -77,8 +77,10 @@ "Instruction cannot be ready if it hasn't been dispatched yet!"); WTEntry.CyclesSpentInSQWhileReady += TVEntry.CycleIssued - TVEntry.CycleReady; - WTEntry.CyclesSpentAfterWBAndBeforeRetire += - (CurrentCycle - 1) - TVEntry.CycleExecuted; + if (CurrentCycle > TVEntry.CycleExecuted) { + WTEntry.CyclesSpentAfterWBAndBeforeRetire += + (CurrentCycle - 1) - TVEntry.CycleExecuted; + } break; } case HWInstructionEvent::Ready: @@ -143,10 +145,11 @@ double AverageTime1, AverageTime2, AverageTime3; AverageTime1 = - (double)Entry.CyclesSpentInSchedulerQueue / CumulativeExecutions; - AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / CumulativeExecutions; - AverageTime3 = - (double)Entry.CyclesSpentAfterWBAndBeforeRetire / CumulativeExecutions; + (double)(Entry.CyclesSpentInSchedulerQueue * 10) / CumulativeExecutions; + AverageTime2 = + (double)(Entry.CyclesSpentInSQWhileReady * 10) / CumulativeExecutions; + AverageTime3 = (double)(Entry.CyclesSpentAfterWBAndBeforeRetire * 10) / + CumulativeExecutions; OS << Executions; OS.PadToColumn(13); @@ -155,18 +158,18 @@ if (!PrintingTotals) tryChangeColor(OS, Entry.CyclesSpentInSchedulerQueue, CumulativeExecutions, BufferSize); - OS << format("%.1f", floor((AverageTime1 * 10) + 0.5) / 10); + OS << format("%.1f", floor(AverageTime1 + 0.5) / 10); OS.PadToColumn(20); if (!PrintingTotals) tryChangeColor(OS, Entry.CyclesSpentInSQWhileReady, CumulativeExecutions, BufferSize); - OS << format("%.1f", floor((AverageTime2 * 10) + 0.5) / 10); + OS << format("%.1f", floor(AverageTime2 + 0.5) / 10); OS.PadToColumn(27); if (!PrintingTotals) tryChangeColor(OS, Entry.CyclesSpentAfterWBAndBeforeRetire, CumulativeExecutions, getSubTargetInfo().getSchedModel().MicroOpBufferSize); - OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10); + OS << format("%.1f", floor(AverageTime3 + 0.5) / 10); if (OS.has_colors()) OS.resetColor(); @@ -243,7 +246,8 @@ for (unsigned I = Entry.CycleExecuted + 1, E = Entry.CycleRetired; I < E; ++I) OS << TimelineView::DisplayChar::RetireLag; - OS << TimelineView::DisplayChar::Retired; + if (Entry.CycleExecuted < Entry.CycleRetired) + OS << TimelineView::DisplayChar::Retired; // Skip other columns. for (unsigned I = Entry.CycleRetired + 1, E = LastCycle; I <= E; ++I) @@ -285,7 +289,14 @@ for (unsigned Iteration = 0; Iteration < Iterations; ++Iteration) { for (const MCInst &Inst : Source) { const TimelineViewEntry &Entry = Timeline[IID]; - if (Entry.CycleRetired == 0) + // When an instruction is retired after timeline-max-cycles, + // its CycleRetired is left at 0. However, it's possible for + // a 0 latency instruction to be retired during cycle 0 and we + // don't want to early exit in that case. The CycleExecuted + // attribute is set correctly whether or not it is greater + // than timeline-max-cycles so we can use that to ensure + // we don't early exit because of a 0 latency instruction. + if (Entry.CycleRetired == 0 && Entry.CycleExecuted != 0) return; unsigned SourceIndex = IID % Source.size();
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/TimelineView.h b/src/llvm-project/llvm/tools/llvm-mca/Views/TimelineView.h index 81f2b03..81be824 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/TimelineView.h +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/TimelineView.h
@@ -125,7 +125,7 @@ unsigned LastCycle; struct TimelineViewEntry { - int CycleDispatched; // A negative value is an "invalid cycle". + int CycleDispatched; // A negative value is an "invalid cycle". unsigned CycleReady; unsigned CycleIssued; unsigned CycleExecuted;
diff --git a/src/llvm-project/llvm/tools/llvm-mca/Views/View.h b/src/llvm-project/llvm/tools/llvm-mca/Views/View.h index 85464bf..c604733 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/Views/View.h +++ b/src/llvm-project/llvm/tools/llvm-mca/Views/View.h
@@ -17,32 +17,22 @@ #include "llvm/MC/MCInstPrinter.h" #include "llvm/MCA/HWEventListener.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" namespace llvm { namespace mca { class View : public HWEventListener { public: - enum OutputKind { OK_READABLE, OK_JSON }; - - void printView(OutputKind OutputKind, llvm::raw_ostream &OS) { - if (OutputKind == OK_JSON) - printViewJSON(OS); - else - printView(OS); - } + virtual ~View() = default; virtual void printView(llvm::raw_ostream &OS) const = 0; - virtual void printViewJSON(llvm::raw_ostream &OS) { - json::Object JO; - JO.try_emplace(getNameAsString().str(), toJSON()); - OS << formatv("{0:2}", json::Value(std::move(JO))) << "\n"; - } - virtual ~View() = default; virtual StringRef getNameAsString() const = 0; + virtual json::Value toJSON() const { return "not implemented"; } + virtual bool isSerializable() const { return true; } + void anchor() override; }; } // namespace mca
diff --git a/src/llvm-project/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp b/src/llvm-project/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp new file mode 100644 index 0000000..a655f3f --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp
@@ -0,0 +1,33 @@ +//===------------------ AMDGPUCustomBehaviour.cpp ---------------*-C++ -* -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file implements methods from the AMDGPUCustomBehaviour class. +/// +//===----------------------------------------------------------------------===// + +#include "AMDGPUCustomBehaviour.h" +#include "MCTargetDesc/AMDGPUMCTargetDesc.h" +#include "SIInstrInfo.h" +#include "llvm/Support/WithColor.h" + +namespace llvm { +namespace mca { + +AMDGPUCustomBehaviour::AMDGPUCustomBehaviour(const MCSubtargetInfo &STI, + const SourceMgr &SrcMgr, + const MCInstrInfo &MCII) + : CustomBehaviour(STI, SrcMgr, MCII) {} + +unsigned AMDGPUCustomBehaviour::checkCustomHazard(ArrayRef<InstRef> IssuedInst, + const InstRef &IR) { + return 0; +} + +} // namespace mca +} // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h b/src/llvm-project/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h new file mode 100644 index 0000000..0dd21c7 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h
@@ -0,0 +1,57 @@ +//===------------------- AMDGPUCustomBehaviour.h ----------------*-C++ -* -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines the AMDGPUCustomBehaviour class which inherits from +/// CustomBehaviour. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_MCA_LIB_AMDGPU_AMDGPUCUSTOMBEHAVIOUR_H +#define LLVM_TOOLS_LLVM_MCA_LIB_AMDGPU_AMDGPUCUSTOMBEHAVIOUR_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MCA/CustomBehaviour.h" +#include "llvm/Support/TargetParser.h" + +namespace llvm { +namespace mca { + +class AMDGPUInstrPostProcess : public InstrPostProcess { +public: + AMDGPUInstrPostProcess(const MCSubtargetInfo &STI, const MCInstrInfo &MCII) + : InstrPostProcess(STI, MCII) {} + + ~AMDGPUInstrPostProcess() {} + + void postProcessInstruction(std::unique_ptr<Instruction> &Inst, + const MCInst &MCI) override {} +}; + +class AMDGPUCustomBehaviour : public CustomBehaviour { +public: + AMDGPUCustomBehaviour(const MCSubtargetInfo &STI, const SourceMgr &SrcMgr, + const MCInstrInfo &MCII); + + ~AMDGPUCustomBehaviour() {} + + /// This method is used to determine if an instruction + /// should be allowed to be dispatched. The return value is + /// how many cycles until the instruction can be dispatched. + /// This method is called after MCA has already checked for + /// register and hardware dependencies so this method should only + /// implement custom behaviour and dependencies that are not picked up + /// by MCA naturally. + unsigned checkCustomHazard(ArrayRef<InstRef> IssuedInst, + const InstRef &IR) override; +}; + +} // namespace mca +} // namespace llvm + +#endif /* LLVM_TOOLS_LLVM_MCA_LIB_AMDGPU_AMDGPUCUSTOMBEHAVIOUR_H */
diff --git a/src/llvm-project/llvm/tools/llvm-mca/lib/AMDGPU/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-mca/lib/AMDGPU/CMakeLists.txt new file mode 100644 index 0000000..cd6e171 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-mca/lib/AMDGPU/CMakeLists.txt
@@ -0,0 +1,18 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/lib/Target/AMDGPU + ${LLVM_BINARY_DIR}/lib/Target/AMDGPU + ) + +set(LLVM_LINK_COMPONENTS + AMDGPU + Core + MCA + Support + ) + +add_llvm_library(LLVMMCACustomBehaviourAMDGPU + AMDGPUCustomBehaviour.cpp + + DEPENDS + AMDGPUCommonTableGen + )
diff --git a/src/llvm-project/llvm/tools/llvm-mca/lib/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-mca/lib/CMakeLists.txt new file mode 100644 index 0000000..57057dc --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-mca/lib/CMakeLists.txt
@@ -0,0 +1,11 @@ +set(TARGETS_TO_APPEND "") +set(MACROS_TO_APPEND "") + +if (LLVM_TARGETS_TO_BUILD MATCHES "AMDGPU") + add_subdirectory(AMDGPU) + list(APPEND TARGETS_TO_APPEND LLVMMCACustomBehaviourAMDGPU) + list(APPEND MACROS_TO_APPEND -DHAS_AMDGPU) +endif() + +set(LLVM_MCA_CUSTOMBEHAVIOUR_TARGETS ${TARGETS_TO_APPEND} PARENT_SCOPE) +set(LLVM_MCA_MACROS_TO_DEFINE ${MACROS_TO_APPEND} PARENT_SCOPE)
diff --git a/src/llvm-project/llvm/tools/llvm-mca/llvm-mca.cpp b/src/llvm-project/llvm/tools/llvm-mca/llvm-mca.cpp index 13a2c63..a473cd8 100644 --- a/src/llvm-project/llvm/tools/llvm-mca/llvm-mca.cpp +++ b/src/llvm-project/llvm/tools/llvm-mca/llvm-mca.cpp
@@ -32,6 +32,9 @@ #include "Views/SchedulerStatistics.h" #include "Views/SummaryView.h" #include "Views/TimelineView.h" +#ifdef HAS_AMDGPU +#include "lib/AMDGPU/AMDGPUCustomBehaviour.h" +#endif #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" @@ -42,6 +45,7 @@ #include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/MCA/CodeEmitter.h" #include "llvm/MCA/Context.h" +#include "llvm/MCA/CustomBehaviour.h" #include "llvm/MCA/InstrBuilder.h" #include "llvm/MCA/Pipeline.h" #include "llvm/MCA/Stages/EntryStage.h" @@ -91,15 +95,13 @@ cl::desc("Target a specific cpu type (-mcpu=help for details)"), cl::value_desc("cpu-name"), cl::cat(ToolOptions), cl::init("native")); -static cl::opt<std::string> - MATTR("mattr", - cl::desc("Additional target features."), - cl::cat(ToolOptions)); +static cl::opt<std::string> MATTR("mattr", + cl::desc("Additional target features."), + cl::cat(ToolOptions)); -static cl::opt<bool> - PrintJson("json", - cl::desc("Print the output in json format"), - cl::cat(ToolOptions), cl::init(false)); +static cl::opt<bool> PrintJson("json", + cl::desc("Print the output in json format"), + cl::cat(ToolOptions), cl::init(false)); static cl::opt<int> OutputAsmVariant("output-asm-variant", @@ -172,11 +174,11 @@ cl::desc("Maximum number of iterations to print in timeline view"), cl::cat(ViewOptions), cl::init(0)); -static cl::opt<unsigned> TimelineMaxCycles( - "timeline-max-cycles", - cl::desc( - "Maximum number of cycles in the timeline view. Defaults to 80 cycles"), - cl::cat(ViewOptions), cl::init(80)); +static cl::opt<unsigned> + TimelineMaxCycles("timeline-max-cycles", + cl::desc("Maximum number of cycles in the timeline view, " + "or 0 for unlimited. Defaults to 80 cycles"), + cl::cat(ViewOptions), cl::init(80)); static cl::opt<bool> AssumeNoAlias("noalias", @@ -220,6 +222,12 @@ cl::desc("Print encoding information in the instruction info view"), cl::cat(ViewOptions), cl::init(false)); +static cl::opt<bool> DisableCustomBehaviour( + "disable-cb", + cl::desc( + "Disable custom behaviour (use the default class which does nothing)."), + cl::cat(ViewOptions), cl::init(false)); + namespace { const Target *getTarget(const char *ProgName) { @@ -236,6 +244,9 @@ return nullptr; } + // Update TripleName with the updated triple from the target lookup. + TripleName = TheTriple.str(); + // Return the found target. return TheTarget; } @@ -244,8 +255,8 @@ if (OutputFilename == "") OutputFilename = "-"; std::error_code EC; - auto Out = - std::make_unique<ToolOutputFile>(OutputFilename, EC, sys::fs::OF_Text); + auto Out = std::make_unique<ToolOutputFile>(OutputFilename, EC, + sys::fs::OF_TextWithCRLF); if (!EC) return std::move(Out); return EC; @@ -257,14 +268,15 @@ O = Default.getValue(); } -static void processViewOptions() { +static void processViewOptions(bool IsOutOfOrder) { if (!EnableAllViews.getNumOccurrences() && !EnableAllStats.getNumOccurrences()) return; if (EnableAllViews.getNumOccurrences()) { processOptionImpl(PrintSummaryView, EnableAllViews); - processOptionImpl(EnableBottleneckAnalysis, EnableAllViews); + if (IsOutOfOrder) + processOptionImpl(EnableBottleneckAnalysis, EnableAllViews); processOptionImpl(PrintResourcePressureView, EnableAllViews); processOptionImpl(PrintTimelineView, EnableAllViews); processOptionImpl(PrintInstructionInfoView, EnableAllViews); @@ -277,7 +289,41 @@ processOptionImpl(PrintRegisterFileStats, Default); processOptionImpl(PrintDispatchStats, Default); processOptionImpl(PrintSchedulerStats, Default); - processOptionImpl(PrintRetireStats, Default); + if (IsOutOfOrder) + processOptionImpl(PrintRetireStats, Default); +} + +std::unique_ptr<mca::InstrPostProcess> +createInstrPostProcess(const Triple &TheTriple, const MCSubtargetInfo &STI, + const MCInstrInfo &MCII) { + // Might be a good idea to have a separate flag so that InstrPostProcess + // can be used with or without CustomBehaviour + if (DisableCustomBehaviour) + return std::make_unique<mca::InstrPostProcess>(STI, MCII); +#ifdef HAS_AMDGPU + if (TheTriple.isAMDGPU()) + return std::make_unique<mca::AMDGPUInstrPostProcess>(STI, MCII); +#endif + return std::make_unique<mca::InstrPostProcess>(STI, MCII); +} + +std::unique_ptr<mca::CustomBehaviour> +createCustomBehaviour(const Triple &TheTriple, const MCSubtargetInfo &STI, + const mca::SourceMgr &SrcMgr, const MCInstrInfo &MCII) { + // Build the appropriate CustomBehaviour object for the current target. + // The CustomBehaviour class should never depend on the source code, + // but it can depend on the list of mca::Instruction and any classes + // that can be built using just the target info. If you need extra + // information from the source code or the list of MCInst, consider + // adding that information to the mca::Instruction class and setting + // it during InstrBuilder::createInstruction(). + if (DisableCustomBehaviour) + return std::make_unique<mca::CustomBehaviour>(STI, SrcMgr, MCII); +#ifdef HAS_AMDGPU + if (TheTriple.isAMDGPU()) + return std::make_unique<mca::AMDGPUCustomBehaviour>(STI, SrcMgr, MCII); +#endif + return std::make_unique<mca::CustomBehaviour>(STI, SrcMgr, MCII); } // Returns true on success. @@ -327,9 +373,6 @@ return 1; } - // Apply overrides to llvm-mca specific options. - processViewOptions(); - if (MCPU == "native") MCPU = std::string(llvm::sys::getHostCPUName()); @@ -339,10 +382,10 @@ if (!STI->isCPUStringValid(MCPU)) return 1; - if (!PrintInstructionTables && !STI->getSchedModel().isOutOfOrder()) { - WithColor::error() << "please specify an out-of-order cpu. '" << MCPU - << "' is an in-order cpu.\n"; - return 1; + bool IsOutOfOrder = STI->getSchedModel().isOutOfOrder(); + if (!PrintInstructionTables && !IsOutOfOrder) { + WithColor::warning() << "support for in-order CPU '" << MCPU + << "' is experimental.\n"; } if (!STI->getSchedModel().hasInstrSchedModel()) { @@ -358,6 +401,9 @@ return 1; } + // Apply overrides to llvm-mca specific options. + processViewOptions(IsOutOfOrder); + std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); assert(MRI && "Unable to create target register info!"); @@ -366,15 +412,15 @@ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); assert(MAI && "Unable to create target asm info!"); - MCObjectFileInfo MOFI; SourceMgr SrcMgr; // Tell SrcMgr about this buffer, which is what the parser will pick up. SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); - MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); - - MOFI.InitMCObjectFileInfo(TheTriple, /* PIC= */ false, Ctx); + MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); + Ctx.setObjectFileInfo(MOFI.get()); std::unique_ptr<buffer_ostream> BOS; @@ -384,9 +430,28 @@ std::unique_ptr<MCInstrAnalysis> MCIA( TheTarget->createMCInstrAnalysis(MCII.get())); + // Need to initialize an MCInstPrinter as it is + // required for initializing the MCTargetStreamer + // which needs to happen within the CRG.parseCodeRegions() call below. + // Without an MCTargetStreamer, certain assembly directives can trigger a + // segfault. (For example, the .cv_fpo_proc directive on x86 will segfault if + // we don't initialize the MCTargetStreamer.) + unsigned IPtempOutputAsmVariant = + OutputAsmVariant == -1 ? 0 : OutputAsmVariant; + std::unique_ptr<MCInstPrinter> IPtemp(TheTarget->createMCInstPrinter( + Triple(TripleName), IPtempOutputAsmVariant, *MAI, *MCII, *MRI)); + if (!IPtemp) { + WithColor::error() + << "unable to create instruction printer for target triple '" + << TheTriple.normalize() << "' with assembly variant " + << IPtempOutputAsmVariant << ".\n"; + return 1; + } + // Parse the input and create CodeRegions that llvm-mca can analyze. mca::AsmCodeRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII); - Expected<const mca::CodeRegions &> RegionsOrErr = CRG.parseCodeRegions(); + Expected<const mca::CodeRegions &> RegionsOrErr = + CRG.parseCodeRegions(std::move(IPtemp)); if (!RegionsOrErr) { if (auto Err = handleErrors(RegionsOrErr.takeError(), [](const StringError &E) { @@ -456,24 +521,19 @@ *STI, *MRI, mc::InitMCTargetOptionsFromFlags())); assert(MAB && "Unable to create asm backend!"); + json::Object JSONOutput; for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) { // Skip empty code regions. if (Region->empty()) continue; - // Don't print the header of this region if it is the default region, and - // it doesn't have an end location. - if (Region->startLoc().isValid() || Region->endLoc().isValid()) { - TOF->os() << "\n[" << RegionIdx++ << "] Code Region"; - StringRef Desc = Region->getDescription(); - if (!Desc.empty()) - TOF->os() << " - " << Desc; - TOF->os() << "\n\n"; - } + IB.clear(); // Lower the MCInst sequence into an mca::Instruction sequence. ArrayRef<MCInst> Insts = Region->getInstructions(); mca::CodeEmitter CE(*STI, *MAB, *MCE, Insts); + std::unique_ptr<mca::InstrPostProcess> IPP = + createInstrPostProcess(TheTriple, *STI, *MCII); std::vector<std::unique_ptr<mca::Instruction>> LoweredSequence; for (const MCInst &MCI : Insts) { Expected<std::unique_ptr<mca::Instruction>> Inst = @@ -496,6 +556,8 @@ return 1; } + IPP->postProcessInstruction(Inst.get(), MCI); + LoweredSequence.emplace_back(std::move(Inst.get())); } @@ -506,7 +568,12 @@ auto P = std::make_unique<mca::Pipeline>(); P->appendStage(std::make_unique<mca::EntryStage>(S)); P->appendStage(std::make_unique<mca::InstructionTables>(SM)); - mca::PipelinePrinter Printer(*P, mca::View::OK_READABLE); + + mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO); + if (PrintJson) { + Printer.addView( + std::make_unique<mca::InstructionView>(*STI, *IP, Insts)); + } // Create the views for this pipeline, execute, and emit a report. if (PrintInstructionInfoView) { @@ -519,26 +586,47 @@ if (!runPipeline(*P)) return 1; - Printer.printReport(TOF->os()); + if (PrintJson) { + Printer.printReport(JSONOutput); + } else { + Printer.printReport(TOF->os()); + } + + ++RegionIdx; continue; } + // Create the CustomBehaviour object for enforcing Target Specific + // behaviours and dependencies that aren't expressed well enough + // in the tablegen. CB cannot depend on the list of MCInst or + // the source code (but it can depend on the list of + // mca::Instruction or any objects that can be reconstructed + // from the target information). + std::unique_ptr<mca::CustomBehaviour> CB = + createCustomBehaviour(TheTriple, *STI, S, *MCII); + // Create a basic pipeline simulating an out-of-order backend. - auto P = MCA.createDefaultPipeline(PO, S); - mca::PipelinePrinter Printer(*P, PrintJson ? mca::View::OK_JSON - : mca::View::OK_READABLE); + auto P = MCA.createDefaultPipeline(PO, S, *CB); + + mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO); // When we output JSON, we add a view that contains the instructions // and CPU resource information. - if (PrintJson) - Printer.addView( - std::make_unique<mca::InstructionView>(*STI, *IP, Insts, MCPU)); + if (PrintJson) { + auto IV = std::make_unique<mca::InstructionView>(*STI, *IP, Insts); + Printer.addView(std::move(IV)); + } if (PrintSummaryView) Printer.addView( std::make_unique<mca::SummaryView>(SM, Insts, DispatchWidth)); if (EnableBottleneckAnalysis) { + if (!IsOutOfOrder) { + WithColor::warning() + << "bottleneck analysis is not supported for in-order CPU '" << MCPU + << "'.\n"; + } Printer.addView(std::make_unique<mca::BottleneckAnalysis>( *STI, *IP, Insts, S.getNumIterations())); } @@ -574,12 +662,18 @@ if (!runPipeline(*P)) return 1; - Printer.printReport(TOF->os()); + if (PrintJson) { + Printer.printReport(JSONOutput); + } else { + Printer.printReport(TOF->os()); + } - // Clear the InstrBuilder internal state in preparation for another round. - IB.clear(); + ++RegionIdx; } + if (PrintJson) + TOF->os() << formatv("{0:2}", json::Value(std::move(JSONOutput))) << "\n"; + TOF->keep(); return 0; }
diff --git a/src/llvm-project/llvm/tools/llvm-ml/Disassembler.cpp b/src/llvm-project/llvm/tools/llvm-ml/Disassembler.cpp index 8eeddb7..793128c 100644 --- a/src/llvm-project/llvm/tools/llvm-ml/Disassembler.cpp +++ b/src/llvm-project/llvm/tools/llvm-ml/Disassembler.cpp
@@ -123,31 +123,31 @@ return false; } -int Disassembler::disassemble(const Target &T, const std::string &Triple, +int Disassembler::disassemble(const Target &T, const std::string &TripleName, MCSubtargetInfo &STI, MCStreamer &Streamer, MemoryBuffer &Buffer, SourceMgr &SM, raw_ostream &Out) { - std::unique_ptr<const MCRegisterInfo> MRI(T.createMCRegInfo(Triple)); + std::unique_ptr<const MCRegisterInfo> MRI(T.createMCRegInfo(TripleName)); if (!MRI) { - errs() << "error: no register info for target " << Triple << "\n"; + errs() << "error: no register info for target " << TripleName << "\n"; return -1; } MCTargetOptions MCOptions; std::unique_ptr<const MCAsmInfo> MAI( - T.createMCAsmInfo(*MRI, Triple, MCOptions)); + T.createMCAsmInfo(*MRI, TripleName, MCOptions)); if (!MAI) { - errs() << "error: no assembly info for target " << Triple << "\n"; + errs() << "error: no assembly info for target " << TripleName << "\n"; return -1; } // Set up the MCContext for creating symbols and MCExpr's. - MCContext Ctx(MAI.get(), MRI.get(), nullptr); + MCContext Ctx(Triple(TripleName), MAI.get(), MRI.get(), &STI); std::unique_ptr<const MCDisassembler> DisAsm( T.createMCDisassembler(STI, Ctx)); if (!DisAsm) { - errs() << "error: no disassembler for target " << Triple << "\n"; + errs() << "error: no disassembler for target " << TripleName << "\n"; return -1; }
diff --git a/src/llvm-project/llvm/tools/llvm-ml/Opts.td b/src/llvm-project/llvm/tools/llvm-ml/Opts.td index 4c2757b..631c856 100644 --- a/src/llvm-project/llvm/tools/llvm-ml/Opts.td +++ b/src/llvm-project/llvm/tools/llvm-ml/Opts.td
@@ -27,29 +27,12 @@ class UnsupportedSeparate<string name> : Separate<["/", "-"], name>, Group<unsupported_Group>; -def help : MLFlag<"?">, - HelpText<"Display available options">; -def help_long : MLFlag<"help">, Alias<help>; -def assemble_only : MLFlag<"c">, HelpText<"Assemble only; do not link">; -def define : MLJoinedOrSeparate<"D">, MetaVarName<"<macro>=<value>">, - HelpText<"Define <macro> to <value> (or blank if <value> " - "omitted)">; -def output_file : MLJoinedOrSeparate<"Fo">, HelpText<"Names the output file">; -def include_path : MLJoinedOrSeparate<"I">, - HelpText<"Sets path for include files">; -def safeseh : MLFlag<"safeseh">, - HelpText<"Mark resulting object files as either containing no " - "exception handlers or containing exception handlers " - "that are all declared with .SAFESEH. Only available in " - "32-bit.">; -def assembly_file : MLJoinedOrSeparate<"Ta">, - HelpText<"Assemble source file with name not ending with " - "the .asm extension">; - def bitness : LLVMJoined<"m">, Values<"32,64">, HelpText<"Target platform (x86 or x86-64)">; def as_lex : LLVMFlag<"as-lex">, HelpText<"Lex tokens from a .asm file without assembling">; +def fatal_warnings : LLVMFlag<"fatal-warnings">, + HelpText<"Treat warnings as errors">; def filetype : LLVMJoined<"filetype=">, Values<"obj,s,null">, HelpText<"Emit a file with the given type">; def output_att_asm : LLVMFlag<"output-att-asm">, @@ -68,6 +51,36 @@ HelpText<"Preserve comments in output assembly">; def save_temp_labels : LLVMFlag<"save-temp-labels">, HelpText<"Don't discard temporary labels">; +def timestamp : LLVMJoined<"timestamp=">, + HelpText<"Specify the assembly timestamp (used for @Date and " + "@Time built-ins)">; +def utc : LLVMFlag<"utc">, + HelpText<"Render @Date and @Time built-ins in GMT/UTC">; +def gmtime : LLVMFlag<"gmtime">, Alias<utc>; + +def help : MLFlag<"?">, + HelpText<"Display available options">; +def help_long : MLFlag<"help">, Alias<help>; +def assemble_only : MLFlag<"c">, HelpText<"Assemble only; do not link">; +def define : MLJoinedOrSeparate<"D">, MetaVarName<"<macro>=<value>">, + HelpText<"Define <macro> to <value> (or blank if <value> " + "omitted)">; +def output_file : MLJoinedOrSeparate<"Fo">, HelpText<"Names the output file">; +def include_path : MLJoinedOrSeparate<"I">, + HelpText<"Sets path for include files">; +def safeseh : MLFlag<"safeseh">, + HelpText<"Mark resulting object files as either containing no " + "exception handlers or containing exception handlers " + "that are all declared with .SAFESEH. Only available in " + "32-bit.">; +def assembly_file : MLJoinedOrSeparate<"Ta">, + HelpText<"Assemble source file with name not ending with " + "the .asm extension">; +def error_on_warning : MLFlag<"WX">, Alias<fatal_warnings>; +def parse_only : MLFlag<"Zs">, HelpText<"Run a syntax-check only">, + Alias<filetype>, AliasArgs<["null"]>; +def ignore_include_envvar : MLFlag<"X">, + HelpText<"Ignore the INCLUDE environment variable">; def tiny_model_support : UnsupportedFlag<"AT">, HelpText<"">; def alternate_linker : UnsupportedJoined<"Bl">, HelpText<"">; @@ -100,11 +113,8 @@ def listing_false_conditionals : UnsupportedFlag<"Sx">, HelpText<"">; def extra_warnings : UnsupportedFlag<"w">, HelpText<"">; def warning_level : UnsupportedJoined<"W">, HelpText<"">; -def error_on_warning : UnsupportedFlag<"WX">, HelpText<"">; -def ignore_include_envvar : UnsupportedFlag<"X">, HelpText<"">; def line_number_info : UnsupportedFlag<"Zd">, HelpText<"">; def export_all_symbols : UnsupportedFlag<"Zf">, HelpText<"">; def codeview_info : UnsupportedFlag<"Zi">, HelpText<"">; def enable_m510_option : UnsupportedFlag<"Zm">, HelpText<"">; def structure_packing : UnsupportedJoined<"Zp">, HelpText<"">; -def parse_only : UnsupportedFlag<"Zs">, HelpText<"">;
diff --git a/src/llvm-project/llvm/tools/llvm-ml/llvm-ml.cpp b/src/llvm-project/llvm/tools/llvm-ml/llvm-ml.cpp index 1733dcd..0e6b055 100644 --- a/src/llvm-project/llvm/tools/llvm-ml/llvm-ml.cpp +++ b/src/llvm-project/llvm/tools/llvm-ml/llvm-ml.cpp
@@ -36,11 +36,13 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" +#include <ctime> using namespace llvm; using namespace llvm::opt; @@ -96,7 +98,7 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path) { std::error_code EC; - auto Out = std::make_unique<ToolOutputFile>(Path, EC, sys::fs::F_None); + auto Out = std::make_unique<ToolOutputFile>(Path, EC, sys::fs::OF_None); if (EC) { WithColor::error() << EC.message() << '\n'; return nullptr; @@ -129,8 +131,31 @@ MCAsmInfo &MAI, MCSubtargetInfo &STI, MCInstrInfo &MCII, MCTargetOptions &MCOptions, const opt::ArgList &InputArgs) { + struct tm TM; + time_t Timestamp; + if (InputArgs.hasArg(OPT_timestamp)) { + StringRef TimestampStr = InputArgs.getLastArgValue(OPT_timestamp); + int64_t IntTimestamp; + if (TimestampStr.getAsInteger(10, IntTimestamp)) { + WithColor::error(errs(), ProgName) + << "invalid timestamp '" << TimestampStr + << "'; must be expressed in seconds since the UNIX epoch.\n"; + return 1; + } + Timestamp = IntTimestamp; + } else { + Timestamp = time(nullptr); + } + if (InputArgs.hasArg(OPT_utc)) { + // Not thread-safe. + TM = *gmtime(&Timestamp); + } else { + // Not thread-safe. + TM = *localtime(&Timestamp); + } + std::unique_ptr<MCAsmParser> Parser( - createMCMasmParser(SrcMgr, Ctx, Str, MAI, 0)); + createMCMasmParser(SrcMgr, Ctx, Str, MAI, TM, 0)); std::unique_ptr<MCTargetAsmParser> TAP( TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions)); @@ -219,7 +244,7 @@ if (InputArgs.hasArg(OPT_help)) { std::string Usage = llvm::formatv("{0} [ /options ] file", ProgName).str(); - T.PrintHelp(outs(), Usage.c_str(), "LLVM MASM Assembler", + T.printHelp(outs(), Usage.c_str(), "LLVM MASM Assembler", /*ShowHidden=*/false); return 0; } else if (InputFilename.empty()) { @@ -231,6 +256,7 @@ MCTargetOptions MCOptions; MCOptions.AssemblyLanguage = "masm"; + MCOptions.MCFatalWarnings = InputArgs.hasArg(OPT_fatal_warnings); Triple TheTriple = GetTriple(ProgName, InputArgs); std::string Error; @@ -262,8 +288,21 @@ SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); // Record the location of the include directories so that the lexer can find - // it later. - SrcMgr.setIncludeDirs(InputArgs.getAllArgValues(OPT_include_path)); + // included files later. + std::vector<std::string> IncludeDirs = + InputArgs.getAllArgValues(OPT_include_path); + if (!InputArgs.hasArg(OPT_ignore_include_envvar)) { + if (llvm::Optional<std::string> IncludeEnvVar = + llvm::sys::Process::GetEnv("INCLUDE")) { + SmallVector<StringRef, 8> Dirs; + StringRef(*IncludeEnvVar) + .split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + IncludeDirs.reserve(IncludeDirs.size() + Dirs.size()); + for (StringRef Dir : Dirs) + IncludeDirs.push_back(Dir.str()); + } + } + SrcMgr.setIncludeDirs(IncludeDirs); std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); assert(MRI && "Unable to create target register info!"); @@ -274,12 +313,16 @@ MAI->setPreserveAsmComments(InputArgs.hasArg(OPT_preserve_comments)); + std::unique_ptr<MCSubtargetInfo> STI(TheTarget->createMCSubtargetInfo( + TripleName, /*CPU=*/"", /*Features=*/"")); + assert(STI && "Unable to create subtarget info!"); + // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and // MCObjectFileInfo needs a MCContext reference in order to initialize itself. - MCObjectFileInfo MOFI; - MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); - MOFI.InitMCObjectFileInfo(TheTriple, /*PIC=*/false, Ctx, - /*LargeCodeModel=*/true); + MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); + std::unique_ptr<MCObjectFileInfo> MOFI(TheTarget->createMCObjectFileInfo( + Ctx, /*PIC=*/false, /*LargeCodeModel=*/true)); + Ctx.setObjectFileInfo(MOFI.get()); if (InputArgs.hasArg(OPT_save_temp_labels)) Ctx.setAllowTemporaryLabels(false); @@ -311,10 +354,6 @@ std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); assert(MCII && "Unable to create instruction info!"); - std::unique_ptr<MCSubtargetInfo> STI(TheTarget->createMCSubtargetInfo( - TripleName, /*CPU=*/"", /*Features=*/"")); - assert(STI && "Unable to create subtarget info!"); - MCInstPrinter *IP = nullptr; if (FileType == "s") { const bool OutputATTAsm = InputArgs.hasArg(OPT_output_att_asm);
diff --git a/src/llvm-project/llvm/tools/llvm-modextract/llvm-modextract.cpp b/src/llvm-project/llvm/tools/llvm-modextract/llvm-modextract.cpp index 7c40996..9a44cbf 100644 --- a/src/llvm-project/llvm/tools/llvm-modextract/llvm-modextract.cpp +++ b/src/llvm-project/llvm/tools/llvm-modextract/llvm-modextract.cpp
@@ -21,21 +21,29 @@ using namespace llvm; +static cl::OptionCategory ModextractCategory("Modextract Options"); + static cl::opt<bool> - BinaryExtract("b", cl::desc("Whether to perform binary extraction")); + BinaryExtract("b", cl::desc("Whether to perform binary extraction"), + cl::cat(ModextractCategory)); static cl::opt<std::string> OutputFilename("o", cl::Required, cl::desc("Output filename"), - cl::value_desc("filename")); + cl::value_desc("filename"), + cl::cat(ModextractCategory)); -static cl::opt<std::string> - InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); +static cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input bitcode>"), + cl::init("-"), + cl::cat(ModextractCategory)); static cl::opt<unsigned> ModuleIndex("n", cl::Required, cl::desc("Index of module to extract"), - cl::value_desc("index")); + cl::value_desc("index"), + cl::cat(ModextractCategory)); int main(int argc, char **argv) { + cl::HideUnrelatedOptions({&ModextractCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "Module extractor"); ExitOnError ExitOnErr("llvm-modextract: error: ");
diff --git a/src/llvm-project/llvm/tools/llvm-mt/llvm-mt.cpp b/src/llvm-project/llvm/tools/llvm-mt/llvm-mt.cpp index 997e5ac..6f26765 100644 --- a/src/llvm-project/llvm/tools/llvm-mt/llvm-mt.cpp +++ b/src/llvm-project/llvm/tools/llvm-mt/llvm-mt.cpp
@@ -109,7 +109,7 @@ } if (InputArgs.hasArg(OPT_help)) { - T.PrintHelp(outs(), "llvm-mt [options] file...", "Manifest Tool", false); + T.printHelp(outs(), "llvm-mt [options] file...", "Manifest Tool", false); return 0; }
diff --git a/src/llvm-project/llvm/tools/llvm-nm/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-nm/CMakeLists.txt index 42699bf..4ad5370 100644 --- a/src/llvm-project/llvm/tools/llvm-nm/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-nm/CMakeLists.txt
@@ -6,14 +6,20 @@ Core Demangle Object + Option Support TextAPI ) +set(LLVM_TARGET_DEFINITIONS Opts.td) +tablegen(LLVM Opts.inc -gen-opt-parser-defs) +add_public_tablegen_target(NmOptsTableGen) + add_llvm_tool(llvm-nm llvm-nm.cpp DEPENDS + NmOptsTableGen intrinsics_gen )
diff --git a/src/llvm-project/llvm/tools/llvm-nm/Opts.td b/src/llvm-project/llvm/tools/llvm-nm/Opts.td new file mode 100644 index 0000000..3a79089 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-nm/Opts.td
@@ -0,0 +1,76 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass BB<string name, string help1, string help2> { + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; +} + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def debug_syms : FF<"debug-syms", "Show all symbols, even debugger only">; +def defined_only : FF<"defined-only", "Show only defined symbols">; +defm demangle : BB<"demangle", "Demangle C++ symbol names", "Don't demangle symbol names">; +def dynamic : FF<"dynamic", "Display dynamic symbols instead of normal symbols">; +def extern_only : FF<"extern-only", "Show only external symbols">; +defm format : Eq<"format", "Specify output format: bsd (default), posix, sysv, darwin, just-symbols">, MetaVarName<"<format>">; +def help : FF<"help", "Display this help">; +def no_llvm_bc : FF<"no-llvm-bc", "Disable LLVM bitcode reader">; +def no_sort : FF<"no-sort", "Show symbols in order encountered">; +def no_weak : FF<"no-weak", "Show only non-weak symbols">; +def numeric_sort : FF<"numeric-sort", "Sort symbols by address">; +def print_armap : FF<"print-armap", "Print the archive map">; +def print_file_name : FF<"print-file-name", "Precede each symbol with the object file it came from">; +def print_size : FF<"print-size", "Show symbol size as well as address">; +def quiet : FF<"quiet", "Suppress 'no symbols' diagnostic">; +defm radix : Eq<"radix", "Radix (o/d/x) for printing symbol Values">, MetaVarName<"<radix>">; +def reverse_sort : FF<"reverse-sort", "Sort in reverse order">; +def size_sort : FF<"size-sort", "Sort symbols by size">; +def special_syms : FF<"special-syms", "Do not filter special symbols from the output">; +def undefined_only : FF<"undefined-only", "Show only undefined symbols">; +def version : FF<"version", "Display the version">; +def without_aliases : FF<"without-aliases", "Exclude aliases from output">, Flags<[HelpHidden]>; + +// Mach-O specific options. +def grp_mach_o : OptionGroup<"kind">, HelpText<"llvm-nm Mach-O Specific Options">; + +def add_dyldinfo : FF<"add-dyldinfo", "Add symbols from the dyldinfo not already in the symbol table">, Group<grp_mach_o>; +def add_inlinedinfo : FF<"add-inlinedinfo", "Add symbols from the inlined libraries, TBD only">, Group<grp_mach_o>; +def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>; +def : Separate<["--", "-"], "arch">, Alias<arch_EQ>; +def dyldinfo_only : FF<"dyldinfo-only", "Show only symbols from the dyldinfo">, Group<grp_mach_o>; +def no_dyldinfo : FF<"no-dyldinfo", "Don't add any symbols from the dyldinfo">, Group<grp_mach_o>; +def s : F<"s", "Dump only symbols from this segment and section name">, Group<grp_mach_o>; +def x : F<"x", "Print symbol entry in hex">, Group<grp_mach_o>; + +def : FF<"just-symbol-name", "Alias for --format=just-symbols">, Alias<format_EQ>, AliasArgs<["just-symbols"]>, Flags<[HelpHidden]>; +def : FF<"portability", "Alias for --format=posix">, Alias<format_EQ>, AliasArgs<["posix"]>; + +def : F<"a", "Alias for --debug-syms">, Alias<debug_syms>; +def : F<"A", "Alias for --print-file-name">, Alias<print_file_name>; +def : F<"B", "Alias for --format=bsd">, Alias<format_EQ>, AliasArgs<["bsd"]>; +def : F<"C", "Alias for --demangle">, Alias<demangle>; +def : F<"D", "Alias for --dynamic">, Alias<dynamic>; +def : JoinedOrSeparate<["-"], "f">, HelpText<"Alias for --format">, Alias<format_EQ>, MetaVarName<"<format>">; +def : F<"h", "Alias for --help">, Alias<help>; +def : F<"g", "Alias for --extern-only">, Alias<extern_only>; +def : F<"j", "Alias for --format=just-symbols">, Alias<format_EQ>, AliasArgs<["just-symbols"]>; +def : F<"m", "Alias for --format=darwin">, Alias<format_EQ>, AliasArgs<["darwin"]>; +def : F<"M", "Deprecated alias for --print-armap">, Alias<print_armap>, Flags<[HelpHidden]>; +def : F<"n", "Alias for --numeric-sort">, Alias<numeric_sort>; +def : F<"o", "Alias for --print-file-name">, Alias<print_file_name>; +def : F<"p", "Alias for --no-sort">, Alias<no_sort>; +def : F<"P", "Alias for --format=posix">, Alias<format_EQ>, AliasArgs<["posix"]>; +def : F<"r", "Alias for --reverse-sort">, Alias<reverse_sort>; +def : F<"S", "Alias for --print-size">, Alias<print_size>; +def : JoinedOrSeparate<["-"], "t">, HelpText<"Alias for --radix">, Alias<radix_EQ>, MetaVarName<"<radix>">; +def : F<"u", "Alias for --undefined-only">, Alias<undefined_only>; +def : F<"U", "Deprecated alias for --defined-only">, Alias<defined_only>, Flags<[HelpHidden]>; +def : F<"v", "Alias for --numeric-sort">, Alias<numeric_sort>; +def : F<"V", "Alias for --version">, Alias<version>; +def : F<"W", "Deprecated alias for --no-weak">, Alias<no_weak>, Flags<[HelpHidden]>;
diff --git a/src/llvm-project/llvm/tools/llvm-nm/llvm-nm.cpp b/src/llvm-project/llvm/tools/llvm-nm/llvm-nm.cpp index c678108..ffb427a 100644 --- a/src/llvm-project/llvm/tools/llvm-nm/llvm-nm.cpp +++ b/src/llvm-project/llvm/tools/llvm-nm/llvm-nm.cpp
@@ -31,6 +31,9 @@ #include "llvm/Object/TapiFile.h" #include "llvm/Object/TapiUniversal.h" #include "llvm/Object/Wasm.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" @@ -47,192 +50,86 @@ using namespace object; namespace { -enum OutputFormatTy { bsd, sysv, posix, darwin }; +using namespace llvm::opt; // for HelpHidden in Opts.inc +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; -cl::OptionCategory NMCat("llvm-nm Options"); +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX -cl::opt<OutputFormatTy> OutputFormat( - "format", cl::desc("Specify output format"), - cl::values(clEnumVal(bsd, "BSD format"), clEnumVal(sysv, "System V format"), - clEnumVal(posix, "POSIX.2 format"), - clEnumVal(darwin, "Darwin -m format")), - cl::init(bsd), cl::cat(NMCat)); -cl::alias OutputFormat2("f", cl::desc("Alias for --format"), - cl::aliasopt(OutputFormat)); +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; -cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), - cl::ZeroOrMore); +class NmOptTable : public opt::OptTable { +public: + NmOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; -cl::opt<bool> UndefinedOnly("undefined-only", - cl::desc("Show only undefined symbols"), - cl::cat(NMCat)); -cl::alias UndefinedOnly2("u", cl::desc("Alias for --undefined-only"), - cl::aliasopt(UndefinedOnly), cl::Grouping); +enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; +} // namespace -cl::opt<bool> DynamicSyms("dynamic", - cl::desc("Display the dynamic symbols instead " - "of normal symbols."), - cl::cat(NMCat)); -cl::alias DynamicSyms2("D", cl::desc("Alias for --dynamic"), - cl::aliasopt(DynamicSyms), cl::Grouping); +static bool ArchiveMap; +static bool DebugSyms; +static bool DefinedOnly; +static bool Demangle; +static bool DynamicSyms; +static bool ExternalOnly; +static OutputFormatTy OutputFormat; +static bool NoLLVMBitcode; +static bool NoSort; +static bool NoWeakSymbols; +static bool NumericSort; +static bool PrintFileName; +static bool PrintSize; +static bool Quiet; +static bool ReverseSort; +static bool SpecialSyms; +static bool SizeSort; +static bool UndefinedOnly; +static bool WithoutAliases; -cl::opt<bool> DefinedOnly("defined-only", cl::desc("Show only defined symbols"), - cl::cat(NMCat)); -cl::alias DefinedOnly2("U", cl::desc("Alias for --defined-only"), - cl::aliasopt(DefinedOnly), cl::Grouping); - -cl::opt<bool> ExternalOnly("extern-only", - cl::desc("Show only external symbols"), - cl::ZeroOrMore, cl::cat(NMCat)); -cl::alias ExternalOnly2("g", cl::desc("Alias for --extern-only"), - cl::aliasopt(ExternalOnly), cl::Grouping, - cl::ZeroOrMore); - -cl::opt<bool> NoWeakSymbols("no-weak", cl::desc("Show only non-weak symbols"), - cl::cat(NMCat)); -cl::alias NoWeakSymbols2("W", cl::desc("Alias for --no-weak"), - cl::aliasopt(NoWeakSymbols), cl::Grouping); - -cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd"), cl::Grouping, - cl::cat(NMCat)); -cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix"), - cl::Grouping, cl::cat(NMCat)); -cl::alias Portability("portability", cl::desc("Alias for --format=posix"), - cl::aliasopt(POSIXFormat), cl::NotHidden); -cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin"), - cl::Grouping, cl::cat(NMCat)); - -static cl::list<std::string> - ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), - cl::ZeroOrMore, cl::cat(NMCat)); -bool ArchAll = false; - -cl::opt<bool> PrintFileName( - "print-file-name", - cl::desc("Precede each symbol with the object file it came from"), - cl::cat(NMCat)); - -cl::alias PrintFileNameA("A", cl::desc("Alias for --print-file-name"), - cl::aliasopt(PrintFileName), cl::Grouping); -cl::alias PrintFileNameo("o", cl::desc("Alias for --print-file-name"), - cl::aliasopt(PrintFileName), cl::Grouping); - -cl::opt<bool> DebugSyms("debug-syms", - cl::desc("Show all symbols, even debugger only"), - cl::cat(NMCat)); -cl::alias DebugSymsa("a", cl::desc("Alias for --debug-syms"), - cl::aliasopt(DebugSyms), cl::Grouping); - -cl::opt<bool> NumericSort("numeric-sort", cl::desc("Sort symbols by address"), - cl::cat(NMCat)); -cl::alias NumericSortn("n", cl::desc("Alias for --numeric-sort"), - cl::aliasopt(NumericSort), cl::Grouping); -cl::alias NumericSortv("v", cl::desc("Alias for --numeric-sort"), - cl::aliasopt(NumericSort), cl::Grouping); - -cl::opt<bool> NoSort("no-sort", cl::desc("Show symbols in order encountered"), - cl::cat(NMCat)); -cl::alias NoSortp("p", cl::desc("Alias for --no-sort"), cl::aliasopt(NoSort), - cl::Grouping); - -cl::opt<bool> Demangle("demangle", cl::ZeroOrMore, - cl::desc("Demangle C++ symbol names"), cl::cat(NMCat)); -cl::alias DemangleC("C", cl::desc("Alias for --demangle"), - cl::aliasopt(Demangle), cl::Grouping); -cl::opt<bool> NoDemangle("no-demangle", cl::init(false), cl::ZeroOrMore, - cl::desc("Don't demangle symbol names"), - cl::cat(NMCat)); - -cl::opt<bool> ReverseSort("reverse-sort", cl::desc("Sort in reverse order"), - cl::cat(NMCat)); -cl::alias ReverseSortr("r", cl::desc("Alias for --reverse-sort"), - cl::aliasopt(ReverseSort), cl::Grouping); - -cl::opt<bool> PrintSize("print-size", - cl::desc("Show symbol size as well as address"), - cl::cat(NMCat)); -cl::alias PrintSizeS("S", cl::desc("Alias for --print-size"), - cl::aliasopt(PrintSize), cl::Grouping); -bool MachOPrintSizeWarning = false; - -cl::opt<bool> SizeSort("size-sort", cl::desc("Sort symbols by size"), - cl::cat(NMCat)); - -cl::opt<bool> WithoutAliases("without-aliases", cl::Hidden, - cl::desc("Exclude aliases from output"), - cl::cat(NMCat)); - -cl::opt<bool> ArchiveMap("print-armap", cl::desc("Print the archive map"), - cl::cat(NMCat)); -cl::alias ArchiveMaps("M", cl::desc("Alias for --print-armap"), - cl::aliasopt(ArchiveMap), cl::Grouping); - +namespace { enum Radix { d, o, x }; -cl::opt<Radix> - AddressRadix("radix", cl::desc("Radix (o/d/x) for printing symbol Values"), - cl::values(clEnumVal(d, "decimal"), clEnumVal(o, "octal"), - clEnumVal(x, "hexadecimal")), - cl::init(x), cl::cat(NMCat)); -cl::alias RadixAlias("t", cl::desc("Alias for --radix"), - cl::aliasopt(AddressRadix)); +} // namespace +static Radix AddressRadix; -cl::opt<bool> JustSymbolName("just-symbol-name", - cl::desc("Print just the symbol's name"), - cl::cat(NMCat)); -cl::alias JustSymbolNames("j", cl::desc("Alias for --just-symbol-name"), - cl::aliasopt(JustSymbolName), cl::Grouping); +// Mach-O specific options. +static bool ArchAll = false; +static std::vector<StringRef> ArchFlags; +static bool AddDyldInfo; +static bool AddInlinedInfo; +static bool DyldInfoOnly; +static bool FormatMachOasHex; +static bool NoDyldInfo; +static std::vector<StringRef> SegSect; +static bool MachOPrintSizeWarning = false; -cl::opt<bool> - SpecialSyms("special-syms", - cl::desc("Do not filter special symbols from the output"), - cl::cat(NMCat)); +// Miscellaneous states. +static bool PrintAddress = true; +static bool MultipleFiles = false; +static bool HadError = false; -cl::list<std::string> SegSect("s", cl::multi_val(2), cl::ZeroOrMore, - cl::value_desc("segment section"), cl::Hidden, - cl::desc("Dump only symbols from this segment " - "and section name, Mach-O only"), - cl::cat(NMCat)); - -cl::opt<bool> FormatMachOasHex("x", - cl::desc("Print symbol entry in hex, " - "Mach-O only"), - cl::Grouping, cl::cat(NMCat)); -cl::opt<bool> AddDyldInfo("add-dyldinfo", - cl::desc("Add symbols from the dyldinfo not already " - "in the symbol table, Mach-O only"), - cl::cat(NMCat)); -cl::opt<bool> NoDyldInfo("no-dyldinfo", - cl::desc("Don't add any symbols from the dyldinfo, " - "Mach-O only"), - cl::cat(NMCat)); -cl::opt<bool> DyldInfoOnly("dyldinfo-only", - cl::desc("Show only symbols from the dyldinfo, " - "Mach-O only"), - cl::cat(NMCat)); - -cl::opt<bool> NoLLVMBitcode("no-llvm-bc", - cl::desc("Disable LLVM bitcode reader"), - cl::cat(NMCat)); - -cl::opt<bool> AddInlinedInfo("add-inlinedinfo", - cl::desc("Add symbols from the inlined libraries, " - "TBD(Mach-O) only"), - cl::cat(NMCat)); - -cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); - -bool PrintAddress = true; - -bool MultipleFiles = false; - -bool HadError = false; - -std::string ToolName; -} // anonymous namespace +static StringRef ToolName; static void error(Twine Message, Twine Path = Twine()) { HadError = true; - WithColor::error(errs(), ToolName) << Path << ": " << Message << ".\n"; + WithColor::error(errs(), ToolName) << Path << ": " << Message << "\n"; } static bool error(std::error_code EC, Twine Path = Twine()) { @@ -262,13 +159,13 @@ errs() << "(" << NameOrErr.get() << ")"; if (!ArchitectureName.empty()) - errs() << " (for architecture " << ArchitectureName << ") "; + errs() << " (for architecture " << ArchitectureName << ")"; std::string Buf; raw_string_ostream OS(Buf); logAllUnhandledErrors(std::move(E), OS); OS.flush(); - errs() << " " << Buf << "\n"; + errs() << ": " << Buf << "\n"; } // This version of error() prints the file name and which architecture slice it @@ -281,13 +178,13 @@ WithColor::error(errs(), ToolName) << FileName; if (!ArchitectureName.empty()) - errs() << " (for architecture " << ArchitectureName << ") "; + errs() << " (for architecture " << ArchitectureName << ")"; std::string Buf; raw_string_ostream OS(Buf); logAllUnhandledErrors(std::move(E), OS); OS.flush(); - errs() << " " << Buf << "\n"; + errs() << ": " << Buf << "\n"; } namespace { @@ -737,16 +634,6 @@ } } -static bool isSpecialSym(SymbolicFile &Obj, StringRef Name) { - auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj); - if (!ELFObj) - return false; - uint16_t EMachine = ELFObj->getEMachine(); - if (EMachine != ELF::EM_ARM && EMachine != ELF::EM_AARCH64) - return false; - return !Name.empty() && Name[0] == '$'; -} - static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, StringRef ArchiveName, StringRef ArchitectureName) { @@ -769,10 +656,10 @@ } if (!PrintFileName) { - if (OutputFormat == posix && MultipleFiles && printName) { + if ((OutputFormat == bsd || OutputFormat == posix || + OutputFormat == just_symbols) && + MultipleFiles && printName) { outs() << '\n' << CurrentFilename << ":\n"; - } else if (OutputFormat == bsd && MultipleFiles && printName) { - outs() << "\n" << CurrentFilename << ":\n"; } else if (OutputFormat == sysv) { outs() << "\n\nSymbols from " << CurrentFilename << ":\n\n"; if (isSymbolList64Bit(Obj)) @@ -835,13 +722,14 @@ bool Undefined = SymFlags & SymbolRef::SF_Undefined; bool Global = SymFlags & SymbolRef::SF_Global; bool Weak = SymFlags & SymbolRef::SF_Weak; + bool FormatSpecific = SymFlags & SymbolRef::SF_FormatSpecific; if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) || (!Global && ExternalOnly) || (Weak && NoWeakSymbols) || - (!SpecialSyms && isSpecialSym(Obj, Name))) + (FormatSpecific && !(SpecialSyms || DebugSyms))) continue; if (PrintFileName) writeFileName(outs(), ArchiveName, ArchitectureName); - if ((JustSymbolName || + if ((OutputFormat == just_symbols || (UndefinedOnly && MachO && OutputFormat != darwin)) && OutputFormat != posix) { outs() << Name << "\n"; @@ -1142,13 +1030,16 @@ } } - if ((Symflags & object::SymbolRef::SF_Weak) && !isa<MachOObjectFile>(Obj)) { - char Ret = isObject(Obj, I) ? 'v' : 'w'; - return (!(Symflags & object::SymbolRef::SF_Undefined)) ? toupper(Ret) : Ret; + if (Symflags & object::SymbolRef::SF_Undefined) { + if (isa<MachOObjectFile>(Obj) || !(Symflags & object::SymbolRef::SF_Weak)) + return 'U'; + return isObject(Obj, I) ? 'v' : 'w'; } - - if (Symflags & object::SymbolRef::SF_Undefined) - return 'U'; + if (isa<ELFObjectFileBase>(&Obj)) + if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC) + return 'i'; + if (!isa<MachOObjectFile>(Obj) && (Symflags & object::SymbolRef::SF_Weak)) + return isObject(Obj, I) ? 'V' : 'W'; if (Symflags & object::SymbolRef::SF_Common) return 'C'; @@ -1169,8 +1060,6 @@ else if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj)) Ret = getSymbolNMTypeChar(*Tapi, I); else if (ELFObjectFileBase *ELF = dyn_cast<ELFObjectFileBase>(&Obj)) { - if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC) - return 'i'; Ret = getSymbolNMTypeChar(*ELF, I); if (ELFSymbolRef(*I).getBinding() == ELF::STB_GNU_UNIQUE) return Ret; @@ -1806,7 +1695,14 @@ error(SymFlagsOrErr.takeError(), Obj.getFileName()); return; } - if (!DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific)) + + // Don't drop format specifc symbols for ARM and AArch64 ELF targets, they + // are used to repesent mapping symbols and needed to honor the + // --special-syms option. + auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj); + if ((!ELFObj || (ELFObj->getEMachine() != ELF::EM_ARM && + ELFObj->getEMachine() != ELF::EM_AARCH64)) && + !DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific)) continue; if (WithoutAliases && (*SymFlagsOrErr & SymbolRef::SF_Indirect)) continue; @@ -1860,7 +1756,7 @@ CurrentFilename = Obj.getFileName(); - if (Symbols.empty() && SymbolList.empty()) { + if (Symbols.empty() && SymbolList.empty() && !Quiet) { writeFileName(errs(), ArchiveName, ArchitectureName); errs() << "no symbols\n"; } @@ -2228,8 +2124,80 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); - cl::HideUnrelatedOptions(NMCat); - cl::ParseCommandLineOptions(argc, argv, "llvm symbol table dumper\n"); + BumpPtrAllocator A; + StringSaver Saver(A); + NmOptTable Tbl; + ToolName = argv[0]; + opt::InputArgList Args = + Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { + error(Msg); + exit(1); + }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp( + outs(), + (Twine(ToolName) + " [options] <input object files>").str().c_str(), + "LLVM symbol table dumper"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + // This needs to contain the word "GNU", libtool looks for that string. + outs() << "llvm-nm, compatible with GNU nm" << '\n'; + cl::PrintVersionMessage(); + return 0; + } + + DebugSyms = Args.hasArg(OPT_debug_syms); + DefinedOnly = Args.hasArg(OPT_defined_only); + Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false); + DynamicSyms = Args.hasArg(OPT_dynamic); + ExternalOnly = Args.hasArg(OPT_extern_only); + StringRef V = Args.getLastArgValue(OPT_format_EQ, "bsd"); + if (V == "bsd") + OutputFormat = bsd; + else if (V == "posix") + OutputFormat = posix; + else if (V == "sysv") + OutputFormat = sysv; + else if (V == "darwin") + OutputFormat = darwin; + else if (V == "just-symbols") + OutputFormat = just_symbols; + else + error("--format value should be one of: bsd, posix, sysv, darwin, " + "just-symbols"); + NoLLVMBitcode = Args.hasArg(OPT_no_llvm_bc); + NoSort = Args.hasArg(OPT_no_sort); + NoWeakSymbols = Args.hasArg(OPT_no_weak); + NumericSort = Args.hasArg(OPT_numeric_sort); + ArchiveMap = Args.hasArg(OPT_print_armap); + PrintFileName = Args.hasArg(OPT_print_file_name); + PrintSize = Args.hasArg(OPT_print_size); + ReverseSort = Args.hasArg(OPT_reverse_sort); + Quiet = Args.hasArg(OPT_quiet); + V = Args.getLastArgValue(OPT_radix_EQ, "x"); + if (V == "o") + AddressRadix = Radix::o; + else if (V == "d") + AddressRadix = Radix::d; + else if (V == "x") + AddressRadix = Radix::x; + else + error("--radix value should be one of: 'o' (octal), 'd' (decimal), 'x' " + "(hexadecimal)"); + SizeSort = Args.hasArg(OPT_size_sort); + SpecialSyms = Args.hasArg(OPT_special_syms); + UndefinedOnly = Args.hasArg(OPT_undefined_only); + WithoutAliases = Args.hasArg(OPT_without_aliases); + + // Mach-O specific options. + FormatMachOasHex = Args.hasArg(OPT_x); + AddDyldInfo = Args.hasArg(OPT_add_dyldinfo); + AddInlinedInfo = Args.hasArg(OPT_add_inlinedinfo); + DyldInfoOnly = Args.hasArg(OPT_dyldinfo_only); + NoDyldInfo = Args.hasArg(OPT_no_dyldinfo); // llvm-nm only reads binary files. if (error(sys::ChangeStdinToBinary())) @@ -2240,14 +2208,6 @@ llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmParsers(); - ToolName = argv[0]; - if (BSDFormat) - OutputFormat = bsd; - if (POSIXFormat) - OutputFormat = posix; - if (DarwinFormat) - OutputFormat = darwin; - // The relative order of these is important. If you pass --size-sort it should // only print out the size. However, if you pass -S --size-sort, it should // print out both the size and address. @@ -2255,29 +2215,44 @@ PrintAddress = false; if (OutputFormat == sysv || SizeSort) PrintSize = true; - if (InputFilenames.empty()) - InputFilenames.push_back("a.out"); - if (InputFilenames.size() > 1) - MultipleFiles = true; - // If both --demangle and --no-demangle are specified then pick the last one. - if (NoDemangle.getPosition() > Demangle.getPosition()) - Demangle = !NoDemangle; - - for (unsigned i = 0; i < ArchFlags.size(); ++i) { - if (ArchFlags[i] == "all") { - ArchAll = true; - } else { - if (!MachOObjectFile::isValidArch(ArchFlags[i])) - error("Unknown architecture named '" + ArchFlags[i] + "'", + for (const auto *A : Args.filtered(OPT_arch_EQ)) { + SmallVector<StringRef, 2> Values; + llvm::SplitString(A->getValue(), Values, ","); + for (StringRef V : Values) { + if (V == "all") + ArchAll = true; + else if (MachOObjectFile::isValidArch(V)) + ArchFlags.push_back(V); + else + error("Unknown architecture named '" + V + "'", "for the --arch option"); } } + // Mach-O takes -s to accept two arguments. We emulate this by iterating over + // both OPT_s and OPT_INPUT. + std::vector<std::string> InputFilenames; + int SegSectArgs = 0; + for (opt::Arg *A : Args.filtered(OPT_s, OPT_INPUT)) { + if (SegSectArgs > 0) { + --SegSectArgs; + SegSect.push_back(A->getValue()); + } else if (A->getOption().matches(OPT_s)) { + SegSectArgs = 2; + } else { + InputFilenames.push_back(A->getValue()); + } + } if (!SegSect.empty() && SegSect.size() != 2) error("bad number of arguments (must be two arguments)", "for the -s option"); + if (InputFilenames.empty()) + InputFilenames.push_back("a.out"); + if (InputFilenames.size() > 1) + MultipleFiles = true; + if (NoDyldInfo && (AddDyldInfo || DyldInfoOnly)) error("--no-dyldinfo can't be used with --add-dyldinfo or --dyldinfo-only");
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/Buffer.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/Buffer.cpp deleted file mode 100644 index 06b2a20..0000000 --- a/src/llvm-project/llvm/tools/llvm-objcopy/Buffer.cpp +++ /dev/null
@@ -1,79 +0,0 @@ -//===- Buffer.cpp ---------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Buffer.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Process.h" -#include <memory> - -namespace llvm { -namespace objcopy { - -Buffer::~Buffer() {} - -static Error createEmptyFile(StringRef FileName) { - // Create an empty tempfile and atomically swap it in place with the desired - // output file. - Expected<sys::fs::TempFile> Temp = - sys::fs::TempFile::create(FileName + ".temp-empty-%%%%%%%"); - return Temp ? Temp->keep(FileName) : Temp.takeError(); -} - -Error FileBuffer::allocate(size_t Size) { - // When a 0-sized file is requested, skip allocation but defer file - // creation/truncation until commit() to avoid side effects if something - // happens between allocate() and commit(). - if (Size == 0) { - EmptyFile = true; - return Error::success(); - } - - Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable); - // FileOutputBuffer::create() returns an Error that is just a wrapper around - // std::error_code. Wrap it in FileError to include the actual filename. - if (!BufferOrErr) - return createFileError(getName(), BufferOrErr.takeError()); - Buf = std::move(*BufferOrErr); - return Error::success(); -} - -Error FileBuffer::commit() { - if (EmptyFile) - return createEmptyFile(getName()); - - assert(Buf && "allocate() not called before commit()!"); - Error Err = Buf->commit(); - // FileOutputBuffer::commit() returns an Error that is just a wrapper around - // std::error_code. Wrap it in FileError to include the actual filename. - return Err ? createFileError(getName(), std::move(Err)) : std::move(Err); -} - -uint8_t *FileBuffer::getBufferStart() { - return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); -} - -Error MemBuffer::allocate(size_t Size) { - Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName()); - return Error::success(); -} - -Error MemBuffer::commit() { return Error::success(); } - -uint8_t *MemBuffer::getBufferStart() { - return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); -} - -std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() { - return std::move(Buf); -} - -} // end namespace objcopy -} // end namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/Buffer.h b/src/llvm-project/llvm/tools/llvm-objcopy/Buffer.h deleted file mode 100644 index 487d558..0000000 --- a/src/llvm-project/llvm/tools/llvm-objcopy/Buffer.h +++ /dev/null
@@ -1,68 +0,0 @@ -//===- Buffer.h -------------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_BUFFER_H -#define LLVM_TOOLS_OBJCOPY_BUFFER_H - -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/MemoryBuffer.h" -#include <memory> - -namespace llvm { -namespace objcopy { - -// The class Buffer abstracts out the common interface of FileOutputBuffer and -// WritableMemoryBuffer so that the hierarchy of Writers depends on this -// abstract interface and doesn't depend on a particular implementation. -// TODO: refactor the buffer classes in LLVM to enable us to use them here -// directly. -class Buffer { - StringRef Name; - -public: - virtual ~Buffer(); - virtual Error allocate(size_t Size) = 0; - virtual uint8_t *getBufferStart() = 0; - virtual Error commit() = 0; - - explicit Buffer(StringRef Name) : Name(Name) {} - StringRef getName() const { return Name; } -}; - -class FileBuffer : public Buffer { - std::unique_ptr<FileOutputBuffer> Buf; - // Indicates that allocate(0) was called, and commit() should create or - // truncate a file instead of using a FileOutputBuffer. - bool EmptyFile = false; - -public: - Error allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit FileBuffer(StringRef FileName) : Buffer(FileName) {} -}; - -class MemBuffer : public Buffer { - std::unique_ptr<WritableMemoryBuffer> Buf; - -public: - Error allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit MemBuffer(StringRef Name) : Buffer(Name) {} - - std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer(); -}; - -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_BUFFER_H
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-objcopy/CMakeLists.txt index 1f733c3..d14d213 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-objcopy/CMakeLists.txt
@@ -22,14 +22,12 @@ add_public_tablegen_target(StripOptsTableGen) add_llvm_tool(llvm-objcopy - Buffer.cpp - CopyConfig.cpp + ConfigManager.cpp llvm-objcopy.cpp COFF/COFFObjcopy.cpp COFF/Object.cpp COFF/Reader.cpp COFF/Writer.cpp - ELF/ELFConfig.cpp ELF/ELFObjcopy.cpp ELF/Object.cpp MachO/MachOObjcopy.cpp
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFConfig.h b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFConfig.h new file mode 100644 index 0000000..3897ff4 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFConfig.h
@@ -0,0 +1,21 @@ +//===- COFFConfig.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H + +namespace llvm { +namespace objcopy { + +// Coff specific configuration for copying/stripping a single file. +struct COFFConfig {}; + +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp index b5de8a4..e50ac2e 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "COFFObjcopy.h" -#include "Buffer.h" -#include "CopyConfig.h" +#include "COFFConfig.h" +#include "CommonConfig.h" #include "Object.h" #include "Reader.h" #include "Writer.h" @@ -131,7 +131,7 @@ Sec.Header.Characteristics = NewCharacteristics; } -static Error handleArgs(const CopyConfig &Config, Object &Obj) { +static Error handleArgs(const CommonConfig &Config, Object &Obj) { // Perform the actual section removals. Obj.removeSections([&Config](const Section &Sec) { // Contrary to --only-keep-debug, --only-section fully removes sections that @@ -249,30 +249,11 @@ if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink)) return E; - if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || - Config.BuildIdLinkInput || Config.BuildIdLinkOutput || - !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() || - !Config.KeepSection.empty() || Config.NewSymbolVisibility || - !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || - !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || - !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || - !Config.SetSectionAlignment.empty() || Config.ExtractDWO || - Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO || - Config.StripNonAlloc || Config.StripSections || - Config.StripSwiftSymbols || Config.Weaken || - Config.DecompressDebugSections || - Config.DiscardMode == DiscardType::Locals || - !Config.SymbolsToAdd.empty() || Config.EntryExpr) { - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for COFF"); - } - return Error::success(); } -Error executeObjcopyOnBinary(const CopyConfig &Config, COFFObjectFile &In, - Buffer &Out) { +Error executeObjcopyOnBinary(const CommonConfig &Config, const COFFConfig &, + COFFObjectFile &In, raw_ostream &Out) { COFFReader Reader(In); Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create(); if (!ObjOrErr)
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h index 858759e..2c7ccd3 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h
@@ -11,18 +11,20 @@ namespace llvm { class Error; +class raw_ostream; namespace object { class COFFObjectFile; } // end namespace object namespace objcopy { -struct CopyConfig; -class Buffer; +struct CommonConfig; +struct COFFConfig; namespace coff { -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::COFFObjectFile &In, Buffer &Out); + +Error executeObjcopyOnBinary(const CommonConfig &Config, const COFFConfig &, + object::COFFObjectFile &In, raw_ostream &Out); } // end namespace coff } // end namespace objcopy
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Reader.h b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Reader.h index ec15369..48c050b 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Reader.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Reader.h
@@ -9,7 +9,6 @@ #ifndef LLVM_TOOLS_OBJCOPY_COFF_READER_H #define LLVM_TOOLS_OBJCOPY_COFF_READER_H -#include "Buffer.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Error.h"
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Writer.cpp index 6b56089..e7be64f 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Writer.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -12,6 +12,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include <cstddef> #include <cstdint> @@ -240,7 +241,7 @@ } void COFFWriter::writeHeaders(bool IsBigObj) { - uint8_t *Ptr = Buf.getBufferStart(); + uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()); if (Obj.IsPE) { memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader)); Ptr += sizeof(Obj.DosHeader); @@ -302,7 +303,8 @@ void COFFWriter::writeSections() { for (const auto &S : Obj.getSections()) { - uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData; + uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + + S.Header.PointerToRawData; ArrayRef<uint8_t> Contents = S.getContents(); std::copy(Contents.begin(), Contents.end(), Ptr); @@ -331,7 +333,8 @@ } template <class SymbolTy> void COFFWriter::writeSymbolStringTables() { - uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable; + uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + + Obj.CoffFileHeader.PointerToSymbolTable; for (const auto &S : Obj.getSymbols()) { // Convert symbols back to the right size, from coff_symbol32. copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr), @@ -366,8 +369,11 @@ if (Error E = finalize(IsBigObj)) return E; - if (Error E = Buf.allocate(FileSize)) - return E; + Buf = WritableMemoryBuffer::getNewMemBuffer(FileSize); + if (!Buf) + return createStringError(llvm::errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(FileSize) + " bytes."); writeHeaders(IsBigObj); writeSections(); @@ -380,7 +386,10 @@ if (Error E = patchDebugDirectory()) return E; - return Buf.commit(); + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); } Expected<uint32_t> COFFWriter::virtualAddressToFileAddress(uint32_t RVA) { @@ -412,7 +421,8 @@ "debug directory extends past end of section"); size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress; - uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset; + uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + + S.Header.PointerToRawData + Offset; uint8_t *End = Ptr + Dir->Size; while (Ptr < End) { debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr);
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Writer.h b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Writer.h index 3c0bdcb..eed43b3 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Writer.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/COFF/Writer.h
@@ -9,9 +9,9 @@ #ifndef LLVM_TOOLS_OBJCOPY_COFF_WRITER_H #define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H -#include "Buffer.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" #include <cstddef> #include <utility> @@ -23,7 +23,8 @@ class COFFWriter { Object &Obj; - Buffer &Buf; + std::unique_ptr<WritableMemoryBuffer> Buf; + raw_ostream &Out; size_t FileSize; size_t FileAlignment; @@ -51,8 +52,8 @@ virtual ~COFFWriter() {} Error write(); - COFFWriter(Object &Obj, Buffer &Buf) - : Obj(Obj), Buf(Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {} + COFFWriter(Object &Obj, raw_ostream &Out) + : Obj(Obj), Out(Out), StrTabBuilder(StringTableBuilder::WinCOFF) {} }; } // end namespace coff
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/CopyConfig.h b/src/llvm-project/llvm/tools/llvm-objcopy/CommonConfig.h similarity index 68% rename from src/llvm-project/llvm/tools/llvm-objcopy/CopyConfig.h rename to src/llvm-project/llvm/tools/llvm-objcopy/CommonConfig.h index 07eac9d..131ce5c 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/CommonConfig.h
@@ -1,4 +1,4 @@ -//===- CopyConfig.h -------------------------------------------------------===// +//===- CommonConfig.h -------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,20 +6,17 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H -#define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H -#include "ELF/ELFConfig.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/ELFTypes.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/Error.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/Regex.h" // Necessary for llvm::DebugCompressionType::None @@ -99,7 +96,7 @@ class NameOrPattern { StringRef Name; - // Regex is shared between multiple CopyConfig instances. + // Regex is shared between multiple CommonConfig instances. std::shared_ptr<Regex> R; std::shared_ptr<GlobPattern> G; bool IsPositiveMatch = true; @@ -117,6 +114,11 @@ llvm::function_ref<Error(Error)> ErrorCallback); bool isPositiveMatch() const { return IsPositiveMatch; } + Optional<StringRef> getName() const { + if (!R && !G) + return Name; + return None; + } bool operator==(StringRef S) const { return R ? R->match(S) : G ? G->match(S) : Name == S; } @@ -126,30 +128,66 @@ // Matcher that checks symbol or section names against the command line flags // provided for that option. class NameMatcher { - std::vector<NameOrPattern> PosMatchers; + DenseSet<CachedHashStringRef> PosNames; + std::vector<NameOrPattern> PosPatterns; std::vector<NameOrPattern> NegMatchers; public: Error addMatcher(Expected<NameOrPattern> Matcher) { if (!Matcher) return Matcher.takeError(); - if (Matcher->isPositiveMatch()) - PosMatchers.push_back(std::move(*Matcher)); - else + if (Matcher->isPositiveMatch()) { + if (Optional<StringRef> MaybeName = Matcher->getName()) + PosNames.insert(CachedHashStringRef(*MaybeName)); + else + PosPatterns.push_back(std::move(*Matcher)); + } else { NegMatchers.push_back(std::move(*Matcher)); + } return Error::success(); } bool matches(StringRef S) const { - return is_contained(PosMatchers, S) && !is_contained(NegMatchers, S); + return (PosNames.contains(CachedHashStringRef(S)) || + is_contained(PosPatterns, S)) && + !is_contained(NegMatchers, S); } - bool empty() const { return PosMatchers.empty() && NegMatchers.empty(); } + bool empty() const { + return PosNames.empty() && PosPatterns.empty() && NegMatchers.empty(); + } +}; + +enum class SymbolFlag { + Global, + Local, + Weak, + Default, + Hidden, + Protected, + File, + Section, + Object, + Function, + IndirectFunction, + Debug, + Constructor, + Warning, + Indirect, + Synthetic, + UniqueObject, +}; + +// Symbol info specified by --add-symbol option. Symbol flags not supported +// by a concrete format should be ignored. +struct NewSymbolInfo { + StringRef SymbolName; + StringRef SectionName; + uint64_t Value = 0; + std::vector<SymbolFlag> Flags; + std::vector<StringRef> BeforeSyms; }; // Configuration for copying/stripping a single file. -struct CopyConfig { - // Format-specific options to be initialized lazily when needed. - Optional<elf::ELFCopyConfig> ELF; - +struct CommonConfig { // Main input/output options StringRef InputFilename; FileFormat InputFormat = FileFormat::Unspecified; @@ -163,20 +201,15 @@ StringRef AddGnuDebugLink; // Cached gnu_debuglink's target CRC uint32_t GnuDebugLinkCRC32; - StringRef BuildIdLinkDir; - Optional<StringRef> BuildIdLinkInput; - Optional<StringRef> BuildIdLinkOutput; Optional<StringRef> ExtractPartition; StringRef SplitDWO; StringRef SymbolsPrefix; StringRef AllocSectionsPrefix; DiscardType DiscardMode = DiscardType::None; - Optional<StringRef> NewSymbolVisibility; // Repeated options std::vector<StringRef> AddSection; std::vector<StringRef> DumpSection; - std::vector<StringRef> SymbolsToAdd; std::vector<StringRef> RPathToAdd; std::vector<StringRef> RPathToPrepend; DenseMap<StringRef, StringRef> RPathsToUpdate; @@ -212,12 +245,16 @@ // --change-start is used. std::function<uint64_t(uint64_t)> EntryExpr; + // Symbol info specified by --add-symbol option. + std::vector<NewSymbolInfo> SymbolsToAdd; + // Boolean options bool AllowBrokenLinks = false; bool DeterministicArchives = true; bool ExtractDWO = false; bool ExtractMainPartition = false; bool KeepFileSymbols = false; + bool KeepUndefined = false; bool LocalizeHidden = false; bool OnlyKeepDebug = false; bool PreserveDates = false; @@ -235,55 +272,9 @@ bool RemoveAllRpaths = false; DebugCompressionType CompressionType = DebugCompressionType::None; - - // parseELFConfig performs ELF-specific command-line parsing. Fills `ELF` on - // success or returns an Error otherwise. - Error parseELFConfig() { - if (!ELF) { - Expected<elf::ELFCopyConfig> ELFConfig = elf::parseConfig(*this); - if (!ELFConfig) - return ELFConfig.takeError(); - ELF = *ELFConfig; - } - return Error::success(); - } }; -// Configuration for the overall invocation of this tool. When invoked as -// objcopy, will always contain exactly one CopyConfig. When invoked as strip, -// will contain one or more CopyConfigs. -struct DriverConfig { - SmallVector<CopyConfig, 1> CopyConfigs; - BumpPtrAllocator Alloc; -}; - -// ParseObjcopyOptions returns the config and sets the input arguments. If a -// help flag is set then ParseObjcopyOptions will print the help messege and -// exit. ErrorCallback is used to handle recoverable errors. An Error returned -// by the callback aborts the parsing and is then returned by this function. -Expected<DriverConfig> -parseObjcopyOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback); - -// ParseInstallNameToolOptions returns the config and sets the input arguments. -// If a help flag is set then ParseInstallNameToolOptions will print the help -// messege and exit. -Expected<DriverConfig> -parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr); - -// ParseBitcodeStripOptions returns the config and sets the input arguments. -// If a help flag is set then ParseBitcodeStripOptions will print the help -// messege and exit. -Expected<DriverConfig> parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr); - -// ParseStripOptions returns the config and sets the input arguments. If a -// help flag is set then ParseStripOptions will print the help messege and -// exit. ErrorCallback is used to handle recoverable errors. An Error returned -// by the callback aborts the parsing and is then returned by this function. -Expected<DriverConfig> -parseStripOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback); } // namespace objcopy } // namespace llvm -#endif +#endif // LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/CommonOpts.td b/src/llvm-project/llvm/tools/llvm-objcopy/CommonOpts.td index 6481d1d..4222532 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/CommonOpts.td +++ b/src/llvm-project/llvm/tools/llvm-objcopy/CommonOpts.td
@@ -84,6 +84,9 @@ def keep_file_symbols : Flag<["--"], "keep-file-symbols">, HelpText<"Do not remove file symbols">; +def keep_undefined : Flag<["--"], "keep-undefined">, + HelpText<"Do not remove undefined symbols">; + def only_keep_debug : Flag<["--"], "only-keep-debug">, HelpText< @@ -99,7 +102,8 @@ def discard_all : Flag<["--"], "discard-all">, - HelpText<"Remove all local symbols except file and section symbols">; + HelpText<"Remove all local symbols except file and section symbols. Also " + "remove all debug sections">; def x : Flag<["-"], "x">, Alias<discard_all>, HelpText<"Alias for --discard-all">;
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/CopyConfig.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/ConfigManager.cpp similarity index 79% rename from src/llvm-project/llvm/tools/llvm-objcopy/CopyConfig.cpp rename to src/llvm-project/llvm/tools/llvm-objcopy/ConfigManager.cpp index ba74759..9f7d06b 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/ConfigManager.cpp
@@ -1,4 +1,4 @@ -//===- CopyConfig.cpp -----------------------------------------------------===// +//===- ConfigManager.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "CopyConfig.h" - +#include "ConfigManager.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -18,12 +17,13 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/StringSaver.h" #include <memory> -namespace llvm { -namespace objcopy { +using namespace llvm; +using namespace llvm::objcopy; namespace { enum ObjcopyID { @@ -60,7 +60,9 @@ class ObjcopyOptTable : public opt::OptTable { public: - ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {} + ObjcopyOptTable() : OptTable(ObjcopyInfoTable) { + setGroupedShortOptions(true); + } }; enum InstallNameToolID { @@ -164,7 +166,7 @@ class StripOptTable : public opt::OptTable { public: - StripOptTable() : OptTable(StripInfoTable) {} + StripOptTable() : OptTable(StripInfoTable) { setGroupedShortOptions(true); } }; } // namespace @@ -244,9 +246,10 @@ "bad format for --set-section-alignment: missing section name"); uint64_t NewAlign; if (Split.second.getAsInteger(0, NewAlign)) - return createStringError(errc::invalid_argument, - "invalid alignment for --set-section-alignment: '%s'", - Split.second.str().c_str()); + return createStringError( + errc::invalid_argument, + "invalid alignment for --set-section-alignment: '%s'", + Split.second.str().c_str()); return std::make_pair(Split.first, NewAlign); } @@ -272,10 +275,12 @@ return SFU; } +namespace { struct TargetInfo { FileFormat Format; MachineInfo Machine; }; +} // namespace // FIXME: consolidate with the bfd parsing used by lld. static const StringMap<MachineInfo> TargetMap{ @@ -337,10 +342,9 @@ return {TargetInfo{Format, MI}}; } -static Error -addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, - StringRef Filename, MatchStyle MS, - llvm::function_ref<Error(Error)> ErrorCallback) { +static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, + StringRef Filename, MatchStyle MS, + function_ref<Error(Error)> ErrorCallback) { StringSaver Saver(Alloc); SmallVector<StringRef, 16> Lines; auto BufOrErr = MemoryBuffer::getFile(Filename); @@ -363,7 +367,7 @@ Expected<NameOrPattern> NameOrPattern::create(StringRef Pattern, MatchStyle MS, - llvm::function_ref<Error(Error)> ErrorCallback) { + function_ref<Error(Error)> ErrorCallback) { switch (MS) { case MatchStyle::Literal: return NameOrPattern(Pattern); @@ -457,7 +461,7 @@ HelpText = " [options] input"; break; } - OptTable.PrintHelp(OS, (ToolName + HelpText).str().c_str(), + OptTable.printHelp(OS, (ToolName + HelpText).str().c_str(), (ToolName + " tool").str().c_str()); // TODO: Replace this with libOption call once it adds extrahelp support. // The CommandLine library has a cl::extrahelp class to support this, @@ -465,19 +469,186 @@ OS << "\nPass @FILE as argument to read options from FILE.\n"; } +static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) { + // Parse value given with --add-symbol option and create the + // new symbol if possible. The value format for --add-symbol is: + // + // <name>=[<section>:]<value>[,<flags>] + // + // where: + // <name> - symbol name, can be empty string + // <section> - optional section name. If not given ABS symbol is created + // <value> - symbol value, can be decimal or hexadecimal number prefixed + // with 0x. + // <flags> - optional flags affecting symbol type, binding or visibility. + NewSymbolInfo SI; + StringRef Value; + std::tie(SI.SymbolName, Value) = FlagValue.split('='); + if (Value.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-symbol, missing '=' after '%s'", + SI.SymbolName.str().c_str()); + + if (Value.contains(':')) { + std::tie(SI.SectionName, Value) = Value.split(':'); + if (SI.SectionName.empty() || Value.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-symbol, missing section name or symbol value"); + } + + SmallVector<StringRef, 6> Flags; + Value.split(Flags, ','); + if (Flags[0].getAsInteger(0, SI.Value)) + return createStringError(errc::invalid_argument, "bad symbol value: '%s'", + Flags[0].str().c_str()); + + using Functor = std::function<void()>; + SmallVector<StringRef, 6> UnsupportedFlags; + for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) + static_cast<Functor>( + StringSwitch<Functor>(Flags[I]) + .CaseLower("global", + [&] { SI.Flags.push_back(SymbolFlag::Global); }) + .CaseLower("local", [&] { SI.Flags.push_back(SymbolFlag::Local); }) + .CaseLower("weak", [&] { SI.Flags.push_back(SymbolFlag::Weak); }) + .CaseLower("default", + [&] { SI.Flags.push_back(SymbolFlag::Default); }) + .CaseLower("hidden", + [&] { SI.Flags.push_back(SymbolFlag::Hidden); }) + .CaseLower("protected", + [&] { SI.Flags.push_back(SymbolFlag::Protected); }) + .CaseLower("file", [&] { SI.Flags.push_back(SymbolFlag::File); }) + .CaseLower("section", + [&] { SI.Flags.push_back(SymbolFlag::Section); }) + .CaseLower("object", + [&] { SI.Flags.push_back(SymbolFlag::Object); }) + .CaseLower("function", + [&] { SI.Flags.push_back(SymbolFlag::Function); }) + .CaseLower( + "indirect-function", + [&] { SI.Flags.push_back(SymbolFlag::IndirectFunction); }) + .CaseLower("debug", [&] { SI.Flags.push_back(SymbolFlag::Debug); }) + .CaseLower("constructor", + [&] { SI.Flags.push_back(SymbolFlag::Constructor); }) + .CaseLower("warning", + [&] { SI.Flags.push_back(SymbolFlag::Warning); }) + .CaseLower("indirect", + [&] { SI.Flags.push_back(SymbolFlag::Indirect); }) + .CaseLower("synthetic", + [&] { SI.Flags.push_back(SymbolFlag::Synthetic); }) + .CaseLower("unique-object", + [&] { SI.Flags.push_back(SymbolFlag::UniqueObject); }) + .StartsWithLower("before=", + [&] { + StringRef SymNamePart = + Flags[I].split('=').second; + + if (!SymNamePart.empty()) + SI.BeforeSyms.push_back(SymNamePart); + }) + .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))(); + if (!UnsupportedFlags.empty()) + return createStringError(errc::invalid_argument, + "unsupported flag%s for --add-symbol: '%s'", + UnsupportedFlags.size() > 1 ? "s" : "", + join(UnsupportedFlags, "', '").c_str()); + + return SI; +} + +Expected<const ELFConfig &> ConfigManager::getELFConfig() const { + if (Common.StripSwiftSymbols || Common.KeepUndefined) + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for ELF"); + + return ELF; +} + +Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const { + if (Common.AllowBrokenLinks || !Common.SplitDWO.empty() || + !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() || + !Common.DumpSection.empty() || !Common.KeepSection.empty() || + ELF.NewSymbolVisibility || !Common.SymbolsToGlobalize.empty() || + !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() || + !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || + !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || + Common.ExtractDWO || Common.LocalizeHidden || Common.PreserveDates || + Common.StripDWO || Common.StripNonAlloc || Common.StripSections || + Common.StripSwiftSymbols || Common.KeepUndefined || Common.Weaken || + Common.DecompressDebugSections || + Common.DiscardMode == DiscardType::Locals || + !Common.SymbolsToAdd.empty() || Common.EntryExpr) { + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for COFF"); + } + + return COFF; +} + +Expected<const MachOConfig &> ConfigManager::getMachOConfig() const { + if (Common.AllowBrokenLinks || !Common.SplitDWO.empty() || + !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() || + !Common.KeepSection.empty() || ELF.NewSymbolVisibility || + !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() || + !Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() || + !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || + !Common.UnneededSymbolsToRemove.empty() || + !Common.SetSectionAlignment.empty() || !Common.SetSectionFlags.empty() || + Common.ExtractDWO || Common.LocalizeHidden || Common.PreserveDates || + Common.StripAllGNU || Common.StripDWO || Common.StripNonAlloc || + Common.StripSections || Common.Weaken || Common.DecompressDebugSections || + Common.StripUnneeded || Common.DiscardMode == DiscardType::Locals || + !Common.SymbolsToAdd.empty() || Common.EntryExpr) { + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for MachO"); + } + + return MachO; +} + +Expected<const WasmConfig &> ConfigManager::getWasmConfig() const { + if (!Common.AddGnuDebugLink.empty() || Common.ExtractPartition || + !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() || + !Common.AllocSectionsPrefix.empty() || + Common.DiscardMode != DiscardType::None || ELF.NewSymbolVisibility || + !Common.SymbolsToAdd.empty() || !Common.RPathToAdd.empty() || + !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToLocalize.empty() || + !Common.SymbolsToKeep.empty() || !Common.SymbolsToRemove.empty() || + !Common.UnneededSymbolsToRemove.empty() || + !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || + !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || + !Common.SetSectionFlags.empty() || !Common.SymbolsToRename.empty()) { + return createStringError( + llvm::errc::invalid_argument, + "only flags for section dumping, removal, and addition are supported"); + } + + return Wasm; +} + // ParseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then ParseObjcopyOptions will print the help messege and // exit. Expected<DriverConfig> -parseObjcopyOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback) { +objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr, + function_ref<Error(Error)> ErrorCallback) { DriverConfig DC; ObjcopyOptTable T; + + const char *const *DashDash = + std::find_if(RawArgsArr.begin(), RawArgsArr.end(), + [](StringRef Str) { return Str == "--"; }); + ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash); + if (DashDash != RawArgsArr.end()) + DashDash = std::next(DashDash); + unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - if (InputArgs.size() == 0) { + if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) { printHelp(T, errs(), ToolType::Objcopy); exit(1); } @@ -501,6 +672,7 @@ for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT)) Positional.push_back(Arg->getValue()); + std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional)); if (Positional.empty()) return createStringError(errc::invalid_argument, "no input file specified"); @@ -509,7 +681,9 @@ return createStringError(errc::invalid_argument, "too many positional arguments"); - CopyConfig Config; + ConfigManager ConfigMgr; + CommonConfig &Config = ConfigMgr.Common; + ELFConfig &ELFConfig = ConfigMgr.ELF; Config.InputFilename = Positional[0]; Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1]; if (InputArgs.hasArg(OBJCOPY_target) && @@ -548,10 +722,24 @@ .Case("ihex", FileFormat::IHex) .Default(FileFormat::Unspecified); - if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) - Config.NewSymbolVisibility = + if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) { + const uint8_t Invalid = 0xff; + StringRef VisibilityStr = InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility); + ELFConfig.NewSymbolVisibility = StringSwitch<uint8_t>(VisibilityStr) + .Case("default", ELF::STV_DEFAULT) + .Case("hidden", ELF::STV_HIDDEN) + .Case("internal", ELF::STV_INTERNAL) + .Case("protected", ELF::STV_PROTECTED) + .Default(Invalid); + + if (ELFConfig.NewSymbolVisibility == Invalid) + return createStringError(errc::invalid_argument, + "'%s' is not a valid symbol visibility", + VisibilityStr.str().c_str()); + } + Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat) .Case("binary", FileFormat::Binary) .Case("ihex", FileFormat::IHex) @@ -607,13 +795,6 @@ Config.GnuDebugLinkCRC32 = llvm::crc32(arrayRefFromStringRef(Debug->getBuffer())); } - Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); - if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) - Config.BuildIdLinkInput = - InputArgs.getLastArgValue(OBJCOPY_build_id_link_input); - if (InputArgs.hasArg(OBJCOPY_build_id_link_output)) - Config.BuildIdLinkOutput = - InputArgs.getLastArgValue(OBJCOPY_build_id_link_output); Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols); Config.AllocSectionsPrefix = @@ -706,8 +887,14 @@ "bad format for --add-section: missing file name"); Config.AddSection.push_back(ArgValue); } - for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) - Config.DumpSection.push_back(Arg->getValue()); + for (auto *Arg : InputArgs.filtered(OBJCOPY_dump_section)) { + StringRef Value(Arg->getValue()); + if (Value.split('=').second.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --dump-section, expected section=file"); + Config.DumpSection.push_back(Value); + } Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu); Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug); @@ -727,6 +914,7 @@ : DiscardType::Locals; Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); + Config.KeepUndefined = InputArgs.hasArg(OBJCOPY_keep_undefined); Config.DecompressDebugSections = InputArgs.hasArg(OBJCOPY_decompress_debug_sections); if (Config.DiscardMode == DiscardType::All) { @@ -797,8 +985,13 @@ addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) - Config.SymbolsToAdd.push_back(Arg->getValue()); + for (auto *Arg : InputArgs.filtered(OBJCOPY_add_symbol)) { + Expected<NewSymbolInfo> SymInfo = parseNewSymbolInfo(Arg->getValue()); + if (!SymInfo) + return SymInfo.takeError(); + + Config.SymbolsToAdd.push_back(*SymInfo); + } Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); @@ -852,7 +1045,7 @@ "cannot specify --extract-partition together with " "--extract-main-partition"); - DC.CopyConfigs.push_back(std::move(Config)); + DC.CopyConfigs.push_back(std::move(ConfigMgr)); return std::move(DC); } @@ -860,9 +1053,10 @@ // If a help flag is set then ParseInstallNameToolOptions will print the help // messege and exit. Expected<DriverConfig> -parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { +objcopy::parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { DriverConfig DC; - CopyConfig Config; + ConfigManager ConfigMgr; + CommonConfig &Config = ConfigMgr.Common; InstallNameToolOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = @@ -986,14 +1180,15 @@ Config.InputFilename = Positional[0]; Config.OutputFilename = Positional[0]; - DC.CopyConfigs.push_back(std::move(Config)); + DC.CopyConfigs.push_back(std::move(ConfigMgr)); return std::move(DC); } Expected<DriverConfig> -parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) { +objcopy::parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) { DriverConfig DC; - CopyConfig Config; + ConfigManager ConfigMgr; + CommonConfig &Config = ConfigMgr.Common; BitcodeStripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; opt::InputArgList InputArgs = @@ -1030,7 +1225,7 @@ Config.InputFilename = Positional[0]; Config.OutputFilename = Positional[0]; - DC.CopyConfigs.push_back(std::move(Config)); + DC.CopyConfigs.push_back(std::move(ConfigMgr)); return std::move(DC); } @@ -1038,14 +1233,21 @@ // help flag is set then ParseStripOptions will print the help messege and // exit. Expected<DriverConfig> -parseStripOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback) { +objcopy::parseStripOptions(ArrayRef<const char *> RawArgsArr, + function_ref<Error(Error)> ErrorCallback) { + const char *const *DashDash = + std::find_if(RawArgsArr.begin(), RawArgsArr.end(), + [](StringRef Str) { return Str == "--"; }); + ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash); + if (DashDash != RawArgsArr.end()) + DashDash = std::next(DashDash); + StripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - if (InputArgs.size() == 0) { + if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) { printHelp(T, errs(), ToolType::Strip); exit(1); } @@ -1067,6 +1269,7 @@ Arg->getAsString(InputArgs).c_str()); for (auto Arg : InputArgs.filtered(STRIP_INPUT)) Positional.push_back(Arg->getValue()); + std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional)); if (Positional.empty()) return createStringError(errc::invalid_argument, "no input file specified"); @@ -1076,7 +1279,8 @@ errc::invalid_argument, "multiple input files cannot be used in combination with -o"); - CopyConfig Config; + ConfigManager ConfigMgr; + CommonConfig &Config = ConfigMgr.Common; if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard)) return createStringError(errc::invalid_argument, @@ -1104,6 +1308,7 @@ Config.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols); Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug); Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); + Config.KeepUndefined = InputArgs.hasArg(STRIP_keep_undefined); for (auto Arg : InputArgs.filtered(STRIP_keep_section)) if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( @@ -1148,7 +1353,7 @@ Config.InputFilename = Positional[0]; Config.OutputFilename = InputArgs.getLastArgValue(STRIP_output, Positional[0]); - DC.CopyConfigs.push_back(std::move(Config)); + DC.CopyConfigs.push_back(std::move(ConfigMgr)); } else { StringMap<unsigned> InputFiles; for (StringRef Filename : Positional) { @@ -1164,7 +1369,7 @@ } Config.InputFilename = Filename; Config.OutputFilename = Filename; - DC.CopyConfigs.push_back(Config); + DC.CopyConfigs.push_back(ConfigMgr); } } @@ -1175,6 +1380,3 @@ return std::move(DC); } - -} // namespace objcopy -} // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/ConfigManager.h b/src/llvm-project/llvm/tools/llvm-objcopy/ConfigManager.h new file mode 100644 index 0000000..c0d0e8b --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objcopy/ConfigManager.h
@@ -0,0 +1,80 @@ +//===- ConfigManager.h ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H +#define LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H + +#include "COFF/COFFConfig.h" +#include "CommonConfig.h" +#include "ELF/ELFConfig.h" +#include "MachO/MachOConfig.h" +#include "MultiFormatConfig.h" +#include "wasm/WasmConfig.h" +#include "llvm/Support/Allocator.h" +#include <vector> + +namespace llvm { +namespace objcopy { + +// ConfigManager keeps all configurations and prepare +// format-specific options. +struct ConfigManager : public MultiFormatConfig { + virtual ~ConfigManager() {} + + const CommonConfig &getCommonConfig() const override { return Common; } + Expected<const ELFConfig &> getELFConfig() const override; + Expected<const COFFConfig &> getCOFFConfig() const override; + Expected<const MachOConfig &> getMachOConfig() const override; + Expected<const WasmConfig &> getWasmConfig() const override; + + // All configs. + CommonConfig Common; + ELFConfig ELF; + COFFConfig COFF; + MachOConfig MachO; + WasmConfig Wasm; +}; + +// Configuration for the overall invocation of this tool. When invoked as +// objcopy, will always contain exactly one CopyConfig. When invoked as strip, +// will contain one or more CopyConfigs. +struct DriverConfig { + SmallVector<ConfigManager, 1> CopyConfigs; + BumpPtrAllocator Alloc; +}; + +// ParseObjcopyOptions returns the config and sets the input arguments. If a +// help flag is set then ParseObjcopyOptions will print the help messege and +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseObjcopyOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); + +// ParseInstallNameToolOptions returns the config and sets the input arguments. +// If a help flag is set then ParseInstallNameToolOptions will print the help +// messege and exit. +Expected<DriverConfig> +parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr); + +// ParseBitcodeStripOptions returns the config and sets the input arguments. +// If a help flag is set then ParseBitcodeStripOptions will print the help +// messege and exit. +Expected<DriverConfig> parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr); + +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseStripOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp deleted file mode 100644 index 4099376..0000000 --- a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp +++ /dev/null
@@ -1,133 +0,0 @@ -//===- ELFConfig.cpp ------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "CopyConfig.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Error.h" - -namespace llvm { -namespace objcopy { -namespace elf { - -static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue, - uint8_t DefaultVisibility) { - // Parse value given with --add-symbol option and create the - // new symbol if possible. The value format for --add-symbol is: - // - // <name>=[<section>:]<value>[,<flags>] - // - // where: - // <name> - symbol name, can be empty string - // <section> - optional section name. If not given ABS symbol is created - // <value> - symbol value, can be decimal or hexadecimal number prefixed - // with 0x. - // <flags> - optional flags affecting symbol type, binding or visibility: - // The following are currently supported: - // - // global, local, weak, default, hidden, file, section, object, - // indirect-function. - // - // The following flags are ignored and provided for GNU - // compatibility only: - // - // warning, debug, constructor, indirect, synthetic, - // unique-object, before=<symbol>. - NewSymbolInfo SI; - StringRef Value; - std::tie(SI.SymbolName, Value) = FlagValue.split('='); - if (Value.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --add-symbol, missing '=' after '%s'", - SI.SymbolName.str().c_str()); - - if (Value.contains(':')) { - std::tie(SI.SectionName, Value) = Value.split(':'); - if (SI.SectionName.empty() || Value.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --add-symbol, missing section name or symbol value"); - } - - SmallVector<StringRef, 6> Flags; - Value.split(Flags, ','); - if (Flags[0].getAsInteger(0, SI.Value)) - return createStringError(errc::invalid_argument, "bad symbol value: '%s'", - Flags[0].str().c_str()); - - SI.Visibility = DefaultVisibility; - - using Functor = std::function<void(void)>; - SmallVector<StringRef, 6> UnsupportedFlags; - for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) - static_cast<Functor>( - StringSwitch<Functor>(Flags[I]) - .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; }) - .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; }) - .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; }) - .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; }) - .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; }) - .CaseLower("protected", - [&SI] { SI.Visibility = ELF::STV_PROTECTED; }) - .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; }) - .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; }) - .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; }) - .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; }) - .CaseLower("indirect-function", - [&SI] { SI.Type = ELF::STT_GNU_IFUNC; }) - .CaseLower("debug", [] {}) - .CaseLower("constructor", [] {}) - .CaseLower("warning", [] {}) - .CaseLower("indirect", [] {}) - .CaseLower("synthetic", [] {}) - .CaseLower("unique-object", [] {}) - .StartsWithLower("before", [] {}) - .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))(); - if (!UnsupportedFlags.empty()) - return createStringError(errc::invalid_argument, - "unsupported flag%s for --add-symbol: '%s'", - UnsupportedFlags.size() > 1 ? "s" : "", - join(UnsupportedFlags, "', '").c_str()); - return SI; -} - -Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config) { - ELFCopyConfig ELFConfig; - if (Config.NewSymbolVisibility) { - const uint8_t Invalid = 0xff; - ELFConfig.NewSymbolVisibility = - StringSwitch<uint8_t>(*Config.NewSymbolVisibility) - .Case("default", ELF::STV_DEFAULT) - .Case("hidden", ELF::STV_HIDDEN) - .Case("internal", ELF::STV_INTERNAL) - .Case("protected", ELF::STV_PROTECTED) - .Default(Invalid); - - if (ELFConfig.NewSymbolVisibility == Invalid) - return createStringError(errc::invalid_argument, - "'%s' is not a valid symbol visibility", - Config.NewSymbolVisibility->str().c_str()); - } - - for (StringRef Arg : Config.SymbolsToAdd) { - Expected<elf::NewSymbolInfo> NSI = parseNewSymbolInfo( - Arg, - ELFConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT)); - if (!NSI) - return NSI.takeError(); - ELFConfig.SymbolsToAdd.push_back(*NSI); - } - - return ELFConfig; -} - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFConfig.h b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFConfig.h index 977efbc..42d407d 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFConfig.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFConfig.h
@@ -6,39 +6,23 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_OBJCOPY_ELFCONFIG_H -#define LLVM_TOOLS_OBJCOPY_ELFCONFIG_H +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/ELFTypes.h" -#include "llvm/Support/Error.h" #include <vector> namespace llvm { namespace objcopy { -struct CopyConfig; -namespace elf { - -struct NewSymbolInfo { - StringRef SymbolName; - StringRef SectionName; - uint64_t Value = 0; - uint8_t Type = ELF::STT_NOTYPE; - uint8_t Bind = ELF::STB_GLOBAL; - uint8_t Visibility = ELF::STV_DEFAULT; +// ELF specific configuration for copying/stripping a single file. +struct ELFConfig { + uint8_t NewSymbolVisibility = (uint8_t)ELF::STV_DEFAULT; }; -struct ELFCopyConfig { - Optional<uint8_t> NewSymbolVisibility; - std::vector<NewSymbolInfo> SymbolsToAdd; -}; - -Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config); - -} // namespace elf } // namespace objcopy } // namespace llvm -#endif +#endif // LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp index c53a34b..986eeca 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -7,9 +7,10 @@ //===----------------------------------------------------------------------===// #include "ELFObjcopy.h" -#include "Buffer.h" -#include "CopyConfig.h" +#include "CommonConfig.h" +#include "ELFConfig.h" #include "Object.h" +#include "llvm-objcopy.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" @@ -44,12 +45,12 @@ #include <system_error> #include <utility> -namespace llvm { -namespace objcopy { -namespace elf { +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::objcopy; +using namespace llvm::objcopy::elf; +using namespace llvm::object; -using namespace object; -using namespace ELF; using SectionPred = std::function<bool(const SectionBase &Sec)>; static bool isDebugSection(const SectionBase &Sec) { @@ -70,7 +71,7 @@ return !isDWOSection(Sec); } -uint64_t getNewShfFlags(SectionFlag AllFlags) { +static uint64_t getNewShfFlags(SectionFlag AllFlags) { uint64_t NewFlags = 0; if (AllFlags & SectionFlag::SecAlloc) NewFlags |= ELF::SHF_ALLOC; @@ -132,77 +133,40 @@ return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; } -static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, +static std::unique_ptr<Writer> createELFWriter(const CommonConfig &Config, + Object &Obj, raw_ostream &Out, ElfType OutputElfType) { // Depending on the initial ELFT and OutputFormat we need a different Writer. switch (OutputElfType) { case ELFT_ELF32LE: - return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, !Config.StripSections, + return std::make_unique<ELFWriter<ELF32LE>>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); case ELFT_ELF64LE: - return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, !Config.StripSections, + return std::make_unique<ELFWriter<ELF64LE>>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); case ELFT_ELF32BE: - return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, !Config.StripSections, + return std::make_unique<ELFWriter<ELF32BE>>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); case ELFT_ELF64BE: - return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, !Config.StripSections, + return std::make_unique<ELFWriter<ELF64BE>>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); } llvm_unreachable("Invalid output format"); } -static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, +static std::unique_ptr<Writer> createWriter(const CommonConfig &Config, + Object &Obj, raw_ostream &Out, ElfType OutputElfType) { switch (Config.OutputFormat) { case FileFormat::Binary: - return std::make_unique<BinaryWriter>(Obj, Buf); + return std::make_unique<BinaryWriter>(Obj, Out); case FileFormat::IHex: - return std::make_unique<IHexWriter>(Obj, Buf); + return std::make_unique<IHexWriter>(Obj, Out); default: - return createELFWriter(Config, Obj, Buf, OutputElfType); + return createELFWriter(Config, Obj, Out, OutputElfType); } } -template <class ELFT> -static Expected<ArrayRef<uint8_t>> -findBuildID(const CopyConfig &Config, const object::ELFFile<ELFT> &In) { - auto PhdrsOrErr = In.program_headers(); - if (auto Err = PhdrsOrErr.takeError()) - return createFileError(Config.InputFilename, std::move(Err)); - - for (const auto &Phdr : *PhdrsOrErr) { - if (Phdr.p_type != PT_NOTE) - continue; - Error Err = Error::success(); - for (auto Note : In.notes(Phdr, Err)) - if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU) - return Note.getDesc(); - if (Err) - return createFileError(Config.InputFilename, std::move(Err)); - } - - return createFileError(Config.InputFilename, - createStringError(llvm::errc::invalid_argument, - "could not find build ID")); -} - -static Expected<ArrayRef<uint8_t>> -findBuildID(const CopyConfig &Config, const object::ELFObjectFileBase &In) { - if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In)) - return findBuildID(Config, O->getELFFile()); - else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In)) - return findBuildID(Config, O->getELFFile()); - else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In)) - return findBuildID(Config, O->getELFFile()); - else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In)) - return findBuildID(Config, O->getELFFile()); - - llvm_unreachable("Bad file format"); -} - template <class... Ts> static Error makeStringError(std::error_code EC, const Twine &Msg, Ts &&... Args) { @@ -210,83 +174,6 @@ return createStringError(EC, FullMsg.c_str(), std::forward<Ts>(Args)...); } -#define MODEL_8 "%%%%%%%%" -#define MODEL_16 MODEL_8 MODEL_8 -#define MODEL_32 (MODEL_16 MODEL_16) - -static Error linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink, - StringRef Suffix, - ArrayRef<uint8_t> BuildIdBytes) { - SmallString<128> Path = Config.BuildIdLinkDir; - sys::path::append(Path, llvm::toHex(BuildIdBytes[0], /*LowerCase*/ true)); - if (auto EC = sys::fs::create_directories(Path)) - return createFileError( - Path.str(), - makeStringError(EC, "cannot create build ID link directory")); - - sys::path::append(Path, - llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true)); - Path += Suffix; - SmallString<128> TmpPath; - // create_hard_link races so we need to link to a temporary path but - // we want to make sure that we choose a filename that does not exist. - // By using 32 model characters we get 128-bits of entropy. It is - // unlikely that this string has ever existed before much less exists - // on this disk or in the current working directory. - // Additionally we prepend the original Path for debugging but also - // because it ensures that we're linking within a directory on the same - // partition on the same device which is critical. It has the added - // win of yet further decreasing the odds of a conflict. - sys::fs::createUniquePath(Twine(Path) + "-" + MODEL_32 + ".tmp", TmpPath, - /*MakeAbsolute*/ false); - if (auto EC = sys::fs::create_hard_link(ToLink, TmpPath)) { - Path.push_back('\0'); - return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), - Path.data()); - } - // We then atomically rename the link into place which will just move the - // link. If rename fails something is more seriously wrong so just return - // an error. - if (auto EC = sys::fs::rename(TmpPath, Path)) { - Path.push_back('\0'); - return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), - Path.data()); - } - // If `Path` was already a hard-link to the same underlying file then the - // temp file will be left so we need to remove it. Remove will not cause - // an error by default if the file is already gone so just blindly remove - // it rather than checking. - if (auto EC = sys::fs::remove(TmpPath)) { - TmpPath.push_back('\0'); - return makeStringError(EC, "could not remove '%s'", TmpPath.data()); - } - return Error::success(); -} - -static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader, - StringRef File, ElfType OutputElfType) { - Expected<std::unique_ptr<Object>> DWOFile = Reader.create(false); - if (!DWOFile) - return DWOFile.takeError(); - - auto OnlyKeepDWOPred = [&DWOFile](const SectionBase &Sec) { - return onlyKeepDWOPred(**DWOFile, Sec); - }; - if (Error E = - (*DWOFile)->removeSections(Config.AllowBrokenLinks, OnlyKeepDWOPred)) - return E; - if (Config.OutputArch) { - (*DWOFile)->Machine = Config.OutputArch.getValue().EMachine; - (*DWOFile)->OSABI = Config.OutputArch.getValue().OSABI; - } - FileBuffer FB(File); - std::unique_ptr<Writer> Writer = - createWriter(Config, **DWOFile, FB, OutputElfType); - if (Error E = Writer->finalize()) - return E; - return Writer->write(); -} - static Error dumpSectionToFile(StringRef SecName, StringRef Filename, Object &Obj) { for (auto &Sec : Obj.sections()) { @@ -357,7 +244,7 @@ Sym.Type != STT_SECTION; } -static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { +static Error updateAndRemoveSymbols(const CommonConfig &Config, Object &Obj) { // TODO: update or remove symbols only if there is an option that affects // them. if (!Obj.SymbolTable) @@ -452,7 +339,7 @@ return Obj.removeSymbols(RemoveSymbolsPred); } -static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { +static Error replaceAndRemoveSections(const CommonConfig &Config, Object &Obj) { SectionPred RemovePred = [](const SectionBase &) { return false; }; // Removes: @@ -462,7 +349,7 @@ }; } - if (Config.StripDWO || !Config.SplitDWO.empty()) + if (Config.StripDWO) RemovePred = [RemovePred](const SectionBase &Sec) { return isDWOSection(Sec) || RemovePred(Sec); }; @@ -613,6 +500,60 @@ return Obj.removeSections(Config.AllowBrokenLinks, RemovePred); } +// Add symbol to the Object symbol table with the specified properties. +static void addSymbol(Object &Obj, const NewSymbolInfo &SymInfo, + uint8_t DefaultVisibility) { + SectionBase *Sec = Obj.findSection(SymInfo.SectionName); + uint64_t Value = Sec ? Sec->Addr + SymInfo.Value : SymInfo.Value; + + uint8_t Bind = ELF::STB_GLOBAL; + uint8_t Type = ELF::STT_NOTYPE; + uint8_t Visibility = DefaultVisibility; + + for (SymbolFlag FlagValue : SymInfo.Flags) + switch (FlagValue) { + case SymbolFlag::Global: + Bind = ELF::STB_GLOBAL; + break; + case SymbolFlag::Local: + Bind = ELF::STB_LOCAL; + break; + case SymbolFlag::Weak: + Bind = ELF::STB_WEAK; + break; + case SymbolFlag::Default: + Visibility = ELF::STV_DEFAULT; + break; + case SymbolFlag::Hidden: + Visibility = ELF::STV_HIDDEN; + break; + case SymbolFlag::Protected: + Visibility = ELF::STV_PROTECTED; + break; + case SymbolFlag::File: + Type = ELF::STT_FILE; + break; + case SymbolFlag::Section: + Type = ELF::STT_SECTION; + break; + case SymbolFlag::Object: + Type = ELF::STT_OBJECT; + break; + case SymbolFlag::Function: + Type = ELF::STT_FUNC; + break; + case SymbolFlag::IndirectFunction: + Type = ELF::STT_GNU_IFUNC; + break; + default: /* Other flag values are ignored for ELF. */ + break; + }; + + Obj.SymbolTable->addSymbol( + SymInfo.SymbolName, Bind, Type, Sec, Value, Visibility, + Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); +} + // This function handles the high level operations of GNU objcopy including // handling command line options. It's important to outline certain properties // we expect to hold of the command line operations. Any operation that "keeps" @@ -620,21 +561,19 @@ // any previous removals. Lastly whether or not something is removed shouldn't // depend a) on the order the options occur in or b) on some opaque priority // system. The only priority is that keeps/copies overrule removes. -static Error handleArgs(const CopyConfig &Config, Object &Obj, - const Reader &Reader, ElfType OutputElfType) { - if (Config.StripSwiftSymbols) - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for ELF"); - if (!Config.SplitDWO.empty()) - if (Error E = - splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType)) - return E; - +static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig, + Object &Obj) { if (Config.OutputArch) { Obj.Machine = Config.OutputArch.getValue().EMachine; Obj.OSABI = Config.OutputArch.getValue().OSABI; } + if (!Config.SplitDWO.empty() && Config.ExtractDWO) { + return Obj.removeSections( + Config.AllowBrokenLinks, + [&Obj](const SectionBase &Sec) { return onlyKeepDWOPred(Obj, Sec); }); + } + // Dump sections before add/remove for compatibility with GNU objcopy. for (StringRef Flag : Config.DumpSection) { StringRef SectionName; @@ -748,17 +687,12 @@ // If the symbol table was previously removed, we need to create a new one // before adding new symbols. - if (!Obj.SymbolTable && !Config.ELF->SymbolsToAdd.empty()) + if (!Obj.SymbolTable && !Config.SymbolsToAdd.empty()) if (Error E = Obj.addNewSymbolTable()) return E; - for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) { - SectionBase *Sec = Obj.findSection(SI.SectionName); - uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value; - Obj.SymbolTable->addSymbol( - SI.SymbolName, SI.Bind, SI.Type, Sec, Value, SI.Visibility, - Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); - } + for (const NewSymbolInfo &SI : Config.SymbolsToAdd) + addSymbol(Obj, SI, ELFConfig.NewSymbolVisibility); // --set-section-flags works with sections added by --add-section. if (!Config.SetSectionFlags.empty()) { @@ -776,8 +710,8 @@ return Error::success(); } -static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out, - ElfType OutputElfType) { +static Error writeOutput(const CommonConfig &Config, Object &Obj, + raw_ostream &Out, ElfType OutputElfType) { std::unique_ptr<Writer> Writer = createWriter(Config, Obj, Out, OutputElfType); if (Error E = Writer->finalize()) @@ -785,8 +719,9 @@ return Writer->write(); } -Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { +Error objcopy::elf::executeObjcopyOnIHex(const CommonConfig &Config, + const ELFConfig &ELFConfig, + MemoryBuffer &In, raw_ostream &Out) { IHexReader Reader(&In); Expected<std::unique_ptr<Object>> Obj = Reader.create(true); if (!Obj) @@ -794,16 +729,16 @@ const ElfType OutputElfType = getOutputElfType(Config.OutputArch.getValueOr(MachineInfo())); - if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) + if (Error E = handleArgs(Config, ELFConfig, **Obj)) return E; return writeOutput(Config, **Obj, Out, OutputElfType); } -Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { - uint8_t NewSymbolVisibility = - Config.ELF->NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT); - BinaryReader Reader(&In, NewSymbolVisibility); +Error objcopy::elf::executeObjcopyOnRawBinary(const CommonConfig &Config, + const ELFConfig &ELFConfig, + MemoryBuffer &In, + raw_ostream &Out) { + BinaryReader Reader(&In, ELFConfig.NewSymbolVisibility); Expected<std::unique_ptr<Object>> Obj = Reader.create(true); if (!Obj) return Obj.takeError(); @@ -812,13 +747,15 @@ // (-B<arch>). const ElfType OutputElfType = getOutputElfType(Config.OutputArch.getValueOr(MachineInfo())); - if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) + if (Error E = handleArgs(Config, ELFConfig, **Obj)) return E; return writeOutput(Config, **Obj, Out, OutputElfType); } -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out) { +Error objcopy::elf::executeObjcopyOnBinary(const CommonConfig &Config, + const ELFConfig &ELFConfig, + object::ELFObjectFileBase &In, + raw_ostream &Out) { ELFReader Reader(&In, Config.ExtractPartition); Expected<std::unique_ptr<Object>> Obj = Reader.create(!Config.SymbolsToAdd.empty()); @@ -828,41 +765,12 @@ const ElfType OutputElfType = Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) : getOutputElfType(In); - ArrayRef<uint8_t> BuildIdBytes; - if (!Config.BuildIdLinkDir.empty()) { - auto BuildIdBytesOrErr = findBuildID(Config, In); - if (auto E = BuildIdBytesOrErr.takeError()) - return E; - BuildIdBytes = *BuildIdBytesOrErr; - - if (BuildIdBytes.size() < 2) - return createFileError( - Config.InputFilename, - createStringError(object_error::parse_failed, - "build ID is smaller than two bytes")); - } - - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) - if (Error E = - linkToBuildIdDir(Config, Config.InputFilename, - Config.BuildIdLinkInput.getValue(), BuildIdBytes)) - return E; - - if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) + if (Error E = handleArgs(Config, ELFConfig, **Obj)) return createFileError(Config.InputFilename, std::move(E)); if (Error E = writeOutput(Config, **Obj, Out, OutputElfType)) return createFileError(Config.InputFilename, std::move(E)); - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) - if (Error E = - linkToBuildIdDir(Config, Config.OutputFilename, - Config.BuildIdLinkOutput.getValue(), BuildIdBytes)) - return createFileError(Config.OutputFilename, std::move(E)); return Error::success(); } - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h index e13e237..852661e 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h
@@ -12,22 +12,26 @@ namespace llvm { class Error; class MemoryBuffer; +class raw_ostream; namespace object { class ELFObjectFileBase; } // end namespace object namespace objcopy { -struct CopyConfig; -class Buffer; +struct CommonConfig; +struct ELFConfig; namespace elf { -Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out); -Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out); -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out); +Error executeObjcopyOnIHex(const CommonConfig &Config, + const ELFConfig &ELFConfig, MemoryBuffer &In, + raw_ostream &Out); +Error executeObjcopyOnRawBinary(const CommonConfig &Config, + const ELFConfig &ELFConfig, MemoryBuffer &In, + raw_ostream &Out); +Error executeObjcopyOnBinary(const CommonConfig &Config, + const ELFConfig &ELFConfig, + object::ELFObjectFileBase &In, raw_ostream &Out); } // end namespace elf } // end namespace objcopy
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/Object.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/Object.cpp index 0ff82f9..ba91d08 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/Object.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/Object.cpp
@@ -29,16 +29,14 @@ #include <utility> #include <vector> -namespace llvm { -namespace objcopy { -namespace elf { - -using namespace object; -using namespace ELF; +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::objcopy::elf; +using namespace llvm::object; template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) { - uint8_t *B = Buf.getBufferStart() + Obj.ProgramHdrSegment.Offset + - Seg.Index * sizeof(Elf_Phdr); + uint8_t *B = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + + Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr); Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B); Phdr.p_type = Seg.Type; Phdr.p_flags = Seg.Flags; @@ -67,7 +65,8 @@ void SectionBase::onRemove() {} template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) { - uint8_t *B = Buf.getBufferStart() + Sec.HeaderOffset; + uint8_t *B = + reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Sec.HeaderOffset; Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B); Shdr.sh_name = Sec.NameIndex; Shdr.sh_type = Sec.Type; @@ -191,7 +190,7 @@ // Fills exactly Len bytes of buffer with hexadecimal characters // representing value 'X' template <class T, class Iterator> -static Iterator utohexstr(T X, Iterator It, size_t Len) { +static Iterator toHexStr(T X, Iterator It, size_t Len) { // Fill range with '0' std::fill(It, It + Len, '0'); @@ -220,13 +219,13 @@ assert(Line.size()); auto Iter = Line.begin(); *Iter++ = ':'; - Iter = utohexstr(Data.size(), Iter, 2); - Iter = utohexstr(Addr, Iter, 4); - Iter = utohexstr(Type, Iter, 2); + Iter = toHexStr(Data.size(), Iter, 2); + Iter = toHexStr(Addr, Iter, 4); + Iter = toHexStr(Type, Iter, 2); for (uint8_t X : Data) - Iter = utohexstr(X, Iter, 2); + Iter = toHexStr(X, Iter, 2); StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter)); - Iter = utohexstr(getChecksum(S), Iter, 2); + Iter = toHexStr(getChecksum(S), Iter, 2); *Iter++ = '\r'; *Iter++ = '\n'; assert(Iter == Line.end()); @@ -474,7 +473,7 @@ return createStringError(errc::invalid_argument, "'" + Sec.Name + "': " + toString(std::move(Err))); - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); return Error::success(); @@ -519,7 +518,7 @@ template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; if (Sec.CompressionType == DebugCompressionType::None) { std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); return Error::success(); @@ -624,7 +623,8 @@ } Error SectionWriter::visit(const StringTableSection &Sec) { - Sec.StrTabBuilder.write(Out.getBufferStart() + Sec.Offset); + Sec.StrTabBuilder.write(reinterpret_cast<uint8_t *>(Out.getBufferStart()) + + Sec.Offset); return Error::success(); } @@ -638,7 +638,7 @@ template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; llvm::copy(Sec.Indexes, reinterpret_cast<Elf_Word *>(Buf)); return Error::success(); } @@ -979,7 +979,7 @@ template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const RelocationSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; if (Sec.Type == SHT_REL) writeRel(Sec.Relocations, reinterpret_cast<Elf_Rel *>(Buf)); else @@ -1069,6 +1069,12 @@ void GroupSection::finalize() { this->Info = Sym ? Sym->Index : 0; this->Link = SymTab ? SymTab->Index : 0; + // Linker deduplication for GRP_COMDAT is based on Sym->Name. The local/global + // status is not part of the equation. If Sym is localized, the intention is + // likely to make the group fully localized. Drop GRP_COMDAT to suppress + // deduplication. See https://groups.google.com/g/generic-abi/c/2X6mR-s2zoc + if ((FlagWord & GRP_COMDAT) && Sym && Sym->Binding == STB_LOCAL) + this->FlagWord &= ~GRP_COMDAT; } Error GroupSection::removeSectionReferences( @@ -1160,7 +1166,8 @@ template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) { - unsigned char *Buf = Out.getBufferStart() + Sec.Offset; + unsigned char *Buf = + reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; Elf_Word *CRC = reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word)); *CRC = Sec.CRC32; @@ -1180,7 +1187,7 @@ Error ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) { ELF::Elf32_Word *Buf = reinterpret_cast<ELF::Elf32_Word *>(Out.getBufferStart() + Sec.Offset); - *Buf++ = Sec.FlagWord; + support::endian::write32<ELFT::TargetEndianness>(Buf++, Sec.FlagWord); for (SectionBase *S : Sec.GroupMembers) support::endian::write32<ELFT::TargetEndianness>(Buf++, S->Index); return Error::success(); @@ -1202,6 +1209,10 @@ // not the first. uint64_t SecSize = Sec.Size ? Sec.Size : 1; + // Ignore just added sections. + if (Sec.OriginalOffset == std::numeric_limits<uint64_t>::max()) + return false; + if (Sec.Type == SHT_NOBITS) { if (!(Sec.Flags & SHF_ALLOC)) return false; @@ -1290,8 +1301,9 @@ DataSection.Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE; std::string SanitizedFilename = MemBuf->getBufferIdentifier().str(); - std::replace_if(std::begin(SanitizedFilename), std::end(SanitizedFilename), - [](char C) { return !isalnum(C); }, '_'); + std::replace_if( + std::begin(SanitizedFilename), std::end(SanitizedFilename), + [](char C) { return !isAlnum(C); }, '_'); Twine Prefix = Twine("_binary_") + SanitizedFilename; SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection, @@ -1505,7 +1517,8 @@ reinterpret_cast<const ELF::Elf32_Word *>(GroupSec->Contents.data()); const ELF::Elf32_Word *End = Word + GroupSec->Contents.size() / sizeof(ELF::Elf32_Word); - GroupSec->setFlagWord(*Word++); + GroupSec->setFlagWord( + support::endian::read32<ELFT::TargetEndianness>(Word++)); for (; Word != End; ++Word) { uint32_t Index = support::endian::read32<ELFT::TargetEndianness>(Word); Expected<SectionBase *> Sec = SecTable.getSection( @@ -1778,7 +1791,7 @@ Sec->OriginalIndex = Sec->Index; Sec->OriginalData = ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset, - (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size); + (Shdr.sh_type == SHT_NOBITS) ? (size_t)0 : Shdr.sh_size); } return Error::success(); @@ -1971,7 +1984,7 @@ } template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { - Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf.getBufferStart()); + Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf->getBufferStart()); std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0); Ehdr.e_ident[EI_MAG0] = 0x7f; Ehdr.e_ident[EI_MAG1] = 'E'; @@ -2036,7 +2049,7 @@ // This reference serves to write the dummy section header at the begining // of the file. It is not used for anything else Elf_Shdr &Shdr = - *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOff); + *reinterpret_cast<Elf_Shdr *>(Buf->getBufferStart() + Obj.SHOff); Shdr.sh_name = 0; Shdr.sh_type = SHT_NULL; Shdr.sh_flags = 0; @@ -2076,7 +2089,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() { for (Segment &Seg : Obj.segments()) { size_t Size = std::min<size_t>(Seg.FileSize, Seg.getContents().size()); - std::memcpy(Buf.getBufferStart() + Seg.Offset, Seg.getContents().data(), + std::memcpy(Buf->getBufferStart() + Seg.Offset, Seg.getContents().data(), Size); } @@ -2087,12 +2100,12 @@ continue; uint64_t Offset = Sec.OriginalOffset - Parent->OriginalOffset + Parent->Offset; - std::memset(Buf.getBufferStart() + Offset, 0, Sec.Size); + std::memset(Buf->getBufferStart() + Offset, 0, Sec.Size); } } template <class ELFT> -ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH, +ELFWriter<ELFT>::ELFWriter(Object &Obj, raw_ostream &Buf, bool WSH, bool OnlyKeepDebug) : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs), OnlyKeepDebug(OnlyKeepDebug) {} @@ -2311,18 +2324,19 @@ uint64_t HdrEnd) { uint64_t MaxOffset = 0; for (Segment *Seg : Segments) { - // An empty segment contains no section (see sectionWithinSegment). If it - // has a parent segment, copy the parent segment's offset field. This works - // for empty PT_TLS. We don't handle empty segments without a parent for - // now. - if (Seg->ParentSegment != nullptr && Seg->MemSize == 0) - Seg->Offset = Seg->ParentSegment->Offset; - - const SectionBase *FirstSec = Seg->firstSection(); - if (Seg->Type == PT_PHDR || !FirstSec) + if (Seg->Type == PT_PHDR) continue; - uint64_t Offset = FirstSec->Offset; + // The segment offset is generally the offset of the first section. + // + // For a segment containing no section (see sectionWithinSegment), if it has + // a parent segment, copy the parent segment's offset field. This works for + // empty PT_TLS. If no parent segment, use 0: the segment is not useful for + // debugging anyway. + const SectionBase *FirstSec = Seg->firstSection(); + uint64_t Offset = + FirstSec ? FirstSec->Offset + : (Seg->ParentSegment ? Seg->ParentSegment->Offset : 0); uint64_t FileSize = 0; for (const SectionBase *Sec : Seg->Sections) { uint64_t Size = Sec->Type == SHT_NOBITS ? 0 : Sec->Size; @@ -2409,7 +2423,11 @@ return E; if (WriteSectionHeaders) writeShdrs(); - return Buf.commit(); + + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); } static Error removeUnneededSections(Object &Obj) { @@ -2450,8 +2468,10 @@ bool NeedsLargeIndexes = false; if (Obj.sections().size() >= SHN_LORESERVE) { SectionTableRef Sections = Obj.sections(); + // Sections doesn't include the null section header, so account for this + // when skipping the first N sections. NeedsLargeIndexes = - any_of(drop_begin(Sections, SHN_LORESERVE), + any_of(drop_begin(Sections, SHN_LORESERVE - 1), [](const SectionBase &Sec) { return Sec.HasSymbol; }); // TODO: handle case where only one section needs the large index table but // only needs it because the large index table hasn't been removed yet. @@ -2529,9 +2549,14 @@ Sec.finalize(); } - if (Error E = Buf.allocate(totalSize())) - return E; - SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(Buf); + size_t TotalSize = totalSize(); + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(TotalSize) + " bytes"); + + SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(*Buf); return Error::success(); } @@ -2540,7 +2565,10 @@ if (Error Err = Sec.accept(*SecWriter)) return Err; - return Buf.commit(); + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); } Error BinaryWriter::finalize() { @@ -2553,7 +2581,7 @@ if (Sec.ParentSegment != nullptr) Sec.Addr = Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr; - if (Sec.Size > 0) + if (Sec.Type != SHT_NOBITS && Sec.Size > 0) MinAddr = std::min(MinAddr, Sec.Addr); } @@ -2568,9 +2596,12 @@ TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); } - if (Error E = Buf.allocate(TotalSize)) - return E; - SecWriter = std::make_unique<BinarySectionWriter>(Buf); + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(TotalSize) + " bytes"); + SecWriter = std::make_unique<BinarySectionWriter>(*Buf); return Error::success(); } @@ -2608,7 +2639,7 @@ } Error IHexWriter::write() { - IHexSectionWriter Writer(Buf); + IHexSectionWriter Writer(*Buf); // Write sections. for (const SectionBase *Sec : Sections) if (Error Err = Sec->accept(Writer)) @@ -2616,11 +2647,17 @@ uint64_t Offset = Writer.getBufferOffset(); // Write entry point address. - Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset); + Offset += writeEntryPointRecord( + reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Offset); // Write EOF. - Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset); + Offset += writeEndOfFileRecord( + reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Offset); assert(Offset == TotalSize); - return Buf.commit(); + + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); } Error IHexWriter::checkSection(const SectionBase &Sec) { @@ -2634,37 +2671,27 @@ } Error IHexWriter::finalize() { - bool UseSegments = false; - auto ShouldWrite = [](const SectionBase &Sec) { - return (Sec.Flags & ELF::SHF_ALLOC) && (Sec.Type != ELF::SHT_NOBITS); - }; - auto IsInPtLoad = [](const SectionBase &Sec) { - return Sec.ParentSegment && Sec.ParentSegment->Type == ELF::PT_LOAD; - }; - // We can't write 64-bit addresses. if (addressOverflows32bit(Obj.Entry)) return createStringError(errc::invalid_argument, - "Entry point address 0x%llx overflows 32 bits.", + "Entry point address 0x%llx overflows 32 bits", Obj.Entry); - // If any section we're to write has segment then we - // switch to using physical addresses. Otherwise we - // use section virtual address. for (const SectionBase &Sec : Obj.sections()) - if (ShouldWrite(Sec) && IsInPtLoad(Sec)) { - UseSegments = true; - break; - } - - for (const SectionBase &Sec : Obj.sections()) - if (ShouldWrite(Sec) && (!UseSegments || IsInPtLoad(Sec))) { + if ((Sec.Flags & ELF::SHF_ALLOC) && Sec.Type != ELF::SHT_NOBITS && + Sec.Size > 0) { if (Error E = checkSection(Sec)) return E; Sections.insert(&Sec); } - IHexSectionWriterBase LengthCalc(Buf); + std::unique_ptr<WritableMemoryBuffer> EmptyBuffer = + WritableMemoryBuffer::getNewMemBuffer(0); + if (!EmptyBuffer) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of 0 bytes"); + + IHexSectionWriterBase LengthCalc(*EmptyBuffer); for (const SectionBase *Sec : Sections) if (Error Err = Sec->accept(LengthCalc)) return Err; @@ -2674,11 +2701,20 @@ TotalSize = LengthCalc.getBufferOffset() + (Obj.Entry ? IHexRecord::getLineLength(4) : 0) + IHexRecord::getLineLength(0); - if (Error E = Buf.allocate(TotalSize)) - return E; + + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(TotalSize) + " bytes"); + return Error::success(); } +namespace llvm { +namespace objcopy { +namespace elf { + template class ELFBuilder<ELF64LE>; template class ELFBuilder<ELF64BE>; template class ELFBuilder<ELF32LE>;
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/Object.h b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/Object.h index 0205c2d..6fd26af 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/ELF/Object.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/ELF/Object.h
@@ -9,8 +9,7 @@ #ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H #define LLVM_TOOLS_OBJCOPY_OBJECT_H -#include "Buffer.h" -#include "CopyConfig.h" +#include "CommonConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -19,6 +18,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" #include <cstddef> #include <cstdint> #include <functional> @@ -106,7 +106,7 @@ class SectionWriter : public SectionVisitor { protected: - Buffer &Out; + WritableMemoryBuffer &Out; public: virtual ~SectionWriter() = default; @@ -123,7 +123,7 @@ virtual Error visit(const CompressedSection &Sec) override = 0; virtual Error visit(const DecompressedSection &Sec) override = 0; - explicit SectionWriter(Buffer &Buf) : Out(Buf) {} + explicit SectionWriter(WritableMemoryBuffer &Buf) : Out(Buf) {} }; template <class ELFT> class ELFSectionWriter : public SectionWriter { @@ -143,7 +143,7 @@ Error visit(const CompressedSection &Sec) override; Error visit(const DecompressedSection &Sec) override; - explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {} + explicit ELFSectionWriter(WritableMemoryBuffer &Buf) : SectionWriter(Buf) {} }; template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor { @@ -187,7 +187,8 @@ Error visit(const CompressedSection &Sec) override; Error visit(const DecompressedSection &Sec) override; - explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {} + explicit BinarySectionWriter(WritableMemoryBuffer &Buf) + : SectionWriter(Buf) {} }; using IHexLineData = SmallVector<char, 64>; @@ -283,7 +284,8 @@ virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data); public: - explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf) {} + explicit IHexSectionWriterBase(WritableMemoryBuffer &Buf) + : BinarySectionWriter(Buf) {} uint64_t getBufferOffset() const { return Offset; } Error visit(const Section &Sec) final; @@ -296,7 +298,7 @@ // Real IHEX section writer class IHexSectionWriter : public IHexSectionWriterBase { public: - IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {} + IHexSectionWriter(WritableMemoryBuffer &Buf) : IHexSectionWriterBase(Buf) {} void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data) override; Error visit(const StringTableSection &Sec) override; @@ -305,14 +307,15 @@ class Writer { protected: Object &Obj; - Buffer &Buf; + std::unique_ptr<WritableMemoryBuffer> Buf; + raw_ostream &Out; public: virtual ~Writer(); virtual Error finalize() = 0; virtual Error write() = 0; - Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {} + Writer(Object &O, raw_ostream &Out) : Obj(O), Out(Out) {} }; template <class ELFT> class ELFWriter : public Writer { @@ -349,7 +352,7 @@ Error finalize() override; Error write() override; - ELFWriter(Object &Obj, Buffer &Buf, bool WSH, bool OnlyKeepDebug); + ELFWriter(Object &Obj, raw_ostream &Out, bool WSH, bool OnlyKeepDebug); }; class BinaryWriter : public Writer { @@ -362,7 +365,7 @@ ~BinaryWriter() {} Error finalize() override; Error write() override; - BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} + BinaryWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {} }; class IHexWriter : public Writer { @@ -381,7 +384,7 @@ ~IHexWriter() {} Error finalize() override; Error write() override; - IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} + IHexWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {} }; class SectionBase {
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOConfig.h b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOConfig.h new file mode 100644 index 0000000..7c5dbfd --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOConfig.h
@@ -0,0 +1,21 @@ +//===- MachOConfig.h --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H + +namespace llvm { +namespace objcopy { + +// Mach-O specific configuration for copying/stripping a single file. +struct MachOConfig {}; + +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp index 8e2bf36..6ed2180 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
@@ -11,9 +11,8 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy::macho; StringTableBuilder::Kind MachOLayoutBuilder::getStringTableBuilderKind(const Object &O, bool Is64Bit) { @@ -252,7 +251,10 @@ uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size(); uint64_t StartOfDataInCode = StartOfFunctionStarts + O.FunctionStarts.Data.size(); - uint64_t StartOfSymbols = StartOfDataInCode + O.DataInCode.Data.size(); + uint64_t StartOfLinkerOptimizationHint = + StartOfDataInCode + O.DataInCode.Data.size(); + uint64_t StartOfSymbols = + StartOfLinkerOptimizationHint + O.LinkerOptimizationHint.Data.size(); uint64_t StartOfIndirectSymbols = StartOfSymbols + NListSize * O.SymTable.Symbols.size(); uint64_t StartOfSymbolStrings = @@ -321,6 +323,11 @@ MLC.linkedit_data_command_data.dataoff = StartOfDataInCode; MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size(); break; + case MachO::LC_LINKER_OPTIMIZATION_HINT: + MLC.linkedit_data_command_data.dataoff = StartOfLinkerOptimizationHint; + MLC.linkedit_data_command_data.datasize = + O.LinkerOptimizationHint.Data.size(); + break; case MachO::LC_FUNCTION_STARTS: MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts; MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size(); @@ -371,6 +378,8 @@ case MachO::LC_LOAD_WEAK_DYLIB: case MachO::LC_UUID: case MachO::LC_SOURCE_VERSION: + case MachO::LC_THREAD: + case MachO::LC_UNIXTHREAD: // Nothing to update. break; default: @@ -392,7 +401,3 @@ Offset = layoutRelocations(Offset); return layoutTail(Offset); } - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp index fef4a0a..8233069 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
@@ -7,22 +7,25 @@ //===----------------------------------------------------------------------===// #include "MachOObjcopy.h" -#include "../CopyConfig.h" #include "../llvm-objcopy.h" +#include "CommonConfig.h" #include "MachOReader.h" #include "MachOWriter.h" +#include "MultiFormatConfig.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/MachOUniversalWriter.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy; +using namespace llvm::objcopy::macho; +using namespace llvm::object; -using namespace object; using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>; using LoadCommandPred = std::function<bool(const LoadCommand &LC)>; @@ -46,7 +49,7 @@ .rtrim('\0'); } -static Error removeSections(const CopyConfig &Config, Object &Obj) { +static Error removeSections(const CommonConfig &Config, Object &Obj) { SectionPred RemovePred = [](const std::unique_ptr<Section> &) { return false; }; @@ -77,14 +80,14 @@ return Obj.removeSections(RemovePred); } -static void markSymbols(const CopyConfig &Config, Object &Obj) { +static void markSymbols(const CommonConfig &, Object &Obj) { // Symbols referenced from the indirect symbol table must not be removed. for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols) if (ISE.Symbol) (*ISE.Symbol)->Referenced = true; } -static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { +static void updateAndRemoveSymbols(const CommonConfig &Config, Object &Obj) { for (SymbolEntry &Sym : Obj.SymTable) { auto I = Config.SymbolsToRename.find(Sym.Name); if (I != Config.SymbolsToRename.end()) @@ -94,6 +97,10 @@ auto RemovePred = [Config, &Obj](const std::unique_ptr<SymbolEntry> &N) { if (N->Referenced) return false; + if (Config.KeepUndefined && N->isUndefinedSymbol()) + return false; + if (N->n_desc & MachO::REFERENCED_DYNAMICALLY) + return false; if (Config.StripAll) return true; if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT)) @@ -132,7 +139,7 @@ return LC; } -static Error processLoadCommands(const CopyConfig &Config, Object &Obj) { +static Error processLoadCommands(const CommonConfig &Config, Object &Obj) { // Remove RPaths. DenseSet<StringRef> RPathsToRemove(Config.RPathsToRemove.begin(), Config.RPathsToRemove.end()); @@ -326,26 +333,7 @@ return Error::success(); } -static Error handleArgs(const CopyConfig &Config, Object &Obj) { - if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || - Config.BuildIdLinkInput || Config.BuildIdLinkOutput || - !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.KeepSection.empty() || - Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() || - !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || - !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || - !Config.SectionsToRename.empty() || - !Config.UnneededSymbolsToRemove.empty() || - !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || - Config.ExtractDWO || Config.LocalizeHidden || Config.PreserveDates || - Config.StripAllGNU || Config.StripDWO || Config.StripNonAlloc || - Config.StripSections || Config.Weaken || Config.DecompressDebugSections || - Config.StripUnneeded || Config.DiscardMode == DiscardType::Locals || - !Config.SymbolsToAdd.empty() || Config.EntryExpr) { - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for MachO"); - } - +static Error handleArgs(const CommonConfig &Config, Object &Obj) { // Dump sections before add/remove for compatibility with GNU objcopy. for (StringRef Flag : Config.DumpSection) { StringRef SectionName; @@ -385,8 +373,10 @@ return Error::success(); } -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::MachOObjectFile &In, Buffer &Out) { +Error objcopy::macho::executeObjcopyOnBinary(const CommonConfig &Config, + const MachOConfig &, + object::MachOObjectFile &In, + raw_ostream &Out) { MachOReader Reader(In); Expected<std::unique_ptr<Object>> O = Reader.create(); if (!O) @@ -414,9 +404,9 @@ return Writer.write(); } -Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config, - const MachOUniversalBinary &In, - Buffer &Out) { +Error objcopy::macho::executeObjcopyOnMachOUniversalBinary( + const MultiFormatConfig &Config, const MachOUniversalBinary &In, + raw_ostream &Out) { SmallVector<OwningBinary<Binary>, 2> Binaries; SmallVector<Slice, 2> Slices; for (const auto &O : In.objects()) { @@ -429,7 +419,7 @@ Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr = writeArchiveToBuffer(*NewArchiveMembersOrErr, (*ArOrErr)->hasSymbolTable(), (*ArOrErr)->kind(), - Config.DeterministicArchives, + Config.getCommonConfig().DeterministicArchives, (*ArOrErr)->isThin()); if (!OutputBufferOrErr) return OutputBufferOrErr.takeError(); @@ -453,36 +443,39 @@ Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile(); if (!ObjOrErr) { consumeError(ObjOrErr.takeError()); - return createStringError(std::errc::invalid_argument, - "slice for '%s' of the universal Mach-O binary " - "'%s' is not a Mach-O object or an archive", - O.getArchFlagName().c_str(), - Config.InputFilename.str().c_str()); + return createStringError( + std::errc::invalid_argument, + "slice for '%s' of the universal Mach-O binary " + "'%s' is not a Mach-O object or an archive", + O.getArchFlagName().c_str(), + Config.getCommonConfig().InputFilename.str().c_str()); } std::string ArchFlagName = O.getArchFlagName(); - MemBuffer MB(ArchFlagName); - if (Error E = executeObjcopyOnBinary(Config, **ObjOrErr, MB)) + + SmallVector<char, 0> Buffer; + raw_svector_ostream MemStream(Buffer); + + Expected<const MachOConfig &> MachO = Config.getMachOConfig(); + if (!MachO) + return MachO.takeError(); + + if (Error E = executeObjcopyOnBinary(Config.getCommonConfig(), *MachO, + **ObjOrErr, MemStream)) return E; - std::unique_ptr<WritableMemoryBuffer> OutputBuffer = - MB.releaseMemoryBuffer(); - Expected<std::unique_ptr<Binary>> BinaryOrErr = - object::createBinary(*OutputBuffer); + + std::unique_ptr<MemoryBuffer> MB = + std::make_unique<SmallVectorMemoryBuffer>(std::move(Buffer), + ArchFlagName); + Expected<std::unique_ptr<Binary>> BinaryOrErr = object::createBinary(*MB); if (!BinaryOrErr) return BinaryOrErr.takeError(); - Binaries.emplace_back(std::move(*BinaryOrErr), std::move(OutputBuffer)); + Binaries.emplace_back(std::move(*BinaryOrErr), std::move(MB)); Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()), O.getAlign()); } - Expected<std::unique_ptr<MemoryBuffer>> B = - writeUniversalBinaryToBuffer(Slices); - if (!B) - return B.takeError(); - if (Error E = Out.allocate((*B)->getBufferSize())) - return E; - memcpy(Out.getBufferStart(), (*B)->getBufferStart(), (*B)->getBufferSize()); - return Out.commit(); -} -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm + if (Error Err = writeUniversalBinaryToStream(Slices, Out)) + return Err; + + return Error::success(); +}
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h index c3f5391..e30940a 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h
@@ -11,6 +11,7 @@ namespace llvm { class Error; +class raw_ostream; namespace object { class MachOObjectFile; @@ -18,15 +19,17 @@ } // end namespace object namespace objcopy { -struct CopyConfig; -class Buffer; +struct CommonConfig; +struct MachOConfig; +class MultiFormatConfig; namespace macho { -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::MachOObjectFile &In, Buffer &Out); +Error executeObjcopyOnBinary(const CommonConfig &Config, const MachOConfig &, + object::MachOObjectFile &In, raw_ostream &Out); Error executeObjcopyOnMachOUniversalBinary( - CopyConfig &Config, const object::MachOUniversalBinary &In, Buffer &Out); + const MultiFormatConfig &Config, const object::MachOUniversalBinary &In, + raw_ostream &Out); } // end namespace macho } // end namespace objcopy
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp index 548a85b..7d1c29b 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
@@ -13,9 +13,9 @@ #include "llvm/Support/Errc.h" #include <memory> -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy; +using namespace llvm::objcopy::macho; void MachOReader::readHeader(Object &O) const { O.Header.Magic = MachOObj.getHeader().magic; @@ -28,7 +28,7 @@ } template <typename SectionType> -Section constructSectionCommon(SectionType Sec, uint32_t Index) { +static Section constructSectionCommon(const SectionType &Sec, uint32_t Index) { StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname))); StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))); Section S(SegName, SectName); @@ -46,39 +46,34 @@ return S; } -template <typename SectionType> -Section constructSection(SectionType Sec, uint32_t Index); - -template <> Section constructSection(MachO::section Sec, uint32_t Index) { +Section constructSection(const MachO::section &Sec, uint32_t Index) { return constructSectionCommon(Sec, Index); } -template <> Section constructSection(MachO::section_64 Sec, uint32_t Index) { +Section constructSection(const MachO::section_64 &Sec, uint32_t Index) { Section S = constructSectionCommon(Sec, Index); S.Reserved3 = Sec.reserved3; return S; } template <typename SectionType, typename SegmentType> -Expected<std::vector<std::unique_ptr<Section>>> -extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, - const object::MachOObjectFile &MachOObj, - uint32_t &NextSectionIndex) { - auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize; - const SectionType *Curr = - reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType)); +Expected<std::vector<std::unique_ptr<Section>>> static extractSections( + const object::MachOObjectFile::LoadCommandInfo &LoadCmd, + const object::MachOObjectFile &MachOObj, uint32_t &NextSectionIndex) { std::vector<std::unique_ptr<Section>> Sections; - for (; reinterpret_cast<const void *>(Curr) < End; Curr++) { - if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { - SectionType Sec; - memcpy((void *)&Sec, Curr, sizeof(SectionType)); + for (auto Curr = reinterpret_cast<const SectionType *>(LoadCmd.Ptr + + sizeof(SegmentType)), + End = reinterpret_cast<const SectionType *>(LoadCmd.Ptr + + LoadCmd.C.cmdsize); + Curr < End; ++Curr) { + SectionType Sec; + memcpy((void *)&Sec, Curr, sizeof(SectionType)); + + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) MachO::swapStruct(Sec); - Sections.push_back( - std::make_unique<Section>(constructSection(Sec, NextSectionIndex))); - } else { - Sections.push_back( - std::make_unique<Section>(constructSection(*Curr, NextSectionIndex))); - } + + Sections.push_back( + std::make_unique<Section>(constructSection(Sec, NextSectionIndex))); Section &S = *Sections.back(); @@ -95,6 +90,7 @@ S.Content = StringRef(reinterpret_cast<const char *>(Data->data()), Data->size()); + const uint32_t CPUType = MachOObj.getHeader().cputype; S.Relocations.reserve(S.NReloc); for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()), RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl()); @@ -103,6 +99,10 @@ R.Symbol = nullptr; // We'll fill this field later. R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl()); R.Scattered = MachOObj.isRelocationScattered(R.Info); + unsigned Type = MachOObj.getAnyRelocationType(R.Info); + // TODO Support CPU_TYPE_ARM. + R.IsAddend = !R.Scattered && (CPUType == MachO::CPU_TYPE_ARM64 && + Type == MachO::ARM64_RELOC_ADDEND); R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info); S.Relocations.push_back(R); } @@ -151,6 +151,9 @@ case MachO::LC_DATA_IN_CODE: O.DataInCodeCommandIndex = O.LoadCommands.size(); break; + case MachO::LC_LINKER_OPTIMIZATION_HINT: + O.LinkerOptimizationHintCommandIndex = O.LoadCommands.size(); + break; case MachO::LC_FUNCTION_STARTS: O.FunctionStartsCommandIndex = O.LoadCommands.size(); break; @@ -223,7 +226,7 @@ for (LoadCommand &LC : O.LoadCommands) for (std::unique_ptr<Section> &Sec : LC.Sections) for (auto &Reloc : Sec->Relocations) - if (!Reloc.Scattered) { + if (!Reloc.Scattered && !Reloc.IsAddend) { const uint32_t SymbolNum = Reloc.getPlainRelocationSymbolNum(MachOObj.isLittleEndian()); if (Reloc.Extern) { @@ -276,6 +279,11 @@ return readLinkData(O, O.DataInCodeCommandIndex, O.DataInCode); } +void MachOReader::readLinkerOptimizationHint(Object &O) const { + return readLinkData(O, O.LinkerOptimizationHintCommandIndex, + O.LinkerOptimizationHint); +} + void MachOReader::readFunctionStartsData(Object &O) const { return readLinkData(O, O.FunctionStartsCommandIndex, O.FunctionStarts); } @@ -330,12 +338,9 @@ readExportInfo(*Obj); readCodeSignature(*Obj); readDataInCodeData(*Obj); + readLinkerOptimizationHint(*Obj); readFunctionStartsData(*Obj); readIndirectSymbolTable(*Obj); readSwiftVersion(*Obj); return std::move(Obj); } - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOReader.h b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOReader.h index b446e02..ca3a021 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOReader.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOReader.h
@@ -39,6 +39,7 @@ void readLinkData(Object &O, Optional<size_t> LCIndex, LinkData &LD) const; void readCodeSignature(Object &O) const; void readDataInCodeData(Object &O) const; + void readLinkerOptimizationHint(Object &O) const; void readFunctionStartsData(Object &O) const; void readIndirectSymbolTable(Object &O) const; void readSwiftVersion(Object &O) const;
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp index 56dd08d..295098e 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp
@@ -16,9 +16,8 @@ #include "llvm/Support/ErrorHandling.h" #include <memory> -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy::macho; size_t MachOWriter::headerSize() const { return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); @@ -108,6 +107,16 @@ LinkEditDataCommand.datasize); } + if (O.LinkerOptimizationHintCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.LinkerOptimizationHintCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Ends.push_back(LinkEditDataCommand.dataoff + + LinkEditDataCommand.datasize); + } + if (O.FunctionStartsCommandIndex) { const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*O.FunctionStartsCommandIndex] @@ -159,11 +168,12 @@ auto HeaderSize = Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); - memcpy(B.getBufferStart(), &Header, HeaderSize); + memcpy(Buf->getBufferStart(), &Header, HeaderSize); } void MachOWriter::writeLoadCommands() { - uint8_t *Begin = B.getBufferStart() + headerSize(); + uint8_t *Begin = + reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + headerSize(); for (const LoadCommand &LC : O.LoadCommands) { // Construct a load command. MachO::macho_load_command MLC = LC.MachOLoadCommand; @@ -257,11 +267,11 @@ assert(Sec->Offset && "Section offset can not be zero"); assert((Sec->Size == Sec->Content.size()) && "Incorrect section size"); - memcpy(B.getBufferStart() + Sec->Offset, Sec->Content.data(), + memcpy(Buf->getBufferStart() + Sec->Offset, Sec->Content.data(), Sec->Content.size()); for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) { RelocationInfo RelocInfo = Sec->Relocations[Index]; - if (!RelocInfo.Scattered) { + if (!RelocInfo.Scattered && !RelocInfo.IsAddend) { const uint32_t SymbolNum = RelocInfo.Extern ? (*RelocInfo.Symbol)->Index : (*RelocInfo.Sec)->Index; @@ -270,7 +280,7 @@ if (IsLittleEndian != sys::IsLittleEndianHost) MachO::swapStruct( reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info)); - memcpy(B.getBufferStart() + Sec->RelOff + + memcpy(Buf->getBufferStart() + Sec->RelOff + Index * sizeof(MachO::any_relocation_info), &RelocInfo.Info, sizeof(RelocInfo.Info)); } @@ -300,7 +310,7 @@ O.LoadCommands[*O.SymTabCommandIndex] .MachOLoadCommand.symtab_command_data; - uint8_t *StrTable = (uint8_t *)B.getBufferStart() + SymTabCommand.stroff; + uint8_t *StrTable = (uint8_t *)Buf->getBufferStart() + SymTabCommand.stroff; LayoutBuilder.getStringTableBuilder().write(StrTable); } @@ -311,7 +321,7 @@ O.LoadCommands[*O.SymTabCommandIndex] .MachOLoadCommand.symtab_command_data; - char *SymTable = (char *)B.getBufferStart() + SymTabCommand.symoff; + char *SymTable = (char *)Buf->getBufferStart() + SymTabCommand.symoff; for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end(); Iter != End; Iter++) { SymbolEntry *Sym = Iter->get(); @@ -330,7 +340,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.rebase_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.rebase_off; assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && "Incorrect rebase opcodes size"); memcpy(Out, O.Rebases.Opcodes.data(), O.Rebases.Opcodes.size()); @@ -342,7 +352,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.bind_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.bind_off; assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && "Incorrect bind opcodes size"); memcpy(Out, O.Binds.Opcodes.data(), O.Binds.Opcodes.size()); @@ -354,7 +364,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.weak_bind_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.weak_bind_off; assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && "Incorrect weak bind opcodes size"); memcpy(Out, O.WeakBinds.Opcodes.data(), O.WeakBinds.Opcodes.size()); @@ -366,7 +376,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.lazy_bind_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.lazy_bind_off; assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && "Incorrect lazy bind opcodes size"); memcpy(Out, O.LazyBinds.Opcodes.data(), O.LazyBinds.Opcodes.size()); @@ -378,7 +388,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.export_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.export_off; assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && "Incorrect export trie size"); memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size()); @@ -393,7 +403,7 @@ .MachOLoadCommand.dysymtab_command_data; uint32_t *Out = - (uint32_t *)(B.getBufferStart() + DySymTabCommand.indirectsymoff); + (uint32_t *)(Buf->getBufferStart() + DySymTabCommand.indirectsymoff); for (const IndirectSymbolEntry &Sym : O.IndirectSymTable.Symbols) { uint32_t Entry = (Sym.Symbol) ? (*Sym.Symbol)->Index : Sym.OriginalIndex; if (IsLittleEndian != sys::IsLittleEndianHost) @@ -407,7 +417,7 @@ return; const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; - char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff; + char *Out = (char *)Buf->getBufferStart() + LinkEditDataCommand.dataoff; assert((LinkEditDataCommand.datasize == LD.Data.size()) && "Incorrect data size"); memcpy(Out, LD.Data.data(), LD.Data.size()); @@ -421,6 +431,11 @@ return writeLinkData(O.DataInCodeCommandIndex, O.DataInCode); } +void MachOWriter::writeLinkerOptimizationHint() { + return writeLinkData(O.LinkerOptimizationHintCommandIndex, + O.LinkerOptimizationHint); +} + void MachOWriter::writeFunctionStartsData() { return writeLinkData(O.FunctionStartsCommandIndex, O.FunctionStarts); } @@ -490,6 +505,16 @@ &MachOWriter::writeDataInCodeData); } + if (O.LinkerOptimizationHintCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.LinkerOptimizationHintCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Queue.emplace_back(LinkEditDataCommand.dataoff, + &MachOWriter::writeLinkerOptimizationHint); + } + if (O.FunctionStartsCommandIndex) { const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*O.FunctionStartsCommandIndex] @@ -511,16 +536,20 @@ Error MachOWriter::finalize() { return LayoutBuilder.layout(); } Error MachOWriter::write() { - if (Error E = B.allocate(totalSize())) - return E; - memset(B.getBufferStart(), 0, totalSize()); + size_t TotalSize = totalSize(); + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(TotalSize) + " bytes"); + memset(Buf->getBufferStart(), 0, totalSize()); writeHeader(); writeLoadCommands(); writeSections(); writeTail(); - return B.commit(); -} -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); +}
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOWriter.h b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOWriter.h index c2c6f5a..c8c06d6 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOWriter.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
@@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// -#include "../Buffer.h" #include "MachOLayoutBuilder.h" #include "MachOObjcopy.h" #include "Object.h" @@ -24,7 +23,8 @@ bool Is64Bit; bool IsLittleEndian; uint64_t PageSize; - Buffer &B; + std::unique_ptr<WritableMemoryBuffer> Buf; + raw_ostream &Out; MachOLayoutBuilder LayoutBuilder; size_t headerSize() const; @@ -48,14 +48,15 @@ void writeLinkData(Optional<size_t> LCIndex, const LinkData &LD); void writeCodeSignatureData(); void writeDataInCodeData(); + void writeLinkerOptimizationHint(); void writeFunctionStartsData(); void writeTail(); public: MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize, - Buffer &B) + raw_ostream &Out) : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), - PageSize(PageSize), B(B), LayoutBuilder(O, Is64Bit, PageSize) {} + PageSize(PageSize), Out(Out), LayoutBuilder(O, Is64Bit, PageSize) {} size_t totalSize() const; Error finalize();
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/Object.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/Object.cpp index cdb9753..b4f98fa 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/Object.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/Object.cpp
@@ -10,9 +10,8 @@ #include "llvm/ADT/SmallPtrSet.h" #include <unordered_set> -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy::macho; const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const { assert(Index < Symbols.size() && "invalid symbol index"); @@ -47,6 +46,9 @@ case MachO::LC_DATA_IN_CODE: DataInCodeCommandIndex = Index; break; + case MachO::LC_LINKER_OPTIMIZATION_HINT: + LinkerOptimizationHintCommandIndex = Index; + break; case MachO::LC_FUNCTION_STARTS: FunctionStartsCommandIndex = Index; break; @@ -190,7 +192,3 @@ return None; } } - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/Object.h b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/Object.h index 0bb4b34..207502e 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/MachO/Object.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MachO/Object.h
@@ -180,6 +180,9 @@ Optional<const Section *> Sec; // True if Info is a scattered_relocation_info. bool Scattered; + // True if the type is an ADDEND. r_symbolnum holds the addend instead of a + // symbol index. + bool IsAddend; // True if the r_symbolnum points to a section number (i.e. r_extern=0). bool Extern; MachO::any_relocation_info Info; @@ -310,6 +313,7 @@ ExportInfo Exports; IndirectSymbolTable IndirectSymTable; LinkData DataInCode; + LinkData LinkerOptimizationHint; LinkData FunctionStarts; LinkData CodeSignature; @@ -325,6 +329,8 @@ Optional<size_t> DySymTabCommandIndex; /// The index LC_DATA_IN_CODE load comamnd if present. Optional<size_t> DataInCodeCommandIndex; + /// The index of LC_LINKER_OPTIMIZATIN_HINT load comamnd if present. + Optional<size_t> LinkerOptimizationHintCommandIndex; /// The index LC_FUNCTION_STARTS load comamnd if present. Optional<size_t> FunctionStartsCommandIndex;
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/MultiFormatConfig.h b/src/llvm-project/llvm/tools/llvm-objcopy/MultiFormatConfig.h new file mode 100644 index 0000000..31d9883 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objcopy/MultiFormatConfig.h
@@ -0,0 +1,37 @@ +//===- MultiFormatConfig.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { + +struct CommonConfig; +struct ELFConfig; +struct COFFConfig; +struct MachOConfig; +struct WasmConfig; + +class MultiFormatConfig { +public: + virtual ~MultiFormatConfig() {} + + virtual const CommonConfig &getCommonConfig() const = 0; + virtual Expected<const ELFConfig &> getELFConfig() const = 0; + virtual Expected<const COFFConfig &> getCOFFConfig() const = 0; + virtual Expected<const MachOConfig &> getMachOConfig() const = 0; + virtual Expected<const WasmConfig &> getWasmConfig() const = 0; +}; + +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/src/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOpts.td index 9e6b6f0..63abbe4 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/src/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -196,19 +196,6 @@ : Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">, MetaVarName<"prefix">; -defm build_id_link_dir - : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and " - "--build-id-link-output to <dir>">, - MetaVarName<"dir">; -defm build_id_link_input - : Eq<"build-id-link-input", "Hard-link the input to <dir>/xx/xxx<suffix> " - "name derived from hex build ID">, - MetaVarName<"suffix">; -defm build_id_link_output - : Eq<"build-id-link-output", "Hard-link the output to <dir>/xx/xxx<suffix> " - "name derived from hex build ID">, - MetaVarName<"suffix">; - defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides " "any previous --change-start or --adjust-start values.">, MetaVarName<"addr">;
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.cpp index 7fd2acd..ad16648 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -6,17 +6,23 @@ // //===----------------------------------------------------------------------===// -#include "Buffer.h" +#include "llvm-objcopy.h" +#include "COFF/COFFConfig.h" #include "COFF/COFFObjcopy.h" -#include "CopyConfig.h" +#include "CommonConfig.h" +#include "ConfigManager.h" +#include "ELF/ELFConfig.h" #include "ELF/ELFObjcopy.h" +#include "MachO/MachOConfig.h" #include "MachO/MachOObjcopy.h" +#include "wasm/WasmConfig.h" #include "wasm/WasmObjcopy.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/Binary.h" @@ -32,6 +38,7 @@ #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" @@ -40,6 +47,7 @@ #include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" @@ -51,13 +59,14 @@ #include <system_error> #include <utility> -namespace llvm { -namespace objcopy { +using namespace llvm; +using namespace llvm::objcopy; +using namespace llvm::object; // The name this program was invoked as. -StringRef ToolName; +static StringRef ToolName; -ErrorSuccess reportWarning(Error E) { +static ErrorSuccess reportWarning(Error E) { assert(E); WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n'; return Error::success(); @@ -72,7 +81,7 @@ // strip-10.exe -> strip // powerpc64-unknown-freebsd13-objcopy -> objcopy // llvm-install-name-tool -> install-name-tool - auto I = Stem.rfind_lower(Tool); + auto I = Stem.rfind_insensitive(Tool); return I != StringRef::npos && (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); }; @@ -87,13 +96,6 @@ return parseObjcopyOptions(Args, reportWarning); } -} // end namespace objcopy -} // end namespace llvm - -using namespace llvm; -using namespace llvm::object; -using namespace llvm::objcopy; - // For regular archives this function simply calls llvm::writeArchive, // For thin archives it writes the archive file itself as well as its members. static Error deepWriteArchive(StringRef ArcName, @@ -108,20 +110,21 @@ return Error::success(); for (const NewArchiveMember &Member : NewMembers) { - // Internally, FileBuffer will use the buffer created by - // FileOutputBuffer::create, for regular files (that is the case for - // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer. + // For regular files (as is the case for deepWriteArchive), + // FileOutputBuffer::create will return OnDiskBuffer. // OnDiskBuffer uses a temporary file and then renames it. So in reality // there is no inefficiency / duplicated in-memory buffers in this case. For // now in-memory buffers can not be completely avoided since // NewArchiveMember still requires them even though writeArchive does not // write them on disk. - FileBuffer FB(Member.MemberName); - if (Error E = FB.allocate(Member.Buf->getBufferSize())) - return E; + Expected<std::unique_ptr<FileOutputBuffer>> FB = + FileOutputBuffer::create(Member.MemberName, Member.Buf->getBufferSize(), + FileOutputBuffer::F_executable); + if (!FB) + return FB.takeError(); std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), - FB.getBufferStart()); - if (Error E = FB.commit()) + (*FB)->getBufferStart()); + if (Error E = (*FB)->commit()) return E; } return Error::success(); @@ -129,18 +132,22 @@ /// The function executeObjcopyOnIHex does the dispatch based on the format /// of the output specified by the command line options. -static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { +static Error executeObjcopyOnIHex(ConfigManager &ConfigMgr, MemoryBuffer &In, + raw_ostream &Out) { // TODO: support output formats other than ELF. - if (Error E = Config.parseELFConfig()) - return E; - return elf::executeObjcopyOnIHex(Config, In, Out); + Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig(); + if (!ELFConfig) + return ELFConfig.takeError(); + + return elf::executeObjcopyOnIHex(ConfigMgr.getCommonConfig(), *ELFConfig, In, + Out); } /// The function executeObjcopyOnRawBinary does the dispatch based on the format /// of the output specified by the command line options. -static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { +static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr, + MemoryBuffer &In, raw_ostream &Out) { + const CommonConfig &Config = ConfigMgr.getCommonConfig(); switch (Config.OutputFormat) { case FileFormat::ELF: // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the @@ -149,9 +156,11 @@ case FileFormat::Binary: case FileFormat::IHex: case FileFormat::Unspecified: - if (Error E = Config.parseELFConfig()) - return E; - return elf::executeObjcopyOnRawBinary(Config, In, Out); + Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig(); + if (!ELFConfig) + return ELFConfig.takeError(); + + return elf::executeObjcopyOnRawBinary(Config, *ELFConfig, In, Out); } llvm_unreachable("unsupported output format"); @@ -159,23 +168,41 @@ /// The function executeObjcopyOnBinary does the dispatch based on the format /// of the input binary (ELF, MachO or COFF). -static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, - Buffer &Out) { +static Error executeObjcopyOnBinary(const MultiFormatConfig &Config, + object::Binary &In, raw_ostream &Out) { if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) { - if (Error E = Config.parseELFConfig()) - return E; - return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); - } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) - return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); - else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) - return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); - else if (auto *MachOUniversalBinary = - dyn_cast<object::MachOUniversalBinary>(&In)) + Expected<const ELFConfig &> ELFConfig = Config.getELFConfig(); + if (!ELFConfig) + return ELFConfig.takeError(); + + return elf::executeObjcopyOnBinary(Config.getCommonConfig(), *ELFConfig, + *ELFBinary, Out); + } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) { + Expected<const COFFConfig &> COFFConfig = Config.getCOFFConfig(); + if (!COFFConfig) + return COFFConfig.takeError(); + + return coff::executeObjcopyOnBinary(Config.getCommonConfig(), *COFFConfig, + *COFFBinary, Out); + } else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) { + Expected<const MachOConfig &> MachOConfig = Config.getMachOConfig(); + if (!MachOConfig) + return MachOConfig.takeError(); + + return macho::executeObjcopyOnBinary(Config.getCommonConfig(), *MachOConfig, + *MachOBinary, Out); + } else if (auto *MachOUniversalBinary = + dyn_cast<object::MachOUniversalBinary>(&In)) { return macho::executeObjcopyOnMachOUniversalBinary( Config, *MachOUniversalBinary, Out); - else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) - return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out); - else + } else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) { + Expected<const WasmConfig &> WasmConfig = Config.getWasmConfig(); + if (!WasmConfig) + return WasmConfig.takeError(); + + return objcopy::wasm::executeObjcopyOnBinary(Config.getCommonConfig(), + *WasmConfig, *WasmBinary, Out); + } else return createStringError(object_error::invalid_file_type, "unsupported object file format"); } @@ -184,7 +211,7 @@ namespace objcopy { Expected<std::vector<NewArchiveMember>> -createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) { +createNewArchiveMembers(const MultiFormatConfig &Config, const Archive &Ar) { std::vector<NewArchiveMember> NewArchiveMembers; Error Err = Error::success(); for (const Archive::Child &Child : Ar.children(Err)) { @@ -197,32 +224,38 @@ return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", ChildOrErr.takeError()); - MemBuffer MB(ChildNameOrErr.get()); - if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) + SmallVector<char, 0> Buffer; + raw_svector_ostream MemStream(Buffer); + + if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MemStream)) return std::move(E); - Expected<NewArchiveMember> Member = - NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); + Expected<NewArchiveMember> Member = NewArchiveMember::getOldMember( + Child, Config.getCommonConfig().DeterministicArchives); if (!Member) return createFileError(Ar.getFileName(), Member.takeError()); - Member->Buf = MB.releaseMemoryBuffer(); + + Member->Buf = std::make_unique<SmallVectorMemoryBuffer>( + std::move(Buffer), ChildNameOrErr.get()); Member->MemberName = Member->Buf->getBufferIdentifier(); NewArchiveMembers.push_back(std::move(*Member)); } if (Err) - return createFileError(Config.InputFilename, std::move(Err)); + return createFileError(Config.getCommonConfig().InputFilename, + std::move(Err)); return std::move(NewArchiveMembers); } } // end namespace objcopy } // end namespace llvm -static Error executeObjcopyOnArchive(CopyConfig &Config, +static Error executeObjcopyOnArchive(const ConfigManager &ConfigMgr, const object::Archive &Ar) { Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr = - createNewArchiveMembers(Config, Ar); + createNewArchiveMembers(ConfigMgr, Ar); if (!NewArchiveMembersOrErr) return NewArchiveMembersOrErr.takeError(); + const CommonConfig &Config = ConfigMgr.getCommonConfig(); return deepWriteArchive(Config.OutputFilename, *NewArchiveMembersOrErr, Ar.hasSymbolTable(), Ar.kind(), Config.DeterministicArchives, Ar.isThin()); @@ -230,8 +263,9 @@ static Error restoreStatOnFile(StringRef Filename, const sys::fs::file_status &Stat, - bool PreserveDates) { + const ConfigManager &ConfigMgr) { int FD; + const CommonConfig &Config = ConfigMgr.getCommonConfig(); // Writing to stdout should not be treated as an error here, just // do not set access/modification times or permissions. @@ -242,7 +276,7 @@ sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) return createFileError(Filename, EC); - if (PreserveDates) + if (Config.PreserveDates) if (auto EC = sys::fs::setLastAccessAndModificationTime( FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) return createFileError(Filename, EC); @@ -250,17 +284,23 @@ sys::fs::file_status OStat; if (std::error_code EC = sys::fs::status(FD, OStat)) return createFileError(Filename, EC); - if (OStat.type() == sys::fs::file_type::regular_file) + if (OStat.type() == sys::fs::file_type::regular_file) { +#ifndef _WIN32 + // Keep ownership if llvm-objcopy is called under root. + if (Config.InputFilename == Config.OutputFilename && OStat.getUser() == 0) + sys::fs::changeFileOwnership(FD, Stat.getUser(), Stat.getGroup()); +#endif + + sys::fs::perms Perm = Stat.permissions(); + if (Config.InputFilename != Config.OutputFilename) + Perm = static_cast<sys::fs::perms>(Perm & ~sys::fs::getUmask() & ~06000); #ifdef _WIN32 - if (auto EC = sys::fs::setPermissions( - Filename, static_cast<sys::fs::perms>(Stat.permissions() & - ~sys::fs::getUmask()))) + if (auto EC = sys::fs::setPermissions(Filename, Perm)) #else - if (auto EC = sys::fs::setPermissions( - FD, static_cast<sys::fs::perms>(Stat.permissions() & - ~sys::fs::getUmask()))) + if (auto EC = sys::fs::setPermissions(FD, Perm)) #endif return createFileError(Filename, EC); + } if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) return createFileError(Filename, EC); @@ -271,7 +311,9 @@ /// The function executeObjcopy does the higher level dispatch based on the type /// of input (raw binary, archive or single object file) and takes care of the /// format-agnostic modifications, i.e. preserving dates. -static Error executeObjcopy(CopyConfig &Config) { +static Error executeObjcopy(ConfigManager &ConfigMgr) { + CommonConfig &Config = ConfigMgr.Common; + sys::fs::file_status Stat; if (Config.InputFilename != "-") { if (auto EC = sys::fs::status(Config.InputFilename, Stat)) @@ -280,61 +322,85 @@ Stat.permissions(static_cast<sys::fs::perms>(0777)); } - using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &); - ProcessRawFn ProcessRaw; - switch (Config.InputFormat) { - case FileFormat::Binary: - ProcessRaw = executeObjcopyOnRawBinary; - break; - case FileFormat::IHex: - ProcessRaw = executeObjcopyOnIHex; - break; - default: - ProcessRaw = nullptr; - } + std::function<Error(raw_ostream & OutFile)> ObjcopyFunc; - if (ProcessRaw) { - auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); + OwningBinary<llvm::object::Binary> BinaryHolder; + std::unique_ptr<MemoryBuffer> MemoryBufferHolder; + + if (Config.InputFormat == FileFormat::Binary || + Config.InputFormat == FileFormat::IHex) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFileOrSTDIN(Config.InputFilename); if (!BufOrErr) return createFileError(Config.InputFilename, BufOrErr.getError()); - FileBuffer FB(Config.OutputFilename); - if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) - return E; + MemoryBufferHolder = std::move(*BufOrErr); + + if (Config.InputFormat == FileFormat::Binary) + ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { + // Handle FileFormat::Binary. + return executeObjcopyOnRawBinary(ConfigMgr, *MemoryBufferHolder, + OutFile); + }; + else + ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { + // Handle FileFormat::IHex. + return executeObjcopyOnIHex(ConfigMgr, *MemoryBufferHolder, OutFile); + }; } else { Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = createBinary(Config.InputFilename); if (!BinaryOrErr) return createFileError(Config.InputFilename, BinaryOrErr.takeError()); + BinaryHolder = std::move(*BinaryOrErr); - if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { - if (Error E = executeObjcopyOnArchive(Config, *Ar)) + if (Archive *Ar = dyn_cast<Archive>(BinaryHolder.getBinary())) { + // Handle Archive. + if (Error E = executeObjcopyOnArchive(ConfigMgr, *Ar)) return E; } else { - FileBuffer FB(Config.OutputFilename); - if (Error E = executeObjcopyOnBinary(Config, - *BinaryOrErr.get().getBinary(), FB)) + // Handle llvm::object::Binary. + ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { + return executeObjcopyOnBinary(ConfigMgr, *BinaryHolder.getBinary(), + OutFile); + }; + } + } + + if (ObjcopyFunc) { + if (Config.SplitDWO.empty()) { + // Apply transformations described by Config and store result into + // Config.OutputFilename using specified ObjcopyFunc function. + if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc)) + return E; + } else { + Config.ExtractDWO = true; + Config.StripDWO = false; + // Copy .dwo tables from the Config.InputFilename into Config.SplitDWO + // file using specified ObjcopyFunc function. + if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc)) + return E; + Config.ExtractDWO = false; + Config.StripDWO = true; + // Apply transformations described by Config, remove .dwo tables and + // store result into Config.OutputFilename using specified ObjcopyFunc + // function. + if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc)) return E; } } - if (Error E = - restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates)) + if (Error E = restoreStatOnFile(Config.OutputFilename, Stat, ConfigMgr)) return E; if (!Config.SplitDWO.empty()) { Stat.permissions(static_cast<sys::fs::perms>(0666)); - if (Error E = - restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates)) + if (Error E = restoreStatOnFile(Config.SplitDWO, Stat, ConfigMgr)) return E; } return Error::success(); } -namespace { - -} // anonymous namespace - int main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0]; @@ -360,8 +426,8 @@ WithColor::error(errs(), ToolName)); return 1; } - for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { - if (Error E = executeObjcopy(CopyConfig)) { + for (ConfigManager &ConfigMgr : DriverConfig->CopyConfigs) { + if (Error E = executeObjcopy(ConfigMgr)) { logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); return 1; }
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.h b/src/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.h index 97a1667..182c95d 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.h
@@ -10,6 +10,7 @@ #define LLVM_TOOLS_OBJCOPY_OBJCOPY_H #include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" namespace llvm { @@ -22,9 +23,10 @@ } // end namespace object namespace objcopy { -struct CopyConfig; +class MultiFormatConfig; Expected<std::vector<NewArchiveMember>> -createNewArchiveMembers(CopyConfig &Config, const object::Archive &Ar); +createNewArchiveMembers(const MultiFormatConfig &Config, + const object::Archive &Ar); } // end namespace objcopy } // end namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmConfig.h b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmConfig.h new file mode 100644 index 0000000..4e40926 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmConfig.h
@@ -0,0 +1,21 @@ +//===- WasmConfig.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H + +namespace llvm { +namespace objcopy { + +// Wasm specific configuration for copying/stripping a single file. +struct WasmConfig {}; + +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp index eb0e563..397d097 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp
@@ -7,18 +7,35 @@ //===----------------------------------------------------------------------===// #include "WasmObjcopy.h" -#include "Buffer.h" -#include "CopyConfig.h" +#include "CommonConfig.h" #include "Object.h" #include "Reader.h" #include "Writer.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/FileOutputBuffer.h" namespace llvm { namespace objcopy { namespace wasm { using namespace object; +using SectionPred = std::function<bool(const Section &Sec)>; + +static bool isDebugSection(const Section &Sec) { + return Sec.Name.startswith(".debug"); +} + +static bool isLinkerSection(const Section &Sec) { + return Sec.Name.startswith("reloc.") || Sec.Name == "linking"; +} + +static bool isNameSection(const Section &Sec) { return Sec.Name == "name"; } + +// Sections which are known to be "comments" or informational and do not affect +// program semantics. +static bool isCommentSection(const Section &Sec) { + return Sec.Name == "producers"; +} static Error dumpSectionToFile(StringRef SecName, StringRef Filename, Object &Obj) { @@ -39,7 +56,60 @@ return createStringError(errc::invalid_argument, "section '%s' not found", SecName.str().c_str()); } -static Error handleArgs(const CopyConfig &Config, Object &Obj) { + +static void removeSections(const CommonConfig &Config, Object &Obj) { + SectionPred RemovePred = [](const Section &) { return false; }; + + // Explicitly-requested sections. + if (!Config.ToRemove.empty()) { + RemovePred = [&Config](const Section &Sec) { + return Config.ToRemove.matches(Sec.Name); + }; + } + + if (Config.StripDebug) { + RemovePred = [RemovePred](const Section &Sec) { + return RemovePred(Sec) || isDebugSection(Sec); + }; + } + + if (Config.StripAll) { + RemovePred = [RemovePred](const Section &Sec) { + return RemovePred(Sec) || isDebugSection(Sec) || isLinkerSection(Sec) || + isNameSection(Sec) || isCommentSection(Sec); + }; + } + + if (Config.OnlyKeepDebug) { + RemovePred = [&Config](const Section &Sec) { + // Keep debug sections, unless explicitly requested to remove. + // Remove everything else, including known sections. + return Config.ToRemove.matches(Sec.Name) || !isDebugSection(Sec); + }; + } + + if (!Config.OnlySection.empty()) { + RemovePred = [&Config](const Section &Sec) { + // Explicitly keep these sections regardless of previous removes. + // Remove everything else, inluding known sections. + return !Config.OnlySection.matches(Sec.Name); + }; + } + + if (!Config.KeepSection.empty()) { + RemovePred = [&Config, RemovePred](const Section &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (Config.KeepSection.matches(Sec.Name)) + return false; + // Otherwise defer to RemovePred. + return RemovePred(Sec); + }; + } + + Obj.removeSections(RemovePred); +} + +static Error handleArgs(const CommonConfig &Config, Object &Obj) { // Only support AddSection, DumpSection, RemoveSection for now. for (StringRef Flag : Config.DumpSection) { StringRef SecName; @@ -49,11 +119,7 @@ return createFileError(FileName, std::move(E)); } - Obj.removeSections([&Config](const Section &Sec) { - if (Config.ToRemove.matches(Sec.Name)) - return true; - return false; - }); + removeSections(Config, Obj); for (StringRef Flag : Config.AddSection) { StringRef SecName, FileName; @@ -72,28 +138,11 @@ Obj.addSectionWithOwnedContents(Sec, std::move(Buf)); } - if (!Config.AddGnuDebugLink.empty() || !Config.BuildIdLinkDir.empty() || - Config.BuildIdLinkInput || Config.BuildIdLinkOutput || - Config.ExtractPartition || !Config.SplitDWO.empty() || - !Config.SymbolsPrefix.empty() || !Config.AllocSectionsPrefix.empty() || - Config.DiscardMode != DiscardType::None || Config.NewSymbolVisibility || - !Config.SymbolsToAdd.empty() || !Config.RPathToAdd.empty() || - !Config.OnlySection.empty() || !Config.SymbolsToGlobalize.empty() || - !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || - !Config.SymbolsToRemove.empty() || - !Config.UnneededSymbolsToRemove.empty() || - !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || - !Config.SectionsToRename.empty() || !Config.SetSectionAlignment.empty() || - !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty()) { - return createStringError( - llvm::errc::invalid_argument, - "only add-section, dump-section, and remove-section are supported"); - } return Error::success(); } -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::WasmObjectFile &In, Buffer &Out) { +Error executeObjcopyOnBinary(const CommonConfig &Config, const WasmConfig &, + object::WasmObjectFile &In, raw_ostream &Out) { Reader TheReader(In); Expected<std::unique_ptr<Object>> ObjOrErr = TheReader.create(); if (!ObjOrErr)
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h index 3557d5c..28268e3 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h
@@ -11,18 +11,19 @@ namespace llvm { class Error; +class raw_ostream; namespace object { class WasmObjectFile; } // end namespace object namespace objcopy { -struct CopyConfig; -class Buffer; +struct CommonConfig; +struct WasmConfig; namespace wasm { -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::WasmObjectFile &In, Buffer &Out); +Error executeObjcopyOnBinary(const CommonConfig &Config, const WasmConfig &, + object::WasmObjectFile &In, raw_ostream &Out); } // end namespace wasm } // end namespace objcopy
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/wasm/Writer.cpp b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/Writer.cpp index 50d2650..2fad9e6 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/wasm/Writer.cpp +++ b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/Writer.cpp
@@ -9,6 +9,7 @@ #include "Writer.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" @@ -54,23 +55,23 @@ } Error Writer::write() { - size_t FileSize = finalize(); - if (Error E = Buf.allocate(FileSize)) - return E; + size_t TotalSize = finalize(); + Out.reserveExtraSpace(TotalSize); // Write the header. - uint8_t *Ptr = Buf.getBufferStart(); - Ptr = std::copy(Obj.Header.Magic.begin(), Obj.Header.Magic.end(), Ptr); - support::endian::write32le(Ptr, Obj.Header.Version); - Ptr += sizeof(Obj.Header.Version); + Out.write(Obj.Header.Magic.data(), Obj.Header.Magic.size()); + uint32_t Version; + support::endian::write32le(&Version, Obj.Header.Version); + Out.write(reinterpret_cast<const char *>(&Version), sizeof(Version)); // Write each section. for (size_t I = 0, S = SectionHeaders.size(); I < S; ++I) { - Ptr = std::copy(SectionHeaders[I].begin(), SectionHeaders[I].end(), Ptr); - ArrayRef<uint8_t> Contents = Obj.Sections[I].Contents; - Ptr = std::copy(Contents.begin(), Contents.end(), Ptr); + Out.write(SectionHeaders[I].data(), SectionHeaders[I].size()); + Out.write(reinterpret_cast<const char *>(Obj.Sections[I].Contents.data()), + Obj.Sections[I].Contents.size()); } - return Buf.commit(); + + return Error::success(); } } // end namespace wasm
diff --git a/src/llvm-project/llvm/tools/llvm-objcopy/wasm/Writer.h b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/Writer.h index da48ee7..4404cd8 100644 --- a/src/llvm-project/llvm/tools/llvm-objcopy/wasm/Writer.h +++ b/src/llvm-project/llvm/tools/llvm-objcopy/wasm/Writer.h
@@ -9,7 +9,6 @@ #ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H #define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H -#include "Buffer.h" #include "Object.h" #include <cstdint> #include <vector> @@ -20,13 +19,13 @@ class Writer { public: - Writer(Object &Obj, Buffer &Buf) : Obj(Obj), Buf(Buf) {} + Writer(Object &Obj, raw_ostream &Out) : Obj(Obj), Out(Out) {} Error write(); private: using SectionHeader = SmallVector<char, 8>; Object &Obj; - Buffer &Buf; + raw_ostream &Out; std::vector<SectionHeader> SectionHeaders; /// Generate a wasm section section header for S.
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-objdump/CMakeLists.txt index 91c28dd..149bb3d 100644 --- a/src/llvm-project/llvm/tools/llvm-objdump/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-objdump/CMakeLists.txt
@@ -3,30 +3,44 @@ AllTargetsDisassemblers AllTargetsInfos BinaryFormat - CodeGen DebugInfoDWARF DebugInfoPDB Demangle MC MCDisassembler Object + Option Support Symbolize ) +set(LLVM_TARGET_DEFINITIONS ObjdumpOpts.td) +tablegen(LLVM ObjdumpOpts.inc -gen-opt-parser-defs) +add_public_tablegen_target(ObjdumpOptsTableGen) + +set(LLVM_TARGET_DEFINITIONS OtoolOpts.td) +tablegen(LLVM OtoolOpts.inc -gen-opt-parser-defs) +add_public_tablegen_target(OtoolOptsTableGen) + add_llvm_tool(llvm-objdump llvm-objdump.cpp + SourcePrinter.cpp COFFDump.cpp ELFDump.cpp MachODump.cpp WasmDump.cpp XCOFFDump.cpp + DEPENDS + ObjdumpOptsTableGen + OtoolOptsTableGen ) -if(HAVE_LIBXAR) +if(LLVM_HAVE_LIBXAR) target_link_libraries(llvm-objdump PRIVATE ${XAR_LIB}) endif() +add_llvm_tool_symlink(llvm-otool llvm-objdump) + if(LLVM_INSTALL_BINUTILS_SYMLINKS) add_llvm_tool_symlink(objdump llvm-objdump) endif()
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/COFFDump.cpp b/src/llvm-project/llvm/tools/llvm-objdump/COFFDump.cpp index b9d69d6..09a9001 100644 --- a/src/llvm-project/llvm/tools/llvm-objdump/COFFDump.cpp +++ b/src/llvm-project/llvm/tools/llvm-objdump/COFFDump.cpp
@@ -454,8 +454,7 @@ continue; const coff_section *Pdata = Obj->getCOFFSection(Section); - for (const RelocationRef &Reloc : Section.relocations()) - Rels.push_back(Reloc); + append_range(Rels, Section.relocations()); // Sort relocations by address. llvm::sort(Rels, isRelocAddressLess);
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/ELFDump.cpp b/src/llvm-project/llvm/tools/llvm-objdump/ELFDump.cpp index 1c4d591..da74158 100644 --- a/src/llvm-project/llvm/tools/llvm-objdump/ELFDump.cpp +++ b/src/llvm-project/llvm/tools/llvm-objdump/ELFDump.cpp
@@ -74,7 +74,10 @@ const typename ELFT::Rela *ERela = Obj->getRela(Rel); Addend = ERela->r_addend; Undef = ERela->getSymbol(false) == 0; - } else if ((*SecOrErr)->sh_type != ELF::SHT_REL) { + } else if ((*SecOrErr)->sh_type == ELF::SHT_REL) { + const typename ELFT::Rel *ERel = Obj->getRel(Rel); + Undef = ERel->getSymbol(false) == 0; + } else { return make_error<BinaryError>(); } @@ -177,7 +180,7 @@ MaxLen = std::max(MaxLen, Elf.getDynamicTagAsString(Dyn.d_tag).size()); std::string TagFmt = " %-" + std::to_string(MaxLen) + "s "; - outs() << "Dynamic Section:\n"; + outs() << "\nDynamic Section:\n"; for (const typename ELFT::Dyn &Dyn : DynamicEntries) { if (Dyn.d_tag == ELF::DT_NULL) continue; @@ -205,7 +208,7 @@ template <class ELFT> static void printProgramHeaders(const ELFFile<ELFT> &Obj, StringRef FileName) { - outs() << "Program Header:\n"; + outs() << "\nProgram Header:\n"; auto ProgramHeaderOrError = Obj.program_headers(); if (!ProgramHeaderOrError) { reportWarning("unable to read program headers: " + @@ -272,13 +275,12 @@ << ((Phdr.p_flags & ELF::PF_W) ? "w" : "-") << ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n"; } - outs() << "\n"; } template <class ELFT> static void printSymbolVersionDependency(ArrayRef<uint8_t> Contents, StringRef StrTab) { - outs() << "Version References:\n"; + outs() << "\nVersion References:\n"; const uint8_t *Buf = Contents.data(); while (Buf) { @@ -304,7 +306,7 @@ static void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr, ArrayRef<uint8_t> Contents, StringRef StrTab) { - outs() << "Version definitions:\n"; + outs() << "\nVersion definitions:\n"; const uint8_t *Buf = Contents.data(); uint32_t VerdefIndex = 1;
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/MachODump.cpp b/src/llvm-project/llvm/tools/llvm-objdump/MachODump.cpp index 51212d5..7c1fdf0 100644 --- a/src/llvm-project/llvm/tools/llvm-objdump/MachODump.cpp +++ b/src/llvm-project/llvm/tools/llvm-objdump/MachODump.cpp
@@ -12,6 +12,7 @@ #include "MachODump.h" +#include "ObjdumpOptID.h" #include "llvm-objdump.h" #include "llvm-c/Disassembler.h" #include "llvm/ADT/STLExtras.h" @@ -34,8 +35,8 @@ #include "llvm/MC/MCTargetOptions.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" +#include "llvm/Option/ArgList.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" @@ -52,7 +53,7 @@ #include <cstring> #include <system_error> -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR extern "C" { #include <xar/xar.h> } @@ -62,125 +63,63 @@ using namespace llvm::object; using namespace llvm::objdump; -cl::OptionCategory objdump::MachOCat("llvm-objdump MachO Specific Options"); - -cl::opt<bool> objdump::FirstPrivateHeader( - "private-header", - cl::desc("Display only the first format specific file header"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::ExportsTrie("exports-trie", - cl::desc("Display mach-o exported symbols"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::Rebase("rebase", - cl::desc("Display mach-o rebasing info"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::Bind("bind", cl::desc("Display mach-o binding info"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::LazyBind("lazy-bind", - cl::desc("Display mach-o lazy binding info"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::WeakBind("weak-bind", - cl::desc("Display mach-o weak binding info"), - cl::cat(MachOCat)); - -static cl::opt<bool> - UseDbg("g", cl::Grouping, - cl::desc("Print line information from debug info if available"), - cl::cat(MachOCat)); - -static cl::opt<std::string> DSYMFile("dsym", - cl::desc("Use .dSYM file for debug info"), - cl::cat(MachOCat)); - -static cl::opt<bool> FullLeadingAddr("full-leading-addr", - cl::desc("Print full leading address"), - cl::cat(MachOCat)); - -static cl::opt<bool> NoLeadingHeaders("no-leading-headers", - cl::desc("Print no leading headers"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::UniversalHeaders( - "universal-headers", - cl::desc("Print Mach-O universal headers (requires --macho)"), - cl::cat(MachOCat)); - -static cl::opt<bool> ArchiveMemberOffsets( - "archive-member-offsets", - cl::desc("Print the offset to each archive member for Mach-O archives " - "(requires --macho and --archive-headers)"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::IndirectSymbols( - "indirect-symbols", - cl::desc( - "Print indirect symbol table for Mach-O objects (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::DataInCode( - "data-in-code", - cl::desc( - "Print the data in code table for Mach-O objects (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> - objdump::LinkOptHints("link-opt-hints", - cl::desc("Print the linker optimization hints for " - "Mach-O objects (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> - objdump::InfoPlist("info-plist", - cl::desc("Print the info plist section as strings for " - "Mach-O objects (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> - objdump::DylibsUsed("dylibs-used", - cl::desc("Print the shared libraries used for linked " - "Mach-O files (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::DylibId("dylib-id", - cl::desc("Print the shared library's id for the " - "dylib Mach-O file (requires --macho)"), - cl::cat(MachOCat)); - -static cl::opt<bool> - NonVerbose("non-verbose", - cl::desc("Print the info for Mach-O objects in non-verbose or " - "numeric form (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> - objdump::ObjcMetaData("objc-meta-data", - cl::desc("Print the Objective-C runtime meta data " - "for Mach-O files (requires --macho)"), - cl::cat(MachOCat)); - -static cl::opt<std::string> DisSymName( - "dis-symname", - cl::desc("disassemble just this symbol's instructions (requires --macho)"), - cl::cat(MachOCat)); - -static cl::opt<bool> NoSymbolicOperands( - "no-symbolic-operands", - cl::desc("do not symbolic operands when disassembling (requires --macho)"), - cl::cat(MachOCat)); - -static cl::list<std::string> - ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), - cl::ZeroOrMore, cl::cat(MachOCat)); +bool objdump::FirstPrivateHeader; +bool objdump::ExportsTrie; +bool objdump::Rebase; +bool objdump::Rpaths; +bool objdump::Bind; +bool objdump::LazyBind; +bool objdump::WeakBind; +static bool UseDbg; +static std::string DSYMFile; +bool objdump::FullLeadingAddr; +bool objdump::LeadingHeaders; +bool objdump::UniversalHeaders; +static bool ArchiveMemberOffsets; +bool objdump::IndirectSymbols; +bool objdump::DataInCode; +bool objdump::FunctionStarts; +bool objdump::LinkOptHints; +bool objdump::InfoPlist; +bool objdump::DylibsUsed; +bool objdump::DylibId; +bool objdump::Verbose; +bool objdump::ObjcMetaData; +std::string objdump::DisSymName; +bool objdump::SymbolicOperands; +static std::vector<std::string> ArchFlags; static bool ArchAll = false; - static std::string ThumbTripleName; +void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) { + FirstPrivateHeader = InputArgs.hasArg(OBJDUMP_private_header); + ExportsTrie = InputArgs.hasArg(OBJDUMP_exports_trie); + Rebase = InputArgs.hasArg(OBJDUMP_rebase); + Rpaths = InputArgs.hasArg(OBJDUMP_rpaths); + Bind = InputArgs.hasArg(OBJDUMP_bind); + LazyBind = InputArgs.hasArg(OBJDUMP_lazy_bind); + WeakBind = InputArgs.hasArg(OBJDUMP_weak_bind); + UseDbg = InputArgs.hasArg(OBJDUMP_g); + DSYMFile = InputArgs.getLastArgValue(OBJDUMP_dsym_EQ).str(); + FullLeadingAddr = InputArgs.hasArg(OBJDUMP_full_leading_addr); + LeadingHeaders = !InputArgs.hasArg(OBJDUMP_no_leading_headers); + UniversalHeaders = InputArgs.hasArg(OBJDUMP_universal_headers); + ArchiveMemberOffsets = InputArgs.hasArg(OBJDUMP_archive_member_offsets); + IndirectSymbols = InputArgs.hasArg(OBJDUMP_indirect_symbols); + DataInCode = InputArgs.hasArg(OBJDUMP_data_in_code); + FunctionStarts = InputArgs.hasArg(OBJDUMP_function_starts); + LinkOptHints = InputArgs.hasArg(OBJDUMP_link_opt_hints); + InfoPlist = InputArgs.hasArg(OBJDUMP_info_plist); + DylibsUsed = InputArgs.hasArg(OBJDUMP_dylibs_used); + DylibId = InputArgs.hasArg(OBJDUMP_dylib_id); + Verbose = !InputArgs.hasArg(OBJDUMP_non_verbose); + ObjcMetaData = InputArgs.hasArg(OBJDUMP_objc_meta_data); + DisSymName = InputArgs.getLastArgValue(OBJDUMP_dis_symname).str(); + SymbolicOperands = !InputArgs.hasArg(OBJDUMP_no_symbolic_operands); + ArchFlags = InputArgs.getAllArgValues(OBJDUMP_arch_EQ); +} + static const Target *GetTarget(const MachOObjectFile *MachOObj, const char **McpuDefault, const Target **ThumbTarget) { @@ -245,7 +184,7 @@ typedef std::vector<DiceTableEntry> DiceTable; typedef DiceTable::iterator dice_table_iterator; -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR namespace { struct ScopedXarFile { xar_t xar; @@ -272,7 +211,7 @@ operator xar_iter_t() { return iter; } }; } // namespace -#endif // defined(HAVE_LIBXAR) +#endif // defined(LLVM_HAVE_LIBXAR) // This is used to search for a data in code table entry for the PC being // disassembled. The j parameter has the PC in j.first. A single data in code @@ -296,19 +235,19 @@ default: case MachO::DICE_KIND_DATA: if (Length >= 4) { - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; Size = 4; } else if (Length >= 2) { - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << Value; Size = 2; } else { - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[0]; outs() << "\t.byte " << Value; @@ -320,14 +259,14 @@ outs() << "\t@ data in code kind = " << Kind << "\n"; break; case MachO::DICE_KIND_JUMP_TABLE8: - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 1), outs()); Value = bytes[0]; outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n"; Size = 1; break; case MachO::DICE_KIND_JUMP_TABLE16: - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << format("%5u", Value & 0xffff) @@ -336,7 +275,7 @@ break; case MachO::DICE_KIND_JUMP_TABLE32: case MachO::DICE_KIND_ABS_JUMP_TABLE32: - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; @@ -362,8 +301,7 @@ Symbols.push_back(Symbol); } - for (const SectionRef &Section : MachOObj->sections()) - Sections.push_back(Section); + append_range(Sections, MachOObj->sections()); bool BaseSegmentAddressSet = false; for (const auto &Command : MachOObj->load_commands()) { @@ -461,7 +399,7 @@ if (isExtern) { symbol_iterator SI = O->symbol_begin(); - advance(SI, Val); + std::advance(SI, Val); S = unwrapOrError(SI->getName(), FileName); } else { section_iterator SI = O->section_begin(); @@ -473,7 +411,7 @@ uint32_t I = Val - 1; while (I != 0 && SI != O->section_end()) { --I; - advance(SI, 1); + std::advance(SI, 1); } if (SI == O->section_end()) { Fmt << Val << " (?,?)"; @@ -1104,6 +1042,43 @@ } } +static void PrintFunctionStarts(MachOObjectFile *O) { + uint64_t BaseSegmentAddress = 0; + for (const MachOObjectFile::LoadCommandInfo &Command : O->load_commands()) { + if (Command.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command SLC = O->getSegmentLoadCommand(Command); + if (StringRef(SLC.segname) == "__TEXT") { + BaseSegmentAddress = SLC.vmaddr; + break; + } + } else if (Command.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 SLC = O->getSegment64LoadCommand(Command); + if (StringRef(SLC.segname) == "__TEXT") { + BaseSegmentAddress = SLC.vmaddr; + break; + } + } + } + + SmallVector<uint64_t, 8> FunctionStarts; + for (const MachOObjectFile::LoadCommandInfo &LC : O->load_commands()) { + if (LC.C.cmd == MachO::LC_FUNCTION_STARTS) { + MachO::linkedit_data_command FunctionStartsLC = + O->getLinkeditDataLoadCommand(LC); + O->ReadULEB128s(FunctionStartsLC.dataoff, FunctionStarts); + break; + } + } + + for (uint64_t S : FunctionStarts) { + uint64_t Addr = BaseSegmentAddress + S; + if (O->is64Bit()) + outs() << format("%016" PRIx64, Addr) << "\n"; + else + outs() << format("%08" PRIx32, static_cast<uint32_t>(Addr)) << "\n"; + } +} + static void PrintDataInCodeTable(MachOObjectFile *O, bool verbose) { MachO::linkedit_data_command DIC = O->getDataInCodeLoadCommand(); uint32_t nentries = DIC.datasize / sizeof(struct MachO::data_in_code_entry); @@ -1260,6 +1235,16 @@ } } +static void printRpaths(MachOObjectFile *O) { + for (const auto &Command : O->load_commands()) { + if (Command.C.cmd == MachO::LC_RPATH) { + auto Rpath = O->getRpathCommand(Command); + const char *P = (const char *)(Command.Ptr) + Rpath.path; + outs() << P << "\n"; + } + } +} + typedef DenseMap<uint64_t, StringRef> SymbolAddressMap; static void CreateSymbolAddressMap(MachOObjectFile *O, @@ -1712,12 +1697,12 @@ StringRef DisSegName, StringRef DisSectName); static void DumpProtocolSection(MachOObjectFile *O, const char *sect, uint32_t size, uint32_t addr); -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, uint32_t size, bool verbose, bool PrintXarHeader, bool PrintXarFileHeaders, std::string XarMemberName); -#endif // defined(HAVE_LIBXAR) +#endif // defined(LLVM_HAVE_LIBXAR) static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, bool verbose) { @@ -1770,7 +1755,7 @@ uint32_t sect_size = BytesStr.size(); uint64_t sect_addr = Section.getAddress(); - if (!NoLeadingHeaders) + if (LeadingHeaders) outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; @@ -1788,13 +1773,13 @@ DumpProtocolSection(O, sect, sect_size, sect_addr); continue; } -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR if (SegName == "__LLVM" && SectName == "__bundle") { - DumpBitcodeSection(O, sect, sect_size, verbose, !NoSymbolicOperands, + DumpBitcodeSection(O, sect, sect_size, verbose, SymbolicOperands, ArchiveHeaders, ""); continue; } -#endif // defined(HAVE_LIBXAR) +#endif // defined(LLVM_HAVE_LIBXAR) switch (section_type) { case MachO::S_REGULAR: DumpRawSectionContents(O, sect, sect_size, sect_addr); @@ -1803,20 +1788,20 @@ outs() << "zerofill section and has no contents in the file\n"; break; case MachO::S_CSTRING_LITERALS: - DumpCstringSection(O, sect, sect_size, sect_addr, !NoLeadingAddr); + DumpCstringSection(O, sect, sect_size, sect_addr, LeadingAddr); break; case MachO::S_4BYTE_LITERALS: - DumpLiteral4Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + DumpLiteral4Section(O, sect, sect_size, sect_addr, LeadingAddr); break; case MachO::S_8BYTE_LITERALS: - DumpLiteral8Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + DumpLiteral8Section(O, sect, sect_size, sect_addr, LeadingAddr); break; case MachO::S_16BYTE_LITERALS: - DumpLiteral16Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + DumpLiteral16Section(O, sect, sect_size, sect_addr, LeadingAddr); break; case MachO::S_LITERAL_POINTERS: DumpLiteralPointerSection(O, Section, sect, sect_size, sect_addr, - !NoLeadingAddr); + LeadingAddr); break; case MachO::S_MOD_INIT_FUNC_POINTERS: case MachO::S_MOD_TERM_FUNC_POINTERS: @@ -1853,7 +1838,7 @@ DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); if (SegName == "__TEXT" && SectName == "__info_plist") { - if (!NoLeadingHeaders) + if (LeadingHeaders) outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; StringRef BytesStr = unwrapOrError(Section.getContents(), O->getFileName()); @@ -1911,9 +1896,9 @@ // UniversalHeaders or ArchiveHeaders. if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase || Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols || - DataInCode || LinkOptHints || DylibsUsed || DylibId || ObjcMetaData || - (!FilterSections.empty())) { - if (!NoLeadingHeaders) { + DataInCode || FunctionStarts || LinkOptHints || DylibsUsed || DylibId || + Rpaths || ObjcMetaData || (!FilterSections.empty())) { + if (LeadingHeaders) { outs() << Name; if (!ArchiveMemberName.empty()) outs() << '(' << ArchiveMemberName << ')'; @@ -1964,19 +1949,21 @@ DisassembleMachO(FileName, MachOOF, "__TEXT", "__text"); } if (IndirectSymbols) - PrintIndirectSymbols(MachOOF, !NonVerbose); + PrintIndirectSymbols(MachOOF, Verbose); if (DataInCode) - PrintDataInCodeTable(MachOOF, !NonVerbose); + PrintDataInCodeTable(MachOOF, Verbose); + if (FunctionStarts) + PrintFunctionStarts(MachOOF); if (LinkOptHints) PrintLinkOptHints(MachOOF); if (Relocations) - PrintRelocations(MachOOF, !NonVerbose); + PrintRelocations(MachOOF, Verbose); if (SectionHeaders) printSectionHeaders(MachOOF); if (SectionContents) printSectionContents(MachOOF); if (!FilterSections.empty()) - DumpSectionContents(FileName, MachOOF, !NonVerbose); + DumpSectionContents(FileName, MachOOF, Verbose); if (InfoPlist) DumpInfoPlistSectionContents(FileName, MachOOF); if (DylibsUsed) @@ -1994,11 +1981,13 @@ if (FirstPrivateHeader) printMachOFileHeader(MachOOF); if (ObjcMetaData) - printObjcMetaData(MachOOF, !NonVerbose); + printObjcMetaData(MachOOF, Verbose); if (ExportsTrie) printExportsTrie(MachOOF); if (Rebase) printRebaseTable(MachOOF); + if (Rpaths) + printRpaths(MachOOF); if (Bind) printBindTable(MachOOF); if (LazyBind) @@ -2333,7 +2322,7 @@ if (Archive *A = dyn_cast<Archive>(&Bin)) { outs() << "Archive : " << Filename << "\n"; if (ArchiveHeaders) - printArchiveHeaders(Filename, A, !NonVerbose, ArchiveMemberOffsets); + printArchiveHeaders(Filename, A, Verbose, ArchiveMemberOffsets); Error Err = Error::success(); unsigned I = -1; @@ -2380,7 +2369,7 @@ auto Filename = UB->getFileName(); if (UniversalHeaders) - printMachOUniversalHeaders(UB, !NonVerbose); + printMachOUniversalHeaders(UB, Verbose); // If we have a list of architecture flags specified dump only those. if (!ArchAll && !ArchFlags.empty()) { @@ -2414,7 +2403,7 @@ outs() << " (architecture " << ArchitectureName << ")"; outs() << "\n"; if (ArchiveHeaders) - printArchiveHeaders(Filename, A.get(), !NonVerbose, + printArchiveHeaders(Filename, A.get(), Verbose, ArchiveMemberOffsets, ArchitectureName); Error Err = Error::success(); unsigned I = -1; @@ -2475,7 +2464,7 @@ std::unique_ptr<Archive> &A = *AOrErr; outs() << "Archive : " << Filename << "\n"; if (ArchiveHeaders) - printArchiveHeaders(Filename, A.get(), !NonVerbose, + printArchiveHeaders(Filename, A.get(), Verbose, ArchiveMemberOffsets); Error Err = Error::success(); unsigned I = -1; @@ -2528,8 +2517,8 @@ outs() << " (architecture " << ArchitectureName << ")"; outs() << "\n"; if (ArchiveHeaders) - printArchiveHeaders(Filename, A.get(), !NonVerbose, - ArchiveMemberOffsets, ArchitectureName); + printArchiveHeaders(Filename, A.get(), Verbose, ArchiveMemberOffsets, + ArchitectureName); Error Err = Error::success(); unsigned I = -1; for (auto &C : A->children(Err)) { @@ -6115,8 +6104,7 @@ CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) - Sections.push_back(Section); + append_range(Sections, O->sections()); struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); @@ -6197,8 +6185,7 @@ CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) - Sections.push_back(Section); + append_range(Sections, O->sections()); struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); @@ -6292,8 +6279,7 @@ CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) - Sections.push_back(Section); + append_range(Sections, O->sections()); struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); @@ -6450,8 +6436,7 @@ CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) - Sections.push_back(Section); + append_range(Sections, O->sections()); struct DisassembleInfo info(O, &AddrMap, &Sections, true); @@ -6475,7 +6460,7 @@ } } -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR static inline void swapStruct(struct xar_header &xar) { sys::swapByteOrder(xar.magic); sys::swapByteOrder(xar.size); @@ -6838,7 +6823,7 @@ } } } -#endif // defined(HAVE_LIBXAR) +#endif // defined(LLVM_HAVE_LIBXAR) static void printObjcMetaData(MachOObjectFile *O, bool verbose) { if (O->is64Bit()) @@ -7165,17 +7150,15 @@ // Get the default information for printing a comment. StringRef CommentBegin = MAI.getCommentString(); unsigned CommentColumn = MAI.getCommentColumn(); - bool IsFirst = true; + ListSeparator LS("\n"); while (!Comments.empty()) { - if (!IsFirst) - FormattedOS << '\n'; + FormattedOS << LS; // Emit a line of comments. FormattedOS.PadToColumn(CommentColumn); size_t Position = Comments.find('\n'); FormattedOS << CommentBegin << ' ' << Comments.substr(0, Position); // Move after the newline character. Comments = Comments.substr(Position + 1); - IsFirst = false; } FormattedOS.flush(); @@ -7245,7 +7228,7 @@ std::unique_ptr<const MCSubtargetInfo> STI( TheTarget->createMCSubtargetInfo(TripleName, MachOMCPU, FeaturesStr)); CHECK_TARGET_INFO_CREATION(STI); - MCContext Ctx(AsmInfo.get(), MRI.get(), nullptr); + MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get()); std::unique_ptr<MCDisassembler> DisAsm( TheTarget->createMCDisassembler(*STI, Ctx)); CHECK_TARGET_INFO_CREATION(DisAsm); @@ -7295,7 +7278,8 @@ ThumbTarget->createMCSubtargetInfo(ThumbTripleName, MachOMCPU, FeaturesStr)); CHECK_THUMB_TARGET_INFO_CREATION(ThumbSTI); - ThumbCtx.reset(new MCContext(ThumbAsmInfo.get(), ThumbMRI.get(), nullptr)); + ThumbCtx.reset(new MCContext(Triple(ThumbTripleName), ThumbAsmInfo.get(), + ThumbMRI.get(), ThumbSTI.get())); ThumbDisAsm.reset(ThumbTarget->createMCDisassembler(*ThumbSTI, *ThumbCtx)); CHECK_THUMB_TARGET_INFO_CREATION(ThumbDisAsm); MCContext *PtrThumbCtx = ThumbCtx.get(); @@ -7334,7 +7318,7 @@ BaseSegmentAddress); // Sort the symbols by address, just in case they didn't come in that way. - llvm::sort(Symbols, SymbolSorter()); + llvm::stable_sort(Symbols, SymbolSorter()); // Build a data in code table that is sorted on by the address of each entry. uint64_t BaseAddress = 0; @@ -7489,13 +7473,13 @@ return; } // Set up the block of info used by the Symbolizer call backs. - SymbolizerInfo.verbose = !NoSymbolicOperands; + SymbolizerInfo.verbose = SymbolicOperands; SymbolizerInfo.O = MachOOF; SymbolizerInfo.S = Sections[SectIdx]; SymbolizerInfo.AddrMap = &AddrMap; SymbolizerInfo.Sections = &Sections; // Same for the ThumbSymbolizer - ThumbSymbolizerInfo.verbose = !NoSymbolicOperands; + ThumbSymbolizerInfo.verbose = SymbolicOperands; ThumbSymbolizerInfo.O = MachOOF; ThumbSymbolizerInfo.S = Sections[SectIdx]; ThumbSymbolizerInfo.AddrMap = &AddrMap; @@ -7620,7 +7604,7 @@ outs() << SymName << ":\n"; uint64_t PC = SectAddress + Index; - if (!NoLeadingAddr) { + if (LeadingAddr) { if (FullLeadingAddr) { if (MachOOF->is64Bit()) outs() << format("%016" PRIx64, PC); @@ -7630,7 +7614,7 @@ outs() << format("%8" PRIx64 ":", PC); } } - if (!NoShowRawInsn || Arch == Triple::arm) + if (ShowRawInsn || Arch == Triple::arm) outs() << "\t"; if (DumpAndSkipDataInCode(PC, Bytes.data() + Index, Dices, Size)) @@ -7647,7 +7631,7 @@ gotInst = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), PC, Annotations); if (gotInst) { - if (!NoShowRawInsn || Arch == Triple::arm) { + if (ShowRawInsn || Arch == Triple::arm) { dumpBytes(makeArrayRef(Bytes.data() + Index, Size), outs()); } formatted_raw_ostream FormattedOS(outs()); @@ -7717,7 +7701,7 @@ raw_svector_ostream Annotations(AnnotationsBytes); if (DisAsm->getInstruction(Inst, InstSize, Bytes.slice(Index), PC, Annotations)) { - if (!NoLeadingAddr) { + if (LeadingAddr) { if (FullLeadingAddr) { if (MachOOF->is64Bit()) outs() << format("%016" PRIx64, PC); @@ -7727,7 +7711,7 @@ outs() << format("%8" PRIx64 ":", PC); } } - if (!NoShowRawInsn || Arch == Triple::arm) { + if (ShowRawInsn || Arch == Triple::arm) { outs() << "\t"; dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs()); } @@ -8012,12 +7996,23 @@ (void)Kind; assert(Kind == 3 && "kind for a compressed 2nd level index should be 3"); + uint32_t NumCommonEncodings = CommonEncodings.size(); uint16_t EntriesStart = readNext<uint16_t>(PageData, Pos); uint16_t NumEntries = readNext<uint16_t>(PageData, Pos); - uint16_t EncodingsStart = readNext<uint16_t>(PageData, Pos); - readNext<uint16_t>(PageData, Pos); - StringRef PageEncodings = PageData.substr(EncodingsStart, StringRef::npos); + uint16_t PageEncodingsStart = readNext<uint16_t>(PageData, Pos); + uint16_t NumPageEncodings = readNext<uint16_t>(PageData, Pos); + SmallVector<uint32_t, 64> PageEncodings; + if (NumPageEncodings) { + outs() << " Page encodings: (count = " << NumPageEncodings << ")\n"; + Pos = PageEncodingsStart; + for (unsigned i = 0; i < NumPageEncodings; ++i) { + uint32_t Encoding = readNext<uint32_t>(PageData, Pos); + PageEncodings.push_back(Encoding); + outs() << " encoding[" << (i + NumCommonEncodings) + << "]: " << format("0x%08" PRIx32, Encoding) << '\n'; + } + } Pos = EntriesStart; for (unsigned i = 0; i < NumEntries; ++i) { @@ -8026,12 +8021,10 @@ uint32_t EncodingIdx = Entry >> 24; uint32_t Encoding; - if (EncodingIdx < CommonEncodings.size()) + if (EncodingIdx < NumCommonEncodings) Encoding = CommonEncodings[EncodingIdx]; else - Encoding = read<uint32_t>(PageEncodings, - sizeof(uint32_t) * - (EncodingIdx - CommonEncodings.size())); + Encoding = PageEncodings[EncodingIdx - NumCommonEncodings]; outs() << " [" << i << "]: " << "function offset=" << format("0x%08" PRIx32, FunctionOffset) @@ -10233,7 +10226,7 @@ void objdump::printMachOFileHeader(const object::ObjectFile *Obj) { const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj); - PrintMachHeader(file, !NonVerbose); + PrintMachHeader(file, Verbose); } void objdump::printMachOLoadCommands(const object::ObjectFile *Obj) { @@ -10251,7 +10244,7 @@ filetype = H.filetype; cputype = H.cputype; } - PrintLoadCommands(file, filetype, cputype, !NonVerbose); + PrintLoadCommands(file, filetype, cputype, Verbose); } //===----------------------------------------------------------------------===// @@ -10292,30 +10285,16 @@ Entry.address() + BaseSegmentAddress); outs() << Entry.name(); if (WeakDef || ThreadLocal || Resolver || Abs) { - bool NeedsComma = false; + ListSeparator LS; outs() << " ["; - if (WeakDef) { - outs() << "weak_def"; - NeedsComma = true; - } - if (ThreadLocal) { - if (NeedsComma) - outs() << ", "; - outs() << "per-thread"; - NeedsComma = true; - } - if (Abs) { - if (NeedsComma) - outs() << ", "; - outs() << "absolute"; - NeedsComma = true; - } - if (Resolver) { - if (NeedsComma) - outs() << ", "; - outs() << format("resolver=0x%08llX", Entry.other()); - NeedsComma = true; - } + if (WeakDef) + outs() << LS << "weak_def"; + if (ThreadLocal) + outs() << LS << "per-thread"; + if (Abs) + outs() << LS << "absolute"; + if (Resolver) + outs() << LS << format("resolver=0x%08llX", Entry.other()); outs() << "]"; } if (ReExport) { @@ -10486,7 +10465,7 @@ } void objdump::printLazyBindTable(ObjectFile *o) { - outs() << "Lazy bind table:\n"; + outs() << "\nLazy bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOLazyBindTable(MachO); else @@ -10496,7 +10475,7 @@ } void objdump::printWeakBindTable(ObjectFile *o) { - outs() << "Weak bind table:\n"; + outs() << "\nWeak bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOWeakBindTable(MachO); else @@ -10506,7 +10485,7 @@ } void objdump::printExportsTrie(const ObjectFile *o) { - outs() << "Exports trie:\n"; + outs() << "\nExports trie:\n"; if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOExportsTrie(MachO); else @@ -10516,7 +10495,7 @@ } void objdump::printRebaseTable(ObjectFile *o) { - outs() << "Rebase table:\n"; + outs() << "\nRebase table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachORebaseTable(MachO); else @@ -10526,7 +10505,7 @@ } void objdump::printBindTable(ObjectFile *o) { - outs() << "Bind table:\n"; + outs() << "\nBind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOBindTable(MachO); else
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/MachODump.h b/src/llvm-project/llvm/tools/llvm-objdump/MachODump.h index adf6c34..7568062 100644 --- a/src/llvm-project/llvm/tools/llvm-objdump/MachODump.h +++ b/src/llvm-project/llvm/tools/llvm-objdump/MachODump.h
@@ -24,24 +24,36 @@ class RelocationRef; } // namespace object +namespace opt { +class InputArgList; +} // namespace opt + namespace objdump { +void parseMachOOptions(const llvm::opt::InputArgList &InputArgs); + // MachO specific options -extern cl::OptionCategory MachOCat; -extern cl::opt<bool> Bind; -extern cl::opt<bool> DataInCode; -extern cl::opt<bool> DylibsUsed; -extern cl::opt<bool> DylibId; -extern cl::opt<bool> ExportsTrie; -extern cl::opt<bool> FirstPrivateHeader; -extern cl::opt<bool> IndirectSymbols; -extern cl::opt<bool> InfoPlist; -extern cl::opt<bool> LazyBind; -extern cl::opt<bool> LinkOptHints; -extern cl::opt<bool> ObjcMetaData; -extern cl::opt<bool> Rebase; -extern cl::opt<bool> UniversalHeaders; -extern cl::opt<bool> WeakBind; +extern bool Bind; +extern bool DataInCode; +extern std::string DisSymName; +extern bool DylibId; +extern bool DylibsUsed; +extern bool ExportsTrie; +extern bool FirstPrivateHeader; +extern bool FullLeadingAddr; +extern bool FunctionStarts; +extern bool IndirectSymbols; +extern bool InfoPlist; +extern bool LazyBind; +extern bool LeadingHeaders; +extern bool LinkOptHints; +extern bool ObjcMetaData; +extern bool Rebase; +extern bool Rpaths; +extern bool SymbolicOperands; +extern bool UniversalHeaders; +extern bool Verbose; +extern bool WeakBind; Error getMachORelocationValueString(const object::MachOObjectFile *Obj, const object::RelocationRef &RelRef,
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/ObjdumpOptID.h b/src/llvm-project/llvm/tools/llvm-objdump/ObjdumpOptID.h new file mode 100644 index 0000000..65f6c60 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objdump/ObjdumpOptID.h
@@ -0,0 +1,13 @@ +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H +#define LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H + +enum ObjdumpOptID { + OBJDUMP_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OBJDUMP_##ID, +#include "ObjdumpOpts.inc" +#undef OPTION +}; + +#endif // LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/ObjdumpOpts.td b/src/llvm-project/llvm/tools/llvm-objdump/ObjdumpOpts.td new file mode 100644 index 0000000..1b19733 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objdump/ObjdumpOpts.td
@@ -0,0 +1,323 @@ +include "llvm/Option/OptParser.td" + +def help : Flag<["--"], "help">, + HelpText<"Display available options (--help-hidden for more)">; + +def help_hidden : Flag<["--"], "help-hidden">, + Flags<[HelpHidden]>, + HelpText<"Display all available options">; + +def version : Flag<["--"], "version">, + HelpText<"Display the version of this program">; +def : Flag<["-"], "v">, Alias<version>, HelpText<"Alias for --version">; + +def adjust_vma_EQ : Joined<["--"], "adjust-vma=">, + MetaVarName<"offset">, + HelpText<"Increase the displayed address by the specified offset">; + +def all_headers : Flag<["--"], "all-headers">, + HelpText<"Display all available header information">; +def : Flag<["-"], "x">, Alias<all_headers>, HelpText<"Alias for --all-headers">; + +def arch_name_EQ : Joined<["--"], "arch-name=">, + HelpText<"Target arch to disassemble for, " + "see --version for available targets">; +def archive_headers : Flag<["--"], "archive-headers">, + HelpText<"Display archive header information">; + +def : Flag<["-"], "a">, Alias<archive_headers>, + HelpText<"Alias for --archive-headers">; + +def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">; +def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">; + +def disassemble : Flag<["--"], "disassemble">, + HelpText<"Display assembler mnemonics for the machine instructions">; +def : Flag<["-"], "d">, Alias<disassemble>, HelpText<"Alias for --disassemble">; + +def disassemble_all : Flag<["--"], "disassemble-all">, + HelpText<"Display assembler mnemonics for the machine instructions">; +def : Flag<["-"], "D">, Alias<disassemble_all>, + HelpText<"Alias for --disassemble-all">; + +def symbol_description : Flag<["--"], "symbol-description">, + HelpText<"Add symbol description for disassembly. This " + "option is for XCOFF files only.">; + +def disassemble_symbols_EQ : Joined<["--"], "disassemble-symbols=">, + HelpText<"List of symbols to disassemble. " + "Accept demangled names when --demangle is " + "specified, otherwise accept mangled names">; + +def disassemble_zeroes : Flag<["--"], "disassemble-zeroes">, + HelpText<"Do not skip blocks of zeroes when disassembling">; +def : Flag<["-"], "z">, Alias<disassemble_zeroes>, + HelpText<"Alias for --disassemble-zeroes">; + +def disassembler_options_EQ : Joined<["--"], "disassembler-options=">, + MetaVarName<"options">, + HelpText<"Pass target specific disassembler options">; +def : JoinedOrSeparate<["-"], "M">, Alias<disassembler_options_EQ>, + HelpText<"Alias for --disassembler-options=">; + +def dynamic_reloc : Flag<["--"], "dynamic-reloc">, + HelpText<"Display the dynamic relocation entries in the file">; +def : Flag<["-"], "R">, Alias<dynamic_reloc>, + HelpText<"Alias for --dynamic-reloc">; + +def dwarf_EQ : Joined<["--"], "dwarf=">, + HelpText<"Dump of dwarf debug sections">, Values<"frames">; + +def fault_map_section : Flag<["--"], "fault-map-section">, + HelpText<"Display contents of faultmap section">; + +def file_headers : Flag<["--"], "file-headers">, + HelpText<"Display the contents of the overall file header">; +def : Flag<["-"], "f">, Alias<file_headers>, + HelpText<"Alias for --file-headers">; + +def full_contents : Flag<["--"], "full-contents">, + HelpText<"Display the content of each section">; +def : Flag<["-"], "s">, Alias<full_contents>, + HelpText<"Alias for --full-contents">; + +def line_numbers : Flag<["--"], "line-numbers">, + HelpText<"Display source line numbers with " + "disassembly. Implies disassemble object">; +def : Flag<["-"], "l">, Alias<line_numbers>, + HelpText<"Alias for --line-numbers">; + +def macho : Flag<["--"], "macho">, + HelpText<"Use MachO specific object file parser">; +def : Flag<["-"], "m">, Alias<macho>, HelpText<"Alias for --macho">; + +def mcpu_EQ : Joined<["--"], "mcpu=">, + MetaVarName<"cpu-name">, + HelpText<"Target a specific cpu type (--mcpu=help for details)">; + +def mattr_EQ : Joined<["--"], "mattr=">, + MetaVarName<"a1,+a2,-a3,...">, + HelpText<"Target specific attributes (--mattr=help for details)">; + +def no_show_raw_insn : Flag<["--"], "no-show-raw-insn">, + HelpText<"When disassembling instructions, " + "do not print the instruction bytes.">; + +def no_leading_addr : Flag<["--"], "no-leading-addr">, + HelpText<"Print no leading address">; + +def raw_clang_ast : Flag<["--"], "raw-clang-ast">, + HelpText<"Dump the raw binary contents of the clang AST section">; + +def reloc : Flag<["--"], "reloc">, + HelpText<"Display the relocation entries in the file">; +def : Flag<["-"], "r">, Alias<reloc>, HelpText<"Alias for --reloc">; + +def print_imm_hex : Flag<["--"], "print-imm-hex">, + HelpText<"Use hex format for immediate values">; + +def no_print_imm_hex : Flag<["--"], "no-print-imm-hex">, + HelpText<"Do not use hex format for immediate values (default)">; +def : Flag<["--"], "print-imm-hex=false">, Alias<no_print_imm_hex>; + +def private_headers : Flag<["--"], "private-headers">, + HelpText<"Display format specific file headers">; +def : Flag<["-"], "p">, Alias<private_headers>, + HelpText<"Alias for --private-headers">; + +def section_EQ : Joined<["--"], "section=">, + HelpText<"Operate on the specified sections only. " + "With --macho dump segment,section">; +def : Separate<["--"], "section">, Alias<section_EQ>; +def : JoinedOrSeparate<["-"], "j">, Alias<section_EQ>, + HelpText<"Alias for --section">; + +def section_headers : Flag<["--"], "section-headers">, + HelpText<"Display summaries of the headers for each section.">; +def : Flag<["--"], "headers">, Alias<section_headers>, + HelpText<"Alias for --section-headers">; +def : Flag<["-"], "h">, Alias<section_headers>, + HelpText<"Alias for --section-headers">; + +def show_lma : Flag<["--"], "show-lma">, + HelpText<"Display LMA column when dumping ELF section headers">; + +def source : Flag<["--"], "source">, + HelpText<"Display source inlined with disassembly. Implies disassemble object">; +def : Flag<["-"], "S">, Alias<source>, HelpText<"Alias for --source">; + +def start_address_EQ : Joined<["--"], "start-address=">, + MetaVarName<"address">, + HelpText<"Disassemble beginning at address">; +def stop_address_EQ : Joined<["--"], "stop-address=">, + MetaVarName<"address">, + HelpText<"Stop disassembly at address">; + +def syms : Flag<["--"], "syms">, + HelpText<"Display the symbol table">; +def : Flag<["-"], "t">, Alias<syms>, HelpText<"Alias for --syms">; + +def symbolize_operands : Flag<["--"], "symbolize-operands">, + HelpText<"Symbolize instruction operands when disassembling">; + +def dynamic_syms : Flag<["--"], "dynamic-syms">, + HelpText<"Display the contents of the dynamic symbol table">; +def : Flag<["-"], "T">, Alias<dynamic_syms>, + HelpText<"Alias for --dynamic-syms">; + +def triple_EQ : Joined<["--"], "triple=">, + HelpText<"Target triple to disassemble for, " + "see --version for available targets">; +def : Separate<["--"], "triple">, + Alias<triple_EQ>; + +def unwind_info : Flag<["--"], "unwind-info">, + HelpText<"Display unwind information">; +def : Flag<["-"], "u">, Alias<unwind_info>, + HelpText<"Alias for --unwind-info">; + +def wide : Flag<["--"], "wide">, + HelpText<"Ignored for compatibility with GNU objdump">; +def : Flag<["-"], "w">, Alias<wide>; + +def prefix : Separate<["--"], "prefix">, + HelpText<"Add prefix to absolute paths">; + +def prefix_strip : Separate<["--"], "prefix-strip">, + HelpText<"Strip out initial directories from absolute " + "paths. No effect without --prefix">; + +def debug_vars_EQ : Joined<["--"], "debug-vars=">, + Values<"unicode,ascii">; +def : Flag<["--"], "debug-vars">, + HelpText<"Print the locations (in registers or memory) of " + "source-level variables alongside disassembly">, + Alias<debug_vars_EQ>, AliasArgs<["unicode"]>; + +def debug_vars_indent_EQ : Joined<["--"], "debug-vars-indent=">, + HelpText<"Distance to indent the source-level variable display, " + "relative to the start of the disassembly">; + +def x86_asm_syntax_att : Flag<["--"], "x86-asm-syntax=att">, + HelpText<"Emit AT&T-style disassembly">; + +def x86_asm_syntax_intel : Flag<["--"], "x86-asm-syntax=intel">, + HelpText<"Emit Intel-style disassembly">; + + +def grp_mach_o : OptionGroup<"kind">, HelpText<"llvm-objdump MachO Specific Options">; + +def private_header : Flag<["--"], "private-header">, + HelpText<"Display only the first format specific file header">, + Group<grp_mach_o>; + +def exports_trie : Flag<["--"], "exports-trie">, + HelpText<"Display mach-o exported symbols">, + Group<grp_mach_o>; + +def rebase : Flag<["--"], "rebase">, + HelpText<"Display mach-o rebasing info">, + Group<grp_mach_o>; + +def bind : Flag<["--"], "bind">, + HelpText<"Display mach-o binding info">, + Group<grp_mach_o>; + +def lazy_bind : Flag<["--"], "lazy-bind">, + HelpText<"Display mach-o lazy binding info">, + Group<grp_mach_o>; + +def weak_bind : Flag<["--"], "weak-bind">, + HelpText<"Display mach-o weak binding info">, + Group<grp_mach_o>; + +def g : Flag<["-"], "g">, + HelpText<"Print line information from debug info if available">, + Group<grp_mach_o>; + +def dsym_EQ : Joined<["--"], "dsym=">, + HelpText<"Use .dSYM file for debug info">, + Group<grp_mach_o>; +def : Separate<["--"], "dsym">, + Alias<dsym_EQ>, + Group<grp_mach_o>; + +def full_leading_addr : Flag<["--"], "full-leading-addr">, + HelpText<"Print full leading address">, + Group<grp_mach_o>; + +def no_leading_headers : Flag<["--"], "no-leading-headers">, + HelpText<"Print no leading headers">, + Group<grp_mach_o>; + +def universal_headers : Flag<["--"], "universal-headers">, + HelpText<"Print Mach-O universal headers (requires --macho)">, + Group<grp_mach_o>; + +def archive_member_offsets : Flag<["--"], "archive-member-offsets">, + HelpText<"Print the offset to each archive member for Mach-O archives " + "(requires --macho and --archive-headers)">, + Group<grp_mach_o>; + +def indirect_symbols : Flag<["--"], "indirect-symbols">, + HelpText<"Print indirect symbol table for Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def data_in_code : Flag<["--"], "data-in-code">, + HelpText<"Print the data in code table for Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def function_starts : Flag<["--"], "function-starts">, + HelpText<"Print the function starts table for " + "Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def link_opt_hints : Flag<["--"], "link-opt-hints">, + HelpText<"Print the linker optimization hints for " + "Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def info_plist : Flag<["--"], "info-plist">, + HelpText<"Print the info plist section as strings for " + "Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def dylibs_used : Flag<["--"], "dylibs-used">, + HelpText<"Print the shared libraries used for linked " + "Mach-O files (requires --macho)">, + Group<grp_mach_o>; + +def dylib_id : Flag<["--"], "dylib-id">, + HelpText<"Print the shared library's id for the " + "dylib Mach-O file (requires --macho)">, + Group<grp_mach_o>; + +def rpaths : Flag<["--"], "rpaths">, + HelpText<"Print the runtime search paths for the " + "Mach-O file (requires --macho)">, + Group<grp_mach_o>; + +def non_verbose : Flag<["--"], "non-verbose">, + HelpText<"Print the info for Mach-O objects in non-verbose or " + "numeric form (requires --macho)">, + Group<grp_mach_o>; + +def objc_meta_data : Flag<["--"], "objc-meta-data">, + HelpText<"Print the Objective-C runtime meta data " + "for Mach-O files (requires --macho)">, + Group<grp_mach_o>; + +def dis_symname : Separate<["--"], "dis-symname">, + HelpText<"disassemble just this symbol's instructions (requires --macho)">, + Group<grp_mach_o>; + +def no_symbolic_operands : Flag<["--"], "no-symbolic-operands">, + HelpText<"do not symbolic operands when disassembling (requires --macho)">, + Group<grp_mach_o>; + +def arch_EQ : Joined<["--"], "arch=">, + HelpText<"architecture(s) from a Mach-O file to dump">, + Group<grp_mach_o>; +def : Separate<["--"], "arch">, + Alias<arch_EQ>, + Group<grp_mach_o>;
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/OtoolOpts.td b/src/llvm-project/llvm/tools/llvm-objdump/OtoolOpts.td new file mode 100644 index 0000000..61ea701 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objdump/OtoolOpts.td
@@ -0,0 +1,68 @@ +include "llvm/Option/OptParser.td" + +def help : Flag<["--"], "help">, HelpText<"print help">; +def help_hidden : Flag<["--"], "help-hidden">, + HelpText<"print help for hidden flags">; + +def arch : Separate<["-"], "arch">, + HelpText<"select slice of universal Mach-O file">; +def C : Flag<["-"], "C">, HelpText<"print linker optimization hints">; +def d : Flag<["-"], "d">, HelpText<"print data section">; +def D : Flag<["-"], "D">, HelpText<"print shared library id">; +def f : Flag<["-"], "f">, HelpText<"print universal headers">; +def G : Flag<["-"], "G">, HelpText<"print data-in-code table">; +def h : Flag<["-"], "h">, HelpText<"print mach header">; +def I : Flag<["-"], "I">, HelpText<"print indirect symbol table">; +def j : Flag<["-"], "j">, HelpText<"print opcode bytes">; +def l : Flag<["-"], "l">, HelpText<"print load commnads">; +def L : Flag<["-"], "L">, HelpText<"print used shared libraries">; +def mcpu_EQ : Joined<["-"], "mcpu=">, HelpText<"select cpu for disassembly">; +def o : Flag<["-"], "o">, HelpText<"print Objective-C segment">; +def p : Separate<["-"], "p">, + MetaVarName<"<function name>">, + HelpText<"start disassembly at <function name>">; +def P : Flag<["-"], "P">, HelpText<"print __TEXT,__info_plist section as strings">; +def : Flag<["-"], "q">, Flags<[HelpHidden]>, + HelpText<"use LLVM's disassembler (default)">; +def r : Flag<["-"], "r">, HelpText<"print relocation entries">; +def s : MultiArg<["-"], "s", 2>, + MetaVarName<"<segname> <sectname>">, + HelpText<"print contents of section">; +def t : Flag<["-"], "t">, HelpText<"print text section">; +def version : Flag<["--"], "version">, HelpText<"print version">; +def v : Flag<["-"], "v">, + HelpText<"verbose output / disassemble when printing text sections">; +def V : Flag<["-"], "V">, + HelpText<"symbolize disassembled operands (implies -v)">; +def x : Flag<["-"], "x">, HelpText<"print all text sections">; +def X : Flag<["-"], "X">, HelpText<"omit leading addresses or headers">; + +// Not (yet?) implemented: +// def a : Flag<["-"], "a">, HelpText<"print archive header">; +// -c print argument strings of a core file +// -m don't use archive(member) syntax +// -dyld_info +// -dyld_opcodes +// -chained_fixups +// -addr_slide=arg +// -function_offsets + + +// Obsolete and unsupported: +def grp_obsolete : OptionGroup<"kind">, + HelpText<"Obsolete and unsupported flags">; + +def : Flag<["-"], "B">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"force Thum disassembly (ARM 32-bit objects only)">; +def : Flag<["-"], "H">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print two-level hints table">; +def : Flag<["-"], "M">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print module table of shared library">; +def : Flag<["-"], "R">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print reference table of shared library">; +def : Flag<["-"], "S">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print table of contents of library">; +def : Flag<["-"], "T">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print table of contents of shared library">; +def : Flag<["-"], "Q">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"llvm-otool cannot use otool-classic's disassembler">;
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/SourcePrinter.cpp b/src/llvm-project/llvm/tools/llvm-objdump/SourcePrinter.cpp new file mode 100644 index 0000000..8befac5 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objdump/SourcePrinter.cpp
@@ -0,0 +1,483 @@ +//===-- SourcePrinter.cpp - source interleaving utilities ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the LiveVariablePrinter and SourcePrinter classes to +// keep track of DWARF info as the current address is updated, and print out the +// source file line and variable liveness as needed. +// +//===----------------------------------------------------------------------===// + +#include "SourcePrinter.h" +#include "llvm-objdump.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/FormatVariadic.h" + +#define DEBUG_TYPE "objdump" + +namespace llvm { +namespace objdump { + +unsigned getInstStartColumn(const MCSubtargetInfo &STI) { + return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; +} + +bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) { + if (LocExpr.Range == None) + return false; + return LocExpr.Range->SectionIndex == Addr.SectionIndex && + LocExpr.Range->LowPC <= Addr.Address && + LocExpr.Range->HighPC > Addr.Address; +} + +void LiveVariable::print(raw_ostream &OS, const MCRegisterInfo &MRI) const { + DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()}, + Unit->getContext().isLittleEndian(), 0); + DWARFExpression Expression(Data, Unit->getAddressByteSize()); + Expression.printCompact(OS, MRI); +} + +void LiveVariablePrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) { + uint64_t FuncLowPC, FuncHighPC, SectionIndex; + FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex); + const char *VarName = VarDie.getName(DINameKind::ShortName); + DWARFUnit *U = VarDie.getDwarfUnit(); + + Expected<DWARFLocationExpressionsVector> Locs = + VarDie.getLocations(dwarf::DW_AT_location); + if (!Locs) { + // If the variable doesn't have any locations, just ignore it. We don't + // report an error or warning here as that could be noisy on optimised + // code. + consumeError(Locs.takeError()); + return; + } + + for (const DWARFLocationExpression &LocExpr : *Locs) { + if (LocExpr.Range) { + LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie); + } else { + // If the LocExpr does not have an associated range, it is valid for + // the whole of the function. + // TODO: technically it is not valid for any range covered by another + // LocExpr, does that happen in reality? + DWARFLocationExpression WholeFuncExpr{ + DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), LocExpr.Expr}; + LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie); + } + } +} + +void LiveVariablePrinter::addFunction(DWARFDie D) { + for (const DWARFDie &Child : D.children()) { + if (Child.getTag() == dwarf::DW_TAG_variable || + Child.getTag() == dwarf::DW_TAG_formal_parameter) + addVariable(D, Child); + else + addFunction(Child); + } +} + +// Get the column number (in characters) at which the first live variable +// line should be printed. +unsigned LiveVariablePrinter::getIndentLevel() const { + return DbgIndent + getInstStartColumn(STI); +} + +// Indent to the first live-range column to the right of the currently +// printed line, and return the index of that column. +// TODO: formatted_raw_ostream uses "column" to mean a number of characters +// since the last \n, and we use it to mean the number of slots in which we +// put live variable lines. Pick a less overloaded word. +unsigned LiveVariablePrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) { + // Logical column number: column zero is the first column we print in, each + // logical column is 2 physical columns wide. + unsigned FirstUnprintedLogicalColumn = + std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0); + // Physical column number: the actual column number in characters, with + // zero being the left-most side of the screen. + unsigned FirstUnprintedPhysicalColumn = + getIndentLevel() + FirstUnprintedLogicalColumn * 2; + + if (FirstUnprintedPhysicalColumn > OS.getColumn()) + OS.PadToColumn(FirstUnprintedPhysicalColumn); + + return FirstUnprintedLogicalColumn; +} + +unsigned LiveVariablePrinter::findFreeColumn() { + for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx) + if (!ActiveCols[ColIdx].isActive()) + return ColIdx; + + size_t OldSize = ActiveCols.size(); + ActiveCols.grow(std::max<size_t>(OldSize * 2, 1)); + return OldSize; +} + +void LiveVariablePrinter::dump() const { + for (const LiveVariable &LV : LiveVariables) { + dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": "; + LV.print(dbgs(), MRI); + dbgs() << "\n"; + } +} + +void LiveVariablePrinter::addCompileUnit(DWARFDie D) { + if (D.getTag() == dwarf::DW_TAG_subprogram) + addFunction(D); + else + for (const DWARFDie &Child : D.children()) + addFunction(Child); +} + +/// Update to match the state of the instruction between ThisAddr and +/// NextAddr. In the common case, any live range active at ThisAddr is +/// live-in to the instruction, and any live range active at NextAddr is +/// live-out of the instruction. If IncludeDefinedVars is false, then live +/// ranges starting at NextAddr will be ignored. +void LiveVariablePrinter::update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, + bool IncludeDefinedVars) { + // First, check variables which have already been assigned a column, so + // that we don't change their order. + SmallSet<unsigned, 8> CheckedVarIdxs; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + if (!ActiveCols[ColIdx].isActive()) + continue; + CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx); + LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx]; + ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr); + ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr); + LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn + << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n"); + + if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) + ActiveCols[ColIdx].VarIdx = Column::NullVarIdx; + } + + // Next, look for variables which don't already have a column, but which + // are now live. + if (IncludeDefinedVars) { + for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End; + ++VarIdx) { + if (CheckedVarIdxs.count(VarIdx)) + continue; + LiveVariable &LV = LiveVariables[VarIdx]; + bool LiveIn = LV.liveAtAddress(ThisAddr); + bool LiveOut = LV.liveAtAddress(NextAddr); + if (!LiveIn && !LiveOut) + continue; + + unsigned ColIdx = findFreeColumn(); + LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << LiveIn + << ", LiveOut=" << LiveOut << "\n"); + ActiveCols[ColIdx].VarIdx = VarIdx; + ActiveCols[ColIdx].LiveIn = LiveIn; + ActiveCols[ColIdx].LiveOut = LiveOut; + ActiveCols[ColIdx].MustDrawLabel = true; + } + } +} + +enum class LineChar { + RangeStart, + RangeMid, + RangeEnd, + LabelVert, + LabelCornerNew, + LabelCornerActive, + LabelHoriz, +}; +const char *LiveVariablePrinter::getLineChar(LineChar C) const { + bool IsASCII = DbgVariables == DVASCII; + switch (C) { + case LineChar::RangeStart: + return IsASCII ? "^" : (const char *)u8"\u2548"; + case LineChar::RangeMid: + return IsASCII ? "|" : (const char *)u8"\u2503"; + case LineChar::RangeEnd: + return IsASCII ? "v" : (const char *)u8"\u253b"; + case LineChar::LabelVert: + return IsASCII ? "|" : (const char *)u8"\u2502"; + case LineChar::LabelCornerNew: + return IsASCII ? "/" : (const char *)u8"\u250c"; + case LineChar::LabelCornerActive: + return IsASCII ? "|" : (const char *)u8"\u2520"; + case LineChar::LabelHoriz: + return IsASCII ? "-" : (const char *)u8"\u2500"; + } + llvm_unreachable("Unhandled LineChar enum"); +} + +/// Print live ranges to the right of an existing line. This assumes the +/// line is not an instruction, so doesn't start or end any live ranges, so +/// we only need to print active ranges or empty columns. If AfterInst is +/// true, this is being printed after the last instruction fed to update(), +/// otherwise this is being printed before it. +void LiveVariablePrinter::printAfterOtherLine(formatted_raw_ostream &OS, + bool AfterInst) { + if (ActiveCols.size()) { + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx < End; ++ColIdx) { + if (ActiveCols[ColIdx].isActive()) { + if ((AfterInst && ActiveCols[ColIdx].LiveOut) || + (!AfterInst && ActiveCols[ColIdx].LiveIn)) + OS << getLineChar(LineChar::RangeMid); + else if (!AfterInst && ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::LabelVert); + else + OS << " "; + } + OS << " "; + } + } + OS << "\n"; +} + +/// Print any live variable range info needed to the right of a +/// non-instruction line of disassembly. This is where we print the variable +/// names and expressions, with thin line-drawing characters connecting them +/// to the live range which starts at the next instruction. If MustPrint is +/// true, we have to print at least one line (with the continuation of any +/// already-active live ranges) because something has already been printed +/// earlier on this line. +void LiveVariablePrinter::printBetweenInsts(formatted_raw_ostream &OS, + bool MustPrint) { + bool PrintedSomething = false; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) { + // First we need to print the live range markers for any active + // columns to the left of this one. + OS.PadToColumn(getIndentLevel()); + for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) { + if (ActiveCols[ColIdx2].isActive()) { + if (ActiveCols[ColIdx2].MustDrawLabel && !ActiveCols[ColIdx2].LiveIn) + OS << getLineChar(LineChar::LabelVert) << " "; + else + OS << getLineChar(LineChar::RangeMid) << " "; + } else + OS << " "; + } + + // Then print the variable name and location of the new live range, + // with box drawing characters joining it to the live range line. + OS << getLineChar(ActiveCols[ColIdx].LiveIn ? LineChar::LabelCornerActive + : LineChar::LabelCornerNew) + << getLineChar(LineChar::LabelHoriz) << " "; + WithColor(OS, raw_ostream::GREEN) + << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName; + OS << " = "; + { + WithColor ExprColor(OS, raw_ostream::CYAN); + LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI); + } + + // If there are any columns to the right of the expression we just + // printed, then continue their live range lines. + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx2 < End; ++ColIdx2) { + if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn) + OS << getLineChar(LineChar::RangeMid) << " "; + else + OS << " "; + } + + OS << "\n"; + PrintedSomething = true; + } + } + + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) + if (ActiveCols[ColIdx].isActive()) + ActiveCols[ColIdx].MustDrawLabel = false; + + // If we must print something (because we printed a line/column number), + // but don't have any new variables to print, then print a line which + // just continues any existing live ranges. + if (MustPrint && !PrintedSomething) + printAfterOtherLine(OS, false); +} + +/// Print the live variable ranges to the right of a disassembled instruction. +void LiveVariablePrinter::printAfterInst(formatted_raw_ostream &OS) { + if (!ActiveCols.size()) + return; + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx < End; ++ColIdx) { + if (!ActiveCols[ColIdx].isActive()) + OS << " "; + else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::RangeMid) << " "; + else if (ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::RangeStart) << " "; + else if (ActiveCols[ColIdx].LiveIn) + OS << getLineChar(LineChar::RangeEnd) << " "; + else + llvm_unreachable("var must be live in or out!"); + } +} + +bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { + std::unique_ptr<MemoryBuffer> Buffer; + if (LineInfo.Source) { + Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source); + } else { + auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName); + if (!BufferOrError) { + if (MissingSources.insert(LineInfo.FileName).second) + reportWarning("failed to find source " + LineInfo.FileName, + Obj->getFileName()); + return false; + } + Buffer = std::move(*BufferOrError); + } + // Chomp the file to get lines + const char *BufferStart = Buffer->getBufferStart(), + *BufferEnd = Buffer->getBufferEnd(); + std::vector<StringRef> &Lines = LineCache[LineInfo.FileName]; + const char *Start = BufferStart; + for (const char *I = BufferStart; I != BufferEnd; ++I) + if (*I == '\n') { + Lines.emplace_back(Start, I - Start - (BufferStart < I && I[-1] == '\r')); + Start = I + 1; + } + if (Start < BufferEnd) + Lines.emplace_back(Start, BufferEnd - Start); + SourceCache[LineInfo.FileName] = std::move(Buffer); + return true; +} + +void SourcePrinter::printSourceLine(formatted_raw_ostream &OS, + object::SectionedAddress Address, + StringRef ObjectFilename, + LiveVariablePrinter &LVP, + StringRef Delimiter) { + if (!Symbolizer) + return; + + DILineInfo LineInfo = DILineInfo(); + Expected<DILineInfo> ExpectedLineInfo = + Symbolizer->symbolizeCode(*Obj, Address); + std::string ErrorMessage; + if (ExpectedLineInfo) { + LineInfo = *ExpectedLineInfo; + } else if (!WarnedInvalidDebugInfo) { + WarnedInvalidDebugInfo = true; + // TODO Untested. + reportWarning("failed to parse debug information: " + + toString(ExpectedLineInfo.takeError()), + ObjectFilename); + } + + if (!objdump::Prefix.empty() && + sys::path::is_absolute_gnu(LineInfo.FileName)) { + // FileName has at least one character since is_absolute_gnu is false for + // an empty string. + assert(!LineInfo.FileName.empty()); + if (PrefixStrip > 0) { + uint32_t Level = 0; + auto StrippedNameStart = LineInfo.FileName.begin(); + + // Path.h iterator skips extra separators. Therefore it cannot be used + // here to keep compatibility with GNU Objdump. + for (auto Pos = StrippedNameStart + 1, End = LineInfo.FileName.end(); + Pos != End && Level < PrefixStrip; ++Pos) { + if (sys::path::is_separator(*Pos)) { + StrippedNameStart = Pos; + ++Level; + } + } + + LineInfo.FileName = + std::string(StrippedNameStart, LineInfo.FileName.end()); + } + + SmallString<128> FilePath; + sys::path::append(FilePath, Prefix, LineInfo.FileName); + + LineInfo.FileName = std::string(FilePath); + } + + if (PrintLines) + printLines(OS, LineInfo, Delimiter, LVP); + if (PrintSource) + printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP); + OldLineInfo = LineInfo; +} + +void SourcePrinter::printLines(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, StringRef Delimiter, + LiveVariablePrinter &LVP) { + bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString && + LineInfo.FunctionName != OldLineInfo.FunctionName; + if (PrintFunctionName) { + OS << Delimiter << LineInfo.FunctionName; + // If demangling is successful, FunctionName will end with "()". Print it + // only if demangling did not run or was unsuccessful. + if (!StringRef(LineInfo.FunctionName).endswith("()")) + OS << "()"; + OS << ":\n"; + } + if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 && + (OldLineInfo.Line != LineInfo.Line || + OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) { + OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line; + LVP.printBetweenInsts(OS, true); + } +} + +void SourcePrinter::printSources(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP) { + if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || + (OldLineInfo.Line == LineInfo.Line && + OldLineInfo.FileName == LineInfo.FileName)) + return; + + if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) + if (!cacheSource(LineInfo)) + return; + auto LineBuffer = LineCache.find(LineInfo.FileName); + if (LineBuffer != LineCache.end()) { + if (LineInfo.Line > LineBuffer->second.size()) { + reportWarning( + formatv( + "debug info line number {0} exceeds the number of lines in {1}", + LineInfo.Line, LineInfo.FileName), + ObjectFilename); + return; + } + // Vector begins at 0, line numbers are non-zero + OS << Delimiter << LineBuffer->second[LineInfo.Line - 1]; + LVP.printBetweenInsts(OS, true); + } +} + +SourcePrinter::SourcePrinter(const object::ObjectFile *Obj, + StringRef DefaultArch) + : Obj(Obj) { + symbolize::LLVMSymbolizer::Options SymbolizerOpts; + SymbolizerOpts.PrintFunctions = + DILineInfoSpecifier::FunctionNameKind::LinkageName; + SymbolizerOpts.Demangle = Demangle; + SymbolizerOpts.DefaultArch = std::string(DefaultArch); + Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); +} + +} // namespace objdump +} // namespace llvm
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/SourcePrinter.h b/src/llvm-project/llvm/tools/llvm-objdump/SourcePrinter.h new file mode 100644 index 0000000..21d5bdc --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-objdump/SourcePrinter.h
@@ -0,0 +1,166 @@ +//===-- SourcePrinter.h - source interleaving utilities --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H +#define LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H + +#include "llvm/ADT/IndexedMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Support/FormattedStream.h" +#include <unordered_map> +#include <vector> + +namespace llvm { +namespace objdump { + +/// Stores a single expression representing the location of a source-level +/// variable, along with the PC range for which that expression is valid. +struct LiveVariable { + DWARFLocationExpression LocExpr; + const char *VarName; + DWARFUnit *Unit; + const DWARFDie FuncDie; + + LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName, + DWARFUnit *Unit, const DWARFDie FuncDie) + : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {} + + bool liveAtAddress(object::SectionedAddress Addr); + + void print(raw_ostream &OS, const MCRegisterInfo &MRI) const; +}; + +/// Helper class for printing source variable locations alongside disassembly. +class LiveVariablePrinter { + // Information we want to track about one column in which we are printing a + // variable live range. + struct Column { + unsigned VarIdx = NullVarIdx; + bool LiveIn = false; + bool LiveOut = false; + bool MustDrawLabel = false; + + bool isActive() const { return VarIdx != NullVarIdx; } + + static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max(); + }; + + // All live variables we know about in the object/image file. + std::vector<LiveVariable> LiveVariables; + + // The columns we are currently drawing. + IndexedMap<Column> ActiveCols; + + const MCRegisterInfo &MRI; + const MCSubtargetInfo &STI; + + void addVariable(DWARFDie FuncDie, DWARFDie VarDie); + + void addFunction(DWARFDie D); + + // Get the column number (in characters) at which the first live variable + // line should be printed. + unsigned getIndentLevel() const; + + // Indent to the first live-range column to the right of the currently + // printed line, and return the index of that column. + // TODO: formatted_raw_ostream uses "column" to mean a number of characters + // since the last \n, and we use it to mean the number of slots in which we + // put live variable lines. Pick a less overloaded word. + unsigned moveToFirstVarColumn(formatted_raw_ostream &OS); + + unsigned findFreeColumn(); + +public: + LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) + : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {} + + void dump() const; + + void addCompileUnit(DWARFDie D); + + /// Update to match the state of the instruction between ThisAddr and + /// NextAddr. In the common case, any live range active at ThisAddr is + /// live-in to the instruction, and any live range active at NextAddr is + /// live-out of the instruction. If IncludeDefinedVars is false, then live + /// ranges starting at NextAddr will be ignored. + void update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, bool IncludeDefinedVars); + + enum class LineChar { + RangeStart, + RangeMid, + RangeEnd, + LabelVert, + LabelCornerNew, + LabelCornerActive, + LabelHoriz, + }; + const char *getLineChar(LineChar C) const; + + /// Print live ranges to the right of an existing line. This assumes the + /// line is not an instruction, so doesn't start or end any live ranges, so + /// we only need to print active ranges or empty columns. If AfterInst is + /// true, this is being printed after the last instruction fed to update(), + /// otherwise this is being printed before it. + void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst); + + /// Print any live variable range info needed to the right of a + /// non-instruction line of disassembly. This is where we print the variable + /// names and expressions, with thin line-drawing characters connecting them + /// to the live range which starts at the next instruction. If MustPrint is + /// true, we have to print at least one line (with the continuation of any + /// already-active live ranges) because something has already been printed + /// earlier on this line. + void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint); + + /// Print the live variable ranges to the right of a disassembled instruction. + void printAfterInst(formatted_raw_ostream &OS); +}; + +class SourcePrinter { +protected: + DILineInfo OldLineInfo; + const object::ObjectFile *Obj = nullptr; + std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer; + // File name to file contents of source. + std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache; + // Mark the line endings of the cached source. + std::unordered_map<std::string, std::vector<StringRef>> LineCache; + // Keep track of missing sources. + StringSet<> MissingSources; + // Only emit 'invalid debug info' warning once. + bool WarnedInvalidDebugInfo = false; + +private: + bool cacheSource(const DILineInfo &LineInfoFile); + + void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef Delimiter, LiveVariablePrinter &LVP); + + void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP); + +public: + SourcePrinter() = default; + SourcePrinter(const object::ObjectFile *Obj, StringRef DefaultArch); + virtual ~SourcePrinter() = default; + virtual void printSourceLine(formatted_raw_ostream &OS, + object::SectionedAddress Address, + StringRef ObjectFilename, + LiveVariablePrinter &LVP, + StringRef Delimiter = "; "); +}; + +} // namespace objdump +} // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/XCOFFDump.cpp b/src/llvm-project/llvm/tools/llvm-objdump/XCOFFDump.cpp index df37abb..c4cc5fe 100644 --- a/src/llvm-project/llvm/tools/llvm-objdump/XCOFFDump.cpp +++ b/src/llvm-project/llvm/tools/llvm-objdump/XCOFFDump.cpp
@@ -46,22 +46,30 @@ Optional<XCOFF::StorageMappingClass> objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile *Obj, const SymbolRef &Sym) { - XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj); + const XCOFFSymbolRef SymRef = Obj->toSymbolRef(Sym.getRawDataRefImpl()); - if (SymRef.hasCsectAuxEnt()) - return SymRef.getXCOFFCsectAuxEnt32()->StorageMappingClass; + if (!SymRef.isCsectSymbol()) + return None; - return None; + auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); + if (!CsectAuxEntOrErr) + return None; + + return CsectAuxEntOrErr.get().getStorageMappingClass(); } bool objdump::isLabel(const XCOFFObjectFile *Obj, const SymbolRef &Sym) { - XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj); + const XCOFFSymbolRef SymRef = Obj->toSymbolRef(Sym.getRawDataRefImpl()); - if (SymRef.hasCsectAuxEnt()) - return SymRef.getXCOFFCsectAuxEnt32()->isLabel(); + if (!SymRef.isCsectSymbol()) + return false; - return false; + auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); + if (!CsectAuxEntOrErr) + return false; + + return CsectAuxEntOrErr.get().isLabel(); } std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo,
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/llvm-objdump.cpp b/src/llvm-project/llvm/tools/llvm-objdump/llvm-objdump.cpp index 17128e9..48ae92f 100644 --- a/src/llvm-project/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/src/llvm-project/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -19,18 +19,19 @@ #include "COFFDump.h" #include "ELFDump.h" #include "MachODump.h" +#include "ObjdumpOptID.h" +#include "SourcePrinter.h" #include "WasmDump.h" #include "XCOFFDump.h" #include "llvm/ADT/IndexedMap.h" #include "llvm/ADT/Optional.h" -#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" -#include "llvm/CodeGen/FaultMaps.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/Demangle/Demangle.h" @@ -50,12 +51,15 @@ #include "llvm/Object/COFF.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/FaultMapParser.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/Wasm.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" @@ -81,307 +85,143 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::objdump; +using namespace llvm::opt; + +namespace { + +class CommonOptTable : public opt::OptTable { +public: + CommonOptTable(ArrayRef<Info> OptionInfos, const char *Usage, + const char *Description) + : OptTable(OptionInfos), Usage(Usage), Description(Description) { + setGroupedShortOptions(true); + } + + void printHelp(StringRef Argv0, bool ShowHidden = false) const { + Argv0 = sys::path::filename(Argv0); + opt::OptTable::printHelp(outs(), (Argv0 + Usage).str().c_str(), Description, + ShowHidden, ShowHidden); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + } + +private: + const char *Usage; + const char *Description; +}; + +// ObjdumpOptID is in ObjdumpOptID.h + +#define PREFIX(NAME, VALUE) const char *const OBJDUMP_##NAME[] = VALUE; +#include "ObjdumpOpts.inc" +#undef PREFIX + +static constexpr opt::OptTable::Info ObjdumpInfoTable[] = { +#define OBJDUMP_nullptr nullptr +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {OBJDUMP_##PREFIX, NAME, HELPTEXT, \ + METAVAR, OBJDUMP_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OBJDUMP_##GROUP, \ + OBJDUMP_##ALIAS, ALIASARGS, VALUES}, +#include "ObjdumpOpts.inc" +#undef OPTION +#undef OBJDUMP_nullptr +}; + +class ObjdumpOptTable : public CommonOptTable { +public: + ObjdumpOptTable() + : CommonOptTable(ObjdumpInfoTable, " [options] <input object files>", + "llvm object file dumper") {} +}; + +enum OtoolOptID { + OTOOL_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OTOOL_##ID, +#include "OtoolOpts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const OTOOL_##NAME[] = VALUE; +#include "OtoolOpts.inc" +#undef PREFIX + +static constexpr opt::OptTable::Info OtoolInfoTable[] = { +#define OTOOL_nullptr nullptr +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {OTOOL_##PREFIX, NAME, HELPTEXT, \ + METAVAR, OTOOL_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OTOOL_##GROUP, \ + OTOOL_##ALIAS, ALIASARGS, VALUES}, +#include "OtoolOpts.inc" +#undef OPTION +#undef OTOOL_nullptr +}; + +class OtoolOptTable : public CommonOptTable { +public: + OtoolOptTable() + : CommonOptTable(OtoolInfoTable, " [option...] [file...]", + "Mach-O object file displaying tool") {} +}; + +} // namespace #define DEBUG_TYPE "objdump" -static cl::OptionCategory ObjdumpCat("llvm-objdump Options"); +static uint64_t AdjustVMA; +static bool AllHeaders; +static std::string ArchName; +bool objdump::ArchiveHeaders; +bool objdump::Demangle; +bool objdump::Disassemble; +bool objdump::DisassembleAll; +bool objdump::SymbolDescription; +static std::vector<std::string> DisassembleSymbols; +static bool DisassembleZeroes; +static std::vector<std::string> DisassemblerOptions; +DIDumpType objdump::DwarfDumpType; +static bool DynamicRelocations; +static bool FaultMapSection; +static bool FileHeaders; +bool objdump::SectionContents; +static std::vector<std::string> InputFilenames; +bool objdump::PrintLines; +static bool MachOOpt; +std::string objdump::MCPU; +std::vector<std::string> objdump::MAttrs; +bool objdump::ShowRawInsn; +bool objdump::LeadingAddr; +static bool RawClangAST; +bool objdump::Relocations; +bool objdump::PrintImmHex; +bool objdump::PrivateHeaders; +std::vector<std::string> objdump::FilterSections; +bool objdump::SectionHeaders; +static bool ShowLMA; +bool objdump::PrintSource; -static cl::opt<uint64_t> AdjustVMA( - "adjust-vma", - cl::desc("Increase the displayed address by the specified offset"), - cl::value_desc("offset"), cl::init(0), cl::cat(ObjdumpCat)); +static uint64_t StartAddress; +static bool HasStartAddressFlag; +static uint64_t StopAddress = UINT64_MAX; +static bool HasStopAddressFlag; -static cl::opt<bool> - AllHeaders("all-headers", - cl::desc("Display all available header information"), - cl::cat(ObjdumpCat)); -static cl::alias AllHeadersShort("x", cl::desc("Alias for --all-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(AllHeaders)); +bool objdump::SymbolTable; +static bool SymbolizeOperands; +static bool DynamicSymbolTable; +std::string objdump::TripleName; +bool objdump::UnwindInfo; +static bool Wide; +std::string objdump::Prefix; +uint32_t objdump::PrefixStrip; -static cl::opt<std::string> - ArchName("arch-name", - cl::desc("Target arch to disassemble for, " - "see --version for available targets"), - cl::cat(ObjdumpCat)); +DebugVarsFormat objdump::DbgVariables = DVDisabled; -cl::opt<bool> - objdump::ArchiveHeaders("archive-headers", - cl::desc("Display archive header information"), - cl::cat(ObjdumpCat)); -static cl::alias ArchiveHeadersShort("a", - cl::desc("Alias for --archive-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(ArchiveHeaders)); - -cl::opt<bool> objdump::Demangle("demangle", cl::desc("Demangle symbols names"), - cl::init(false), cl::cat(ObjdumpCat)); -static cl::alias DemangleShort("C", cl::desc("Alias for --demangle"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(Demangle)); - -cl::opt<bool> objdump::Disassemble( - "disassemble", - cl::desc("Display assembler mnemonics for the machine instructions"), - cl::cat(ObjdumpCat)); -static cl::alias DisassembleShort("d", cl::desc("Alias for --disassemble"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(Disassemble)); - -cl::opt<bool> objdump::DisassembleAll( - "disassemble-all", - cl::desc("Display assembler mnemonics for the machine instructions"), - cl::cat(ObjdumpCat)); -static cl::alias DisassembleAllShort("D", - cl::desc("Alias for --disassemble-all"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(DisassembleAll)); - -cl::opt<bool> objdump::SymbolDescription( - "symbol-description", - cl::desc("Add symbol description for disassembly. This " - "option is for XCOFF files only"), - cl::init(false), cl::cat(ObjdumpCat)); - -static cl::list<std::string> - DisassembleSymbols("disassemble-symbols", cl::CommaSeparated, - cl::desc("List of symbols to disassemble. " - "Accept demangled names when --demangle is " - "specified, otherwise accept mangled names"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> DisassembleZeroes( - "disassemble-zeroes", - cl::desc("Do not skip blocks of zeroes when disassembling"), - cl::cat(ObjdumpCat)); -static cl::alias - DisassembleZeroesShort("z", cl::desc("Alias for --disassemble-zeroes"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(DisassembleZeroes)); - -static cl::list<std::string> - DisassemblerOptions("disassembler-options", - cl::desc("Pass target specific disassembler options"), - cl::value_desc("options"), cl::CommaSeparated, - cl::cat(ObjdumpCat)); -static cl::alias - DisassemblerOptionsShort("M", cl::desc("Alias for --disassembler-options"), - cl::NotHidden, cl::Grouping, cl::Prefix, - cl::CommaSeparated, - cl::aliasopt(DisassemblerOptions)); - -cl::opt<DIDumpType> objdump::DwarfDumpType( - "dwarf", cl::init(DIDT_Null), cl::desc("Dump of dwarf debug sections:"), - cl::values(clEnumValN(DIDT_DebugFrame, "frames", ".debug_frame")), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> DynamicRelocations( - "dynamic-reloc", - cl::desc("Display the dynamic relocation entries in the file"), - cl::cat(ObjdumpCat)); -static cl::alias DynamicRelocationShort("R", - cl::desc("Alias for --dynamic-reloc"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(DynamicRelocations)); - -static cl::opt<bool> - FaultMapSection("fault-map-section", - cl::desc("Display contents of faultmap section"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> - FileHeaders("file-headers", - cl::desc("Display the contents of the overall file header"), - cl::cat(ObjdumpCat)); -static cl::alias FileHeadersShort("f", cl::desc("Alias for --file-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(FileHeaders)); - -cl::opt<bool> - objdump::SectionContents("full-contents", - cl::desc("Display the content of each section"), - cl::cat(ObjdumpCat)); -static cl::alias SectionContentsShort("s", - cl::desc("Alias for --full-contents"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(SectionContents)); - -static cl::list<std::string> InputFilenames(cl::Positional, - cl::desc("<input object files>"), - cl::ZeroOrMore, - cl::cat(ObjdumpCat)); - -static cl::opt<bool> - PrintLines("line-numbers", - cl::desc("Display source line numbers with " - "disassembly. Implies disassemble object"), - cl::cat(ObjdumpCat)); -static cl::alias PrintLinesShort("l", cl::desc("Alias for --line-numbers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(PrintLines)); - -static cl::opt<bool> MachOOpt("macho", - cl::desc("Use MachO specific object file parser"), - cl::cat(ObjdumpCat)); -static cl::alias MachOm("m", cl::desc("Alias for --macho"), cl::NotHidden, - cl::Grouping, cl::aliasopt(MachOOpt)); - -cl::opt<std::string> objdump::MCPU( - "mcpu", cl::desc("Target a specific cpu type (--mcpu=help for details)"), - cl::value_desc("cpu-name"), cl::init(""), cl::cat(ObjdumpCat)); - -cl::list<std::string> objdump::MAttrs( - "mattr", cl::CommaSeparated, - cl::desc("Target specific attributes (--mattr=help for details)"), - cl::value_desc("a1,+a2,-a3,..."), cl::cat(ObjdumpCat)); - -cl::opt<bool> objdump::NoShowRawInsn( - "no-show-raw-insn", - cl::desc( - "When disassembling instructions, do not print the instruction bytes."), - cl::cat(ObjdumpCat)); - -cl::opt<bool> objdump::NoLeadingAddr("no-leading-addr", - cl::desc("Print no leading address"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> RawClangAST( - "raw-clang-ast", - cl::desc("Dump the raw binary contents of the clang AST section"), - cl::cat(ObjdumpCat)); - -cl::opt<bool> - objdump::Relocations("reloc", - cl::desc("Display the relocation entries in the file"), - cl::cat(ObjdumpCat)); -static cl::alias RelocationsShort("r", cl::desc("Alias for --reloc"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(Relocations)); - -cl::opt<bool> - objdump::PrintImmHex("print-imm-hex", - cl::desc("Use hex format for immediate values"), - cl::cat(ObjdumpCat)); - -cl::opt<bool> - objdump::PrivateHeaders("private-headers", - cl::desc("Display format specific file headers"), - cl::cat(ObjdumpCat)); -static cl::alias PrivateHeadersShort("p", - cl::desc("Alias for --private-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(PrivateHeaders)); - -cl::list<std::string> - objdump::FilterSections("section", - cl::desc("Operate on the specified sections only. " - "With --macho dump segment,section"), - cl::cat(ObjdumpCat)); -static cl::alias FilterSectionsj("j", cl::desc("Alias for --section"), - cl::NotHidden, cl::Grouping, cl::Prefix, - cl::aliasopt(FilterSections)); - -cl::opt<bool> objdump::SectionHeaders( - "section-headers", - cl::desc("Display summaries of the headers for each section."), - cl::cat(ObjdumpCat)); -static cl::alias SectionHeadersShort("headers", - cl::desc("Alias for --section-headers"), - cl::NotHidden, - cl::aliasopt(SectionHeaders)); -static cl::alias SectionHeadersShorter("h", - cl::desc("Alias for --section-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(SectionHeaders)); - -static cl::opt<bool> - ShowLMA("show-lma", - cl::desc("Display LMA column when dumping ELF section headers"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> PrintSource( - "source", - cl::desc( - "Display source inlined with disassembly. Implies disassemble object"), - cl::cat(ObjdumpCat)); -static cl::alias PrintSourceShort("S", cl::desc("Alias for --source"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(PrintSource)); - -static cl::opt<uint64_t> - StartAddress("start-address", cl::desc("Disassemble beginning at address"), - cl::value_desc("address"), cl::init(0), cl::cat(ObjdumpCat)); -static cl::opt<uint64_t> StopAddress("stop-address", - cl::desc("Stop disassembly at address"), - cl::value_desc("address"), - cl::init(UINT64_MAX), cl::cat(ObjdumpCat)); - -cl::opt<bool> objdump::SymbolTable("syms", cl::desc("Display the symbol table"), - cl::cat(ObjdumpCat)); -static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(SymbolTable)); - -static cl::opt<bool> SymbolizeOperands( - "symbolize-operands", - cl::desc("Symbolize instruction operands when disassembling"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> DynamicSymbolTable( - "dynamic-syms", - cl::desc("Display the contents of the dynamic symbol table"), - cl::cat(ObjdumpCat)); -static cl::alias DynamicSymbolTableShort("T", - cl::desc("Alias for --dynamic-syms"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(DynamicSymbolTable)); - -cl::opt<std::string> - objdump::TripleName("triple", - cl::desc("Target triple to disassemble for, see " - "--version for available targets"), - cl::cat(ObjdumpCat)); - -cl::opt<bool> objdump::UnwindInfo("unwind-info", - cl::desc("Display unwind information"), - cl::cat(ObjdumpCat)); -static cl::alias UnwindInfoShort("u", cl::desc("Alias for --unwind-info"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(UnwindInfo)); - -static cl::opt<bool> - Wide("wide", cl::desc("Ignored for compatibility with GNU objdump"), - cl::cat(ObjdumpCat)); -static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide)); - -cl::opt<std::string> objdump::Prefix("prefix", - cl::desc("Add prefix to absolute paths"), - cl::cat(ObjdumpCat)); - -enum DebugVarsFormat { - DVDisabled, - DVUnicode, - DVASCII, -}; - -static cl::opt<DebugVarsFormat> DbgVariables( - "debug-vars", cl::init(DVDisabled), - cl::desc("Print the locations (in registers or memory) of " - "source-level variables alongside disassembly"), - cl::ValueOptional, - cl::values(clEnumValN(DVUnicode, "", "unicode"), - clEnumValN(DVUnicode, "unicode", "unicode"), - clEnumValN(DVASCII, "ascii", "unicode")), - cl::cat(ObjdumpCat)); - -static cl::opt<int> - DbgIndent("debug-vars-indent", cl::init(40), - cl::desc("Distance to indent the source-level variable display, " - "relative to the start of the disassembly"), - cl::cat(ObjdumpCat)); - -static cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); +int objdump::DbgIndent = 52; static StringSet<> DisasmSymbolSet; StringSet<> objdump::FoundSectionSet; @@ -588,509 +428,7 @@ /// Get the column at which we want to start printing the instruction /// disassembly, taking into account anything which appears to the left of it. unsigned getInstStartColumn(const MCSubtargetInfo &STI) { - return NoShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; -} - -/// Stores a single expression representing the location of a source-level -/// variable, along with the PC range for which that expression is valid. -struct LiveVariable { - DWARFLocationExpression LocExpr; - const char *VarName; - DWARFUnit *Unit; - const DWARFDie FuncDie; - - LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName, - DWARFUnit *Unit, const DWARFDie FuncDie) - : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {} - - bool liveAtAddress(object::SectionedAddress Addr) { - if (LocExpr.Range == None) - return false; - return LocExpr.Range->SectionIndex == Addr.SectionIndex && - LocExpr.Range->LowPC <= Addr.Address && - LocExpr.Range->HighPC > Addr.Address; - } - - void print(raw_ostream &OS, const MCRegisterInfo &MRI) const { - DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()}, - Unit->getContext().isLittleEndian(), 0); - DWARFExpression Expression(Data, Unit->getAddressByteSize()); - Expression.printCompact(OS, MRI); - } -}; - -/// Helper class for printing source variable locations alongside disassembly. -class LiveVariablePrinter { - // Information we want to track about one column in which we are printing a - // variable live range. - struct Column { - unsigned VarIdx = NullVarIdx; - bool LiveIn = false; - bool LiveOut = false; - bool MustDrawLabel = false; - - bool isActive() const { return VarIdx != NullVarIdx; } - - static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max(); - }; - - // All live variables we know about in the object/image file. - std::vector<LiveVariable> LiveVariables; - - // The columns we are currently drawing. - IndexedMap<Column> ActiveCols; - - const MCRegisterInfo &MRI; - const MCSubtargetInfo &STI; - - void addVariable(DWARFDie FuncDie, DWARFDie VarDie) { - uint64_t FuncLowPC, FuncHighPC, SectionIndex; - FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex); - const char *VarName = VarDie.getName(DINameKind::ShortName); - DWARFUnit *U = VarDie.getDwarfUnit(); - - Expected<DWARFLocationExpressionsVector> Locs = - VarDie.getLocations(dwarf::DW_AT_location); - if (!Locs) { - // If the variable doesn't have any locations, just ignore it. We don't - // report an error or warning here as that could be noisy on optimised - // code. - consumeError(Locs.takeError()); - return; - } - - for (const DWARFLocationExpression &LocExpr : *Locs) { - if (LocExpr.Range) { - LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie); - } else { - // If the LocExpr does not have an associated range, it is valid for - // the whole of the function. - // TODO: technically it is not valid for any range covered by another - // LocExpr, does that happen in reality? - DWARFLocationExpression WholeFuncExpr{ - DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), - LocExpr.Expr}; - LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie); - } - } - } - - void addFunction(DWARFDie D) { - for (const DWARFDie &Child : D.children()) { - if (Child.getTag() == dwarf::DW_TAG_variable || - Child.getTag() == dwarf::DW_TAG_formal_parameter) - addVariable(D, Child); - else - addFunction(Child); - } - } - - // Get the column number (in characters) at which the first live variable - // line should be printed. - unsigned getIndentLevel() const { - return DbgIndent + getInstStartColumn(STI); - } - - // Indent to the first live-range column to the right of the currently - // printed line, and return the index of that column. - // TODO: formatted_raw_ostream uses "column" to mean a number of characters - // since the last \n, and we use it to mean the number of slots in which we - // put live variable lines. Pick a less overloaded word. - unsigned moveToFirstVarColumn(formatted_raw_ostream &OS) { - // Logical column number: column zero is the first column we print in, each - // logical column is 2 physical columns wide. - unsigned FirstUnprintedLogicalColumn = - std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0); - // Physical column number: the actual column number in characters, with - // zero being the left-most side of the screen. - unsigned FirstUnprintedPhysicalColumn = - getIndentLevel() + FirstUnprintedLogicalColumn * 2; - - if (FirstUnprintedPhysicalColumn > OS.getColumn()) - OS.PadToColumn(FirstUnprintedPhysicalColumn); - - return FirstUnprintedLogicalColumn; - } - - unsigned findFreeColumn() { - for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx) - if (!ActiveCols[ColIdx].isActive()) - return ColIdx; - - size_t OldSize = ActiveCols.size(); - ActiveCols.grow(std::max<size_t>(OldSize * 2, 1)); - return OldSize; - } - -public: - LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) - : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {} - - void dump() const { - for (const LiveVariable &LV : LiveVariables) { - dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": "; - LV.print(dbgs(), MRI); - dbgs() << "\n"; - } - } - - void addCompileUnit(DWARFDie D) { - if (D.getTag() == dwarf::DW_TAG_subprogram) - addFunction(D); - else - for (const DWARFDie &Child : D.children()) - addFunction(Child); - } - - /// Update to match the state of the instruction between ThisAddr and - /// NextAddr. In the common case, any live range active at ThisAddr is - /// live-in to the instruction, and any live range active at NextAddr is - /// live-out of the instruction. If IncludeDefinedVars is false, then live - /// ranges starting at NextAddr will be ignored. - void update(object::SectionedAddress ThisAddr, - object::SectionedAddress NextAddr, bool IncludeDefinedVars) { - // First, check variables which have already been assigned a column, so - // that we don't change their order. - SmallSet<unsigned, 8> CheckedVarIdxs; - for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { - if (!ActiveCols[ColIdx].isActive()) - continue; - CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx); - LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx]; - ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr); - ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr); - LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-" - << NextAddr.Address << ", " << LV.VarName << ", Col " - << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn - << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n"); - - if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) - ActiveCols[ColIdx].VarIdx = Column::NullVarIdx; - } - - // Next, look for variables which don't already have a column, but which - // are now live. - if (IncludeDefinedVars) { - for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End; - ++VarIdx) { - if (CheckedVarIdxs.count(VarIdx)) - continue; - LiveVariable &LV = LiveVariables[VarIdx]; - bool LiveIn = LV.liveAtAddress(ThisAddr); - bool LiveOut = LV.liveAtAddress(NextAddr); - if (!LiveIn && !LiveOut) - continue; - - unsigned ColIdx = findFreeColumn(); - LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-" - << NextAddr.Address << ", " << LV.VarName << ", Col " - << ColIdx << ": LiveIn=" << LiveIn - << ", LiveOut=" << LiveOut << "\n"); - ActiveCols[ColIdx].VarIdx = VarIdx; - ActiveCols[ColIdx].LiveIn = LiveIn; - ActiveCols[ColIdx].LiveOut = LiveOut; - ActiveCols[ColIdx].MustDrawLabel = true; - } - } - } - - enum class LineChar { - RangeStart, - RangeMid, - RangeEnd, - LabelVert, - LabelCornerNew, - LabelCornerActive, - LabelHoriz, - }; - const char *getLineChar(LineChar C) const { - bool IsASCII = DbgVariables == DVASCII; - switch (C) { - case LineChar::RangeStart: - return IsASCII ? "^" : (const char *)u8"\u2548"; - case LineChar::RangeMid: - return IsASCII ? "|" : (const char *)u8"\u2503"; - case LineChar::RangeEnd: - return IsASCII ? "v" : (const char *)u8"\u253b"; - case LineChar::LabelVert: - return IsASCII ? "|" : (const char *)u8"\u2502"; - case LineChar::LabelCornerNew: - return IsASCII ? "/" : (const char *)u8"\u250c"; - case LineChar::LabelCornerActive: - return IsASCII ? "|" : (const char *)u8"\u2520"; - case LineChar::LabelHoriz: - return IsASCII ? "-" : (const char *)u8"\u2500"; - } - llvm_unreachable("Unhandled LineChar enum"); - } - - /// Print live ranges to the right of an existing line. This assumes the - /// line is not an instruction, so doesn't start or end any live ranges, so - /// we only need to print active ranges or empty columns. If AfterInst is - /// true, this is being printed after the last instruction fed to update(), - /// otherwise this is being printed before it. - void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst) { - if (ActiveCols.size()) { - unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); - for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); - ColIdx < End; ++ColIdx) { - if (ActiveCols[ColIdx].isActive()) { - if ((AfterInst && ActiveCols[ColIdx].LiveOut) || - (!AfterInst && ActiveCols[ColIdx].LiveIn)) - OS << getLineChar(LineChar::RangeMid); - else if (!AfterInst && ActiveCols[ColIdx].LiveOut) - OS << getLineChar(LineChar::LabelVert); - else - OS << " "; - } - OS << " "; - } - } - OS << "\n"; - } - - /// Print any live variable range info needed to the right of a - /// non-instruction line of disassembly. This is where we print the variable - /// names and expressions, with thin line-drawing characters connecting them - /// to the live range which starts at the next instruction. If MustPrint is - /// true, we have to print at least one line (with the continuation of any - /// already-active live ranges) because something has already been printed - /// earlier on this line. - void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint) { - bool PrintedSomething = false; - for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { - if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) { - // First we need to print the live range markers for any active - // columns to the left of this one. - OS.PadToColumn(getIndentLevel()); - for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) { - if (ActiveCols[ColIdx2].isActive()) { - if (ActiveCols[ColIdx2].MustDrawLabel && - !ActiveCols[ColIdx2].LiveIn) - OS << getLineChar(LineChar::LabelVert) << " "; - else - OS << getLineChar(LineChar::RangeMid) << " "; - } else - OS << " "; - } - - // Then print the variable name and location of the new live range, - // with box drawing characters joining it to the live range line. - OS << getLineChar(ActiveCols[ColIdx].LiveIn - ? LineChar::LabelCornerActive - : LineChar::LabelCornerNew) - << getLineChar(LineChar::LabelHoriz) << " "; - WithColor(OS, raw_ostream::GREEN) - << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName; - OS << " = "; - { - WithColor ExprColor(OS, raw_ostream::CYAN); - LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI); - } - - // If there are any columns to the right of the expression we just - // printed, then continue their live range lines. - unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); - for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size(); - ColIdx2 < End; ++ColIdx2) { - if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn) - OS << getLineChar(LineChar::RangeMid) << " "; - else - OS << " "; - } - - OS << "\n"; - PrintedSomething = true; - } - } - - for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) - if (ActiveCols[ColIdx].isActive()) - ActiveCols[ColIdx].MustDrawLabel = false; - - // If we must print something (because we printed a line/column number), - // but don't have any new variables to print, then print a line which - // just continues any existing live ranges. - if (MustPrint && !PrintedSomething) - printAfterOtherLine(OS, false); - } - - /// Print the live variable ranges to the right of a disassembled instruction. - void printAfterInst(formatted_raw_ostream &OS) { - if (!ActiveCols.size()) - return; - unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); - for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); - ColIdx < End; ++ColIdx) { - if (!ActiveCols[ColIdx].isActive()) - OS << " "; - else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut) - OS << getLineChar(LineChar::RangeMid) << " "; - else if (ActiveCols[ColIdx].LiveOut) - OS << getLineChar(LineChar::RangeStart) << " "; - else if (ActiveCols[ColIdx].LiveIn) - OS << getLineChar(LineChar::RangeEnd) << " "; - else - llvm_unreachable("var must be live in or out!"); - } - } -}; - -class SourcePrinter { -protected: - DILineInfo OldLineInfo; - const ObjectFile *Obj = nullptr; - std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer; - // File name to file contents of source. - std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache; - // Mark the line endings of the cached source. - std::unordered_map<std::string, std::vector<StringRef>> LineCache; - // Keep track of missing sources. - StringSet<> MissingSources; - // Only emit 'invalid debug info' warning once. - bool WarnedInvalidDebugInfo = false; - -private: - bool cacheSource(const DILineInfo& LineInfoFile); - - void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo, - StringRef Delimiter, LiveVariablePrinter &LVP); - - void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, - StringRef ObjectFilename, StringRef Delimiter, - LiveVariablePrinter &LVP); - -public: - SourcePrinter() = default; - SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) : Obj(Obj) { - symbolize::LLVMSymbolizer::Options SymbolizerOpts; - SymbolizerOpts.PrintFunctions = - DILineInfoSpecifier::FunctionNameKind::LinkageName; - SymbolizerOpts.Demangle = Demangle; - SymbolizerOpts.DefaultArch = std::string(DefaultArch); - Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); - } - virtual ~SourcePrinter() = default; - virtual void printSourceLine(formatted_raw_ostream &OS, - object::SectionedAddress Address, - StringRef ObjectFilename, - LiveVariablePrinter &LVP, - StringRef Delimiter = "; "); -}; - -bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { - std::unique_ptr<MemoryBuffer> Buffer; - if (LineInfo.Source) { - Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source); - } else { - auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName); - if (!BufferOrError) { - if (MissingSources.insert(LineInfo.FileName).second) - reportWarning("failed to find source " + LineInfo.FileName, - Obj->getFileName()); - return false; - } - Buffer = std::move(*BufferOrError); - } - // Chomp the file to get lines - const char *BufferStart = Buffer->getBufferStart(), - *BufferEnd = Buffer->getBufferEnd(); - std::vector<StringRef> &Lines = LineCache[LineInfo.FileName]; - const char *Start = BufferStart; - for (const char *I = BufferStart; I != BufferEnd; ++I) - if (*I == '\n') { - Lines.emplace_back(Start, I - Start - (BufferStart < I && I[-1] == '\r')); - Start = I + 1; - } - if (Start < BufferEnd) - Lines.emplace_back(Start, BufferEnd - Start); - SourceCache[LineInfo.FileName] = std::move(Buffer); - return true; -} - -void SourcePrinter::printSourceLine(formatted_raw_ostream &OS, - object::SectionedAddress Address, - StringRef ObjectFilename, - LiveVariablePrinter &LVP, - StringRef Delimiter) { - if (!Symbolizer) - return; - - DILineInfo LineInfo = DILineInfo(); - Expected<DILineInfo> ExpectedLineInfo = - Symbolizer->symbolizeCode(*Obj, Address); - std::string ErrorMessage; - if (ExpectedLineInfo) { - LineInfo = *ExpectedLineInfo; - } else if (!WarnedInvalidDebugInfo) { - WarnedInvalidDebugInfo = true; - // TODO Untested. - reportWarning("failed to parse debug information: " + - toString(ExpectedLineInfo.takeError()), - ObjectFilename); - } - - if (!Prefix.empty() && sys::path::is_absolute_gnu(LineInfo.FileName)) { - SmallString<128> FilePath; - sys::path::append(FilePath, Prefix, LineInfo.FileName); - - LineInfo.FileName = std::string(FilePath); - } - - if (PrintLines) - printLines(OS, LineInfo, Delimiter, LVP); - if (PrintSource) - printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP); - OldLineInfo = LineInfo; -} - -void SourcePrinter::printLines(formatted_raw_ostream &OS, - const DILineInfo &LineInfo, StringRef Delimiter, - LiveVariablePrinter &LVP) { - bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString && - LineInfo.FunctionName != OldLineInfo.FunctionName; - if (PrintFunctionName) { - OS << Delimiter << LineInfo.FunctionName; - // If demangling is successful, FunctionName will end with "()". Print it - // only if demangling did not run or was unsuccessful. - if (!StringRef(LineInfo.FunctionName).endswith("()")) - OS << "()"; - OS << ":\n"; - } - if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 && - (OldLineInfo.Line != LineInfo.Line || - OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) { - OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line; - LVP.printBetweenInsts(OS, true); - } -} - -void SourcePrinter::printSources(formatted_raw_ostream &OS, - const DILineInfo &LineInfo, - StringRef ObjectFilename, StringRef Delimiter, - LiveVariablePrinter &LVP) { - if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || - (OldLineInfo.Line == LineInfo.Line && - OldLineInfo.FileName == LineInfo.FileName)) - return; - - if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) - if (!cacheSource(LineInfo)) - return; - auto LineBuffer = LineCache.find(LineInfo.FileName); - if (LineBuffer != LineCache.end()) { - if (LineInfo.Line > LineBuffer->second.size()) { - reportWarning( - formatv( - "debug info line number {0} exceeds the number of lines in {1}", - LineInfo.Line, LineInfo.FileName), - ObjectFilename); - return; - } - // Vector begins at 0, line numbers are non-zero - OS << Delimiter << LineBuffer->second[LineInfo.Line - 1]; - LVP.printBetweenInsts(OS, true); - } + return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; } static bool isAArch64Elf(const ObjectFile *Obj) { @@ -1133,9 +471,9 @@ LVP.printBetweenInsts(OS, false); size_t Start = OS.tell(); - if (!NoLeadingAddr) + if (LeadingAddr) OS << format("%8" PRIx64 ":", Address.Address); - if (!NoShowRawInsn) { + if (ShowRawInsn) { OS << ' '; dumpBytes(Bytes, OS); } @@ -1166,9 +504,9 @@ formatted_raw_ostream &OS) { uint32_t opcode = (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0]; - if (!NoLeadingAddr) + if (LeadingAddr) OS << format("%8" PRIx64 ":", Address); - if (!NoShowRawInsn) { + if (ShowRawInsn) { OS << "\t"; dumpBytes(Bytes.slice(0, 4), OS); OS << format("\t%08" PRIx32, opcode); @@ -1303,9 +641,9 @@ LiveVariablePrinter &LVP) override { if (SP && (PrintSource || PrintLines)) SP->printSourceLine(OS, Address, ObjectFilename, LVP); - if (!NoLeadingAddr) + if (LeadingAddr) OS << format("%8" PRId64 ":", Address.Address / 8); - if (!NoShowRawInsn) { + if (ShowRawInsn) { OS << "\t"; dumpBytes(Bytes, OS); } @@ -1398,6 +736,43 @@ llvm_unreachable("Unsupported binary format"); } +static Optional<SectionRef> getWasmCodeSection(const WasmObjectFile *Obj) { + for (auto SecI : Obj->sections()) { + const WasmSection &Section = Obj->getWasmSection(SecI); + if (Section.Type == wasm::WASM_SEC_CODE) + return SecI; + } + return None; +} + +static void +addMissingWasmCodeSymbols(const WasmObjectFile *Obj, + std::map<SectionRef, SectionSymbolsTy> &AllSymbols) { + Optional<SectionRef> Section = getWasmCodeSection(Obj); + if (!Section) + return; + SectionSymbolsTy &Symbols = AllSymbols[*Section]; + + std::set<uint64_t> SymbolAddresses; + for (const auto &Sym : Symbols) + SymbolAddresses.insert(Sym.Addr); + + for (const wasm::WasmFunction &Function : Obj->functions()) { + uint64_t Address = Function.CodeSectionOffset; + // Only add fallback symbols for functions not already present in the symbol + // table. + if (SymbolAddresses.count(Address)) + continue; + // This function has no symbol, so it should have no SymbolName. + assert(Function.SymbolName.empty()); + // We use DebugName for the name, though it may be empty if there is no + // "name" custom section, or that section is missing a name for this + // function. + StringRef Name = Function.DebugName; + Symbols.emplace_back(Address, Name, ELF::STT_NOTYPE); + } +} + static void addPltEntries(const ObjectFile *Obj, std::map<SectionRef, SectionSymbolsTy> &AllSymbols, StringSaver &Saver) { @@ -1473,8 +848,7 @@ if (Relocated == Obj.section_end() || !checkSectionFilter(*Relocated).Keep) continue; std::vector<RelocationRef> &V = Ret[*Relocated]; - for (const RelocationRef &R : Sec.relocations()) - V.push_back(R); + append_range(V, Sec.relocations()); // Sort relocations by address. llvm::stable_sort(V, isRelocAddressLess); } @@ -1635,6 +1009,63 @@ } } +// Create an MCSymbolizer for the target and add it to the MCDisassembler. +// This is currently only used on AMDGPU, and assumes the format of the +// void * argument passed to AMDGPU's createMCSymbolizer. +static void addSymbolizer( + MCContext &Ctx, const Target *Target, StringRef TripleName, + MCDisassembler *DisAsm, uint64_t SectionAddr, ArrayRef<uint8_t> Bytes, + SectionSymbolsTy &Symbols, + std::vector<std::unique_ptr<std::string>> &SynthesizedLabelNames) { + + std::unique_ptr<MCRelocationInfo> RelInfo( + Target->createMCRelocationInfo(TripleName, Ctx)); + if (!RelInfo) + return; + std::unique_ptr<MCSymbolizer> Symbolizer(Target->createMCSymbolizer( + TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo))); + MCSymbolizer *SymbolizerPtr = &*Symbolizer; + DisAsm->setSymbolizer(std::move(Symbolizer)); + + if (!SymbolizeOperands) + return; + + // Synthesize labels referenced by branch instructions by + // disassembling, discarding the output, and collecting the referenced + // addresses from the symbolizer. + for (size_t Index = 0; Index != Bytes.size();) { + MCInst Inst; + uint64_t Size; + DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), SectionAddr + Index, + nulls()); + if (Size == 0) + Size = 1; + Index += Size; + } + ArrayRef<uint64_t> LabelAddrsRef = SymbolizerPtr->getReferencedAddresses(); + // Copy and sort to remove duplicates. + std::vector<uint64_t> LabelAddrs; + LabelAddrs.insert(LabelAddrs.end(), LabelAddrsRef.begin(), + LabelAddrsRef.end()); + llvm::sort(LabelAddrs); + LabelAddrs.resize(std::unique(LabelAddrs.begin(), LabelAddrs.end()) - + LabelAddrs.begin()); + // Add the labels. + for (unsigned LabelNum = 0; LabelNum != LabelAddrs.size(); ++LabelNum) { + auto Name = std::make_unique<std::string>(); + *Name = (Twine("L") + Twine(LabelNum)).str(); + SynthesizedLabelNames.push_back(std::move(Name)); + Symbols.push_back(SymbolInfoTy( + LabelAddrs[LabelNum], *SynthesizedLabelNames.back(), ELF::STT_NOTYPE)); + } + llvm::stable_sort(Symbols); + // Recreate the symbolizer with the new symbols list. + RelInfo.reset(Target->createMCRelocationInfo(TripleName, Ctx)); + Symbolizer.reset(Target->createMCSymbolizer( + TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo))); + DisAsm->setSymbolizer(std::move(Symbolizer)); +} + static StringRef getSegmentName(const MachOObjectFile *MachO, const SectionRef &Section) { if (MachO) { @@ -1645,6 +1076,29 @@ return ""; } +static void emitPostInstructionInfo(formatted_raw_ostream &FOS, + const MCAsmInfo &MAI, + const MCSubtargetInfo &STI, + StringRef Comments, + LiveVariablePrinter &LVP) { + do { + if (!Comments.empty()) { + // Emit a line of comments. + StringRef Comment; + std::tie(Comment, Comments) = Comments.split('\n'); + // MAI.getCommentColumn() assumes that instructions are printed at the + // position of 8, while getInstStartColumn() returns the actual position. + unsigned CommentColumn = + MAI.getCommentColumn() - 8 + getInstStartColumn(STI); + FOS.PadToColumn(CommentColumn); + FOS << MAI.getCommentString() << ' ' << Comment; + } + LVP.printAfterInst(FOS); + FOS << '\n'; + } while (!Comments.empty()); + FOS.flush(); +} + static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, MCContext &Ctx, MCDisassembler *PrimaryDisAsm, MCDisassembler *SecondaryDisAsm, @@ -1682,10 +1136,15 @@ if (Obj->isELF() && getElfSymbolType(Obj, Symbol) == ELF::STT_SECTION) continue; - // Don't ask a Mach-O STAB symbol for its section unless you know that - // STAB symbol's section field refers to a valid section index. Otherwise - // the symbol may error trying to load a section that does not exist. if (MachO) { + // __mh_(execute|dylib|dylinker|bundle|preload|object)_header are special + // symbols that support MachO header introspection. They do not bind to + // code locations and are irrelevant for disassembly. + if (NameOrErr->startswith("__mh_") && NameOrErr->endswith("_header")) + continue; + // Don't ask a Mach-O STAB symbol for its section unless you know that + // STAB symbol's section field refers to a valid section index. Otherwise + // the symbol may error trying to load a section that does not exist. DataRefImpl SymDRI = Symbol.getRawDataRefImpl(); uint8_t NType = (MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type: @@ -1704,6 +1163,9 @@ if (AllSymbols.empty() && Obj->isELF()) addDynamicElfSymbols(Obj, AllSymbols); + if (Obj->isWasm()) + addMissingWasmCodeSymbols(cast<WasmObjectFile>(Obj), AllSymbols); + BumpPtrAllocator A; StringSaver Saver(A); addPltEntries(Obj, AllSymbols, Saver); @@ -1797,16 +1259,14 @@ llvm::sort(MappingSymbols); + ArrayRef<uint8_t> Bytes = arrayRefFromStringRef( + unwrapOrError(Section.getContents(), Obj->getFileName())); + + std::vector<std::unique_ptr<std::string>> SynthesizedLabelNames; if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) { // AMDGPU disassembler uses symbolizer for printing labels - std::unique_ptr<MCRelocationInfo> RelInfo( - TheTarget->createMCRelocationInfo(TripleName, Ctx)); - if (RelInfo) { - std::unique_ptr<MCSymbolizer> Symbolizer( - TheTarget->createMCSymbolizer( - TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo))); - DisAsm->setSymbolizer(std::move(Symbolizer)); - } + addSymbolizer(Ctx, TheTarget, TripleName, DisAsm, SectionAddr, Bytes, + Symbols, SynthesizedLabelNames); } StringRef SegmentName = getSegmentName(MachO, Section); @@ -1822,9 +1282,6 @@ SmallString<40> Comments; raw_svector_ostream CommentStream(Comments); - ArrayRef<uint8_t> Bytes = arrayRefFromStringRef( - unwrapOrError(Section.getContents(), Obj->getFileName())); - uint64_t VMAAdjustment = 0; if (shouldAdjustVA(Section)) VMAAdjustment = AdjustVMA; @@ -1871,7 +1328,7 @@ } outs() << '\n'; - if (!NoLeadingAddr) + if (LeadingAddr) outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ", SectionAddr + Start + VMAAdjustment); if (Obj->isXCOFF() && SymbolDescription) { @@ -2002,18 +1459,22 @@ LVP.update({Index, Section.getIndex()}, {Index + Size, Section.getIndex()}, Index + Size != End); + IP->setCommentStream(CommentStream); + PIP.printInst( *IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size), {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, FOS, "", *STI, &SP, Obj->getFileName(), &Rels, LVP); - FOS << CommentStream.str(); - Comments.clear(); + + IP->setCommentStream(llvm::nulls()); // If disassembly has failed, avoid analysing invalid/incomplete // instruction information. Otherwise, try to resolve the target // address (jump target or memory operand address) and print it on the // right of the instruction. if (Disassembled && MIA) { + // Branch targets are printed just after the instructions. + llvm::raw_ostream *TargetOS = &FOS; uint64_t Target; bool PrintTarget = MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target); @@ -2024,8 +1485,11 @@ Target = *MaybeTarget; PrintTarget = true; // Do not print real address when symbolizing. - if (!SymbolizeOperands) - FOS << " # " << Twine::utohexstr(Target); + if (!SymbolizeOperands) { + // Memory operand addresses are printed as comments. + TargetOS = &CommentStream; + *TargetOS << "0x" << Twine::utohexstr(Target); + } } if (PrintTarget) { // In a relocatable object, the target's section must reside in @@ -2084,28 +1548,34 @@ if (Demangle) TargetName = demangle(TargetName); - FOS << " <"; + *TargetOS << " <"; if (!Disp) { // Always Print the binary symbol precisely corresponding to // the target address. - FOS << TargetName; + *TargetOS << TargetName; } else if (!LabelAvailable) { // Always Print the binary symbol plus an offset if there's no // local label corresponding to the target address. - FOS << TargetName << "+0x" << Twine::utohexstr(Disp); + *TargetOS << TargetName << "+0x" << Twine::utohexstr(Disp); } else { - FOS << AllLabels[Target]; + *TargetOS << AllLabels[Target]; } - FOS << ">"; + *TargetOS << ">"; } else if (LabelAvailable) { - FOS << " <" << AllLabels[Target] << ">"; + *TargetOS << " <" << AllLabels[Target] << ">"; } + // By convention, each record in the comment stream should be + // terminated. + if (TargetOS == &CommentStream) + *TargetOS << "\n"; } } } - LVP.printAfterInst(FOS); - FOS << "\n"; + assert(Ctx.getAsmInfo()); + emitPostInstructionInfo(FOS, *Ctx.getAsmInfo(), *STI, + CommentStream.str(), LVP); + Comments.clear(); // Hexagon does this in pretty printer if (Obj->getArch() != Triple::hexagon) { @@ -2187,10 +1657,11 @@ if (!MII) reportError(Obj->getFileName(), "no instruction info for target " + TripleName); - MCObjectFileInfo MOFI; - MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI); + MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get()); // FIXME: for now initialize MCObjectFileInfo with default values - MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); + Ctx.setObjectFileInfo(MOFI.get()); std::unique_ptr<MCDisassembler> DisAsm( TheTarget->createMCDisassembler(*STI, Ctx)); @@ -2267,7 +1738,7 @@ for (std::pair<SectionRef, std::vector<SectionRef>> &P : SecToRelSec) { StringRef SecName = unwrapOrError(P.first.getName(), Obj->getFileName()); - outs() << "RELOCATION RECORDS FOR [" << SecName << "]:\n"; + outs() << "\nRELOCATION RECORDS FOR [" << SecName << "]:\n"; uint32_t OffsetPadding = (Obj->getBytesInAddress() > 4 ? 16 : 8); uint32_t TypePadding = 24; outs() << left_justify("OFFSET", OffsetPadding) << " " @@ -2290,7 +1761,6 @@ << "\n"; } } - outs() << "\n"; } } @@ -2350,16 +1820,13 @@ size_t NameWidth = getMaxSectionNameWidth(Obj); size_t AddressWidth = 2 * Obj->getBytesInAddress(); bool HasLMAColumn = shouldDisplayLMA(Obj); + outs() << "\nSections:\n"; if (HasLMAColumn) - outs() << "Sections:\n" - "Idx " - << left_justify("Name", NameWidth) << " Size " + outs() << "Idx " << left_justify("Name", NameWidth) << " Size " << left_justify("VMA", AddressWidth) << " " << left_justify("LMA", AddressWidth) << " Type\n"; else - outs() << "Sections:\n" - "Idx " - << left_justify("Name", NameWidth) << " Size " + outs() << "Idx " << left_justify("Name", NameWidth) << " Size " << left_justify("VMA", AddressWidth) << " Type\n"; uint64_t Idx; @@ -2373,9 +1840,11 @@ std::string Type = Section.isText() ? "TEXT" : ""; if (Section.isData()) - Type += Type.empty() ? "DATA" : " DATA"; + Type += Type.empty() ? "DATA" : ", DATA"; if (Section.isBSS()) - Type += Type.empty() ? "BSS" : " BSS"; + Type += Type.empty() ? "BSS" : ", BSS"; + if (Section.isDebugSection()) + Type += Type.empty() ? "DEBUG" : ", DEBUG"; if (HasLMAColumn) outs() << format("%3" PRIu64 " %-*s %08" PRIx64 " ", Idx, NameWidth, @@ -2388,7 +1857,6 @@ Name.str().c_str(), Size) << format_hex_no_prefix(VMA, AddressWidth) << " " << Type << "\n"; } - outs() << "\n"; } void objdump::printSectionContents(const ObjectFile *Obj) { @@ -2444,7 +1912,7 @@ void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName, StringRef ArchitectureName, bool DumpDynamic) { if (O->isCOFF() && !DumpDynamic) { - outs() << "SYMBOL TABLE:\n"; + outs() << "\nSYMBOL TABLE:\n"; printCOFFSymbolTable(cast<const COFFObjectFile>(O)); return; } @@ -2452,13 +1920,13 @@ const StringRef FileName = O->getFileName(); if (!DumpDynamic) { - outs() << "SYMBOL TABLE:\n"; + outs() << "\nSYMBOL TABLE:\n"; for (auto I = O->symbol_begin(); I != O->symbol_end(); ++I) printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic); return; } - outs() << "DYNAMIC SYMBOL TABLE:\n"; + outs() << "\nDYNAMIC SYMBOL TABLE:\n"; if (!O->isELF()) { reportWarning( "this operation is not currently supported for this file format", @@ -2730,7 +2198,7 @@ StringRef Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; outs() << "start address: " - << "0x" << format(Fmt.data(), Address) << "\n\n"; + << "0x" << format(Fmt.data(), Address) << "\n"; } static void printArchiveChild(StringRef Filename, const Archive::Child &C) { @@ -2803,11 +2271,11 @@ return; } - if (StartAddress.getNumOccurrences() == 0) + if (!HasStartAddressFlag) reportWarning("no section has address less than 0x" + Twine::utohexstr(Stop) + " specified by --stop-address", Obj->getFileName()); - else if (StopAddress.getNumOccurrences() == 0) + else if (!HasStopAddressFlag) reportWarning("no section has address greater than or equal to 0x" + Twine::utohexstr(Start) + " specified by --start-address", Obj->getFileName()); @@ -2827,10 +2295,10 @@ outs() << A->getFileName() << "(" << O->getFileName() << ")"; else outs() << O->getFileName(); - outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n\n"; + outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n"; } - if (StartAddress.getNumOccurrences() || StopAddress.getNumOccurrences()) + if (HasStartAddressFlag || HasStopAddressFlag) checkForInvalidStartStopAddress(O, StartAddress, StopAddress); // Note: the order here matches GNU objdump for compatability. @@ -2950,33 +2418,246 @@ reportError(errorCodeToError(object_error::invalid_file_type), file); } +template <typename T> +static void parseIntArg(const llvm::opt::InputArgList &InputArgs, int ID, + T &Value) { + if (const opt::Arg *A = InputArgs.getLastArg(ID)) { + StringRef V(A->getValue()); + if (!llvm::to_integer(V, Value, 0)) { + reportCmdLineError(A->getSpelling() + + ": expected a non-negative integer, but got '" + V + + "'"); + } + } +} + +static std::vector<std::string> +commaSeparatedValues(const llvm::opt::InputArgList &InputArgs, int ID) { + std::vector<std::string> Values; + for (StringRef Value : InputArgs.getAllArgValues(ID)) { + llvm::SmallVector<StringRef, 2> SplitValues; + llvm::SplitString(Value, SplitValues, ","); + for (StringRef SplitValue : SplitValues) + Values.push_back(SplitValue.str()); + } + return Values; +} + +static void parseOtoolOptions(const llvm::opt::InputArgList &InputArgs) { + MachOOpt = true; + FullLeadingAddr = true; + PrintImmHex = true; + + ArchName = InputArgs.getLastArgValue(OTOOL_arch).str(); + LinkOptHints = InputArgs.hasArg(OTOOL_C); + if (InputArgs.hasArg(OTOOL_d)) + FilterSections.push_back("__DATA,__data"); + DylibId = InputArgs.hasArg(OTOOL_D); + UniversalHeaders = InputArgs.hasArg(OTOOL_f); + DataInCode = InputArgs.hasArg(OTOOL_G); + FirstPrivateHeader = InputArgs.hasArg(OTOOL_h); + IndirectSymbols = InputArgs.hasArg(OTOOL_I); + ShowRawInsn = InputArgs.hasArg(OTOOL_j); + PrivateHeaders = InputArgs.hasArg(OTOOL_l); + DylibsUsed = InputArgs.hasArg(OTOOL_L); + MCPU = InputArgs.getLastArgValue(OTOOL_mcpu_EQ).str(); + ObjcMetaData = InputArgs.hasArg(OTOOL_o); + DisSymName = InputArgs.getLastArgValue(OTOOL_p).str(); + InfoPlist = InputArgs.hasArg(OTOOL_P); + Relocations = InputArgs.hasArg(OTOOL_r); + if (const Arg *A = InputArgs.getLastArg(OTOOL_s)) { + auto Filter = (A->getValue(0) + StringRef(",") + A->getValue(1)).str(); + FilterSections.push_back(Filter); + } + if (InputArgs.hasArg(OTOOL_t)) + FilterSections.push_back("__TEXT,__text"); + Verbose = InputArgs.hasArg(OTOOL_v) || InputArgs.hasArg(OTOOL_V) || + InputArgs.hasArg(OTOOL_o); + SymbolicOperands = InputArgs.hasArg(OTOOL_V); + if (InputArgs.hasArg(OTOOL_x)) + FilterSections.push_back(",__text"); + LeadingAddr = LeadingHeaders = !InputArgs.hasArg(OTOOL_X); + + InputFilenames = InputArgs.getAllArgValues(OTOOL_INPUT); + if (InputFilenames.empty()) + reportCmdLineError("no input file"); + + for (const Arg *A : InputArgs) { + const Option &O = A->getOption(); + if (O.getGroup().isValid() && O.getGroup().getID() == OTOOL_grp_obsolete) { + reportCmdLineWarning(O.getPrefixedName() + + " is obsolete and not implemented"); + } + } +} + +static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) { + parseIntArg(InputArgs, OBJDUMP_adjust_vma_EQ, AdjustVMA); + AllHeaders = InputArgs.hasArg(OBJDUMP_all_headers); + ArchName = InputArgs.getLastArgValue(OBJDUMP_arch_name_EQ).str(); + ArchiveHeaders = InputArgs.hasArg(OBJDUMP_archive_headers); + Demangle = InputArgs.hasArg(OBJDUMP_demangle); + Disassemble = InputArgs.hasArg(OBJDUMP_disassemble); + DisassembleAll = InputArgs.hasArg(OBJDUMP_disassemble_all); + SymbolDescription = InputArgs.hasArg(OBJDUMP_symbol_description); + DisassembleSymbols = + commaSeparatedValues(InputArgs, OBJDUMP_disassemble_symbols_EQ); + DisassembleZeroes = InputArgs.hasArg(OBJDUMP_disassemble_zeroes); + if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_dwarf_EQ)) { + DwarfDumpType = + StringSwitch<DIDumpType>(A->getValue()).Case("frames", DIDT_DebugFrame); + } + DynamicRelocations = InputArgs.hasArg(OBJDUMP_dynamic_reloc); + FaultMapSection = InputArgs.hasArg(OBJDUMP_fault_map_section); + FileHeaders = InputArgs.hasArg(OBJDUMP_file_headers); + SectionContents = InputArgs.hasArg(OBJDUMP_full_contents); + PrintLines = InputArgs.hasArg(OBJDUMP_line_numbers); + InputFilenames = InputArgs.getAllArgValues(OBJDUMP_INPUT); + MachOOpt = InputArgs.hasArg(OBJDUMP_macho); + MCPU = InputArgs.getLastArgValue(OBJDUMP_mcpu_EQ).str(); + MAttrs = commaSeparatedValues(InputArgs, OBJDUMP_mattr_EQ); + ShowRawInsn = !InputArgs.hasArg(OBJDUMP_no_show_raw_insn); + LeadingAddr = !InputArgs.hasArg(OBJDUMP_no_leading_addr); + RawClangAST = InputArgs.hasArg(OBJDUMP_raw_clang_ast); + Relocations = InputArgs.hasArg(OBJDUMP_reloc); + PrintImmHex = + InputArgs.hasFlag(OBJDUMP_print_imm_hex, OBJDUMP_no_print_imm_hex, false); + PrivateHeaders = InputArgs.hasArg(OBJDUMP_private_headers); + FilterSections = InputArgs.getAllArgValues(OBJDUMP_section_EQ); + SectionHeaders = InputArgs.hasArg(OBJDUMP_section_headers); + ShowLMA = InputArgs.hasArg(OBJDUMP_show_lma); + PrintSource = InputArgs.hasArg(OBJDUMP_source); + parseIntArg(InputArgs, OBJDUMP_start_address_EQ, StartAddress); + HasStartAddressFlag = InputArgs.hasArg(OBJDUMP_start_address_EQ); + parseIntArg(InputArgs, OBJDUMP_stop_address_EQ, StopAddress); + HasStopAddressFlag = InputArgs.hasArg(OBJDUMP_stop_address_EQ); + SymbolTable = InputArgs.hasArg(OBJDUMP_syms); + SymbolizeOperands = InputArgs.hasArg(OBJDUMP_symbolize_operands); + DynamicSymbolTable = InputArgs.hasArg(OBJDUMP_dynamic_syms); + TripleName = InputArgs.getLastArgValue(OBJDUMP_triple_EQ).str(); + UnwindInfo = InputArgs.hasArg(OBJDUMP_unwind_info); + Wide = InputArgs.hasArg(OBJDUMP_wide); + Prefix = InputArgs.getLastArgValue(OBJDUMP_prefix).str(); + parseIntArg(InputArgs, OBJDUMP_prefix_strip, PrefixStrip); + if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_debug_vars_EQ)) { + DbgVariables = StringSwitch<DebugVarsFormat>(A->getValue()) + .Case("ascii", DVASCII) + .Case("unicode", DVUnicode); + } + parseIntArg(InputArgs, OBJDUMP_debug_vars_indent_EQ, DbgIndent); + + parseMachOOptions(InputArgs); + + // Parse -M (--disassembler-options) and deprecated + // --x86-asm-syntax={att,intel}. + // + // Note, for x86, the asm dialect (AssemblerDialect) is initialized when the + // MCAsmInfo is constructed. MCInstPrinter::applyTargetSpecificCLOption is + // called too late. For now we have to use the internal cl::opt option. + const char *AsmSyntax = nullptr; + for (const auto *A : InputArgs.filtered(OBJDUMP_disassembler_options_EQ, + OBJDUMP_x86_asm_syntax_att, + OBJDUMP_x86_asm_syntax_intel)) { + switch (A->getOption().getID()) { + case OBJDUMP_x86_asm_syntax_att: + AsmSyntax = "--x86-asm-syntax=att"; + continue; + case OBJDUMP_x86_asm_syntax_intel: + AsmSyntax = "--x86-asm-syntax=intel"; + continue; + } + + SmallVector<StringRef, 2> Values; + llvm::SplitString(A->getValue(), Values, ","); + for (StringRef V : Values) { + if (V == "att") + AsmSyntax = "--x86-asm-syntax=att"; + else if (V == "intel") + AsmSyntax = "--x86-asm-syntax=intel"; + else + DisassemblerOptions.push_back(V.str()); + } + } + if (AsmSyntax) { + const char *Argv[] = {"llvm-objdump", AsmSyntax}; + llvm::cl::ParseCommandLineOptions(2, Argv); + } + + // objdump defaults to a.out if no filenames specified. + if (InputFilenames.empty()) + InputFilenames.push_back("a.out"); +} + int main(int argc, char **argv) { using namespace llvm; InitLLVM X(argc, argv); - const cl::OptionCategory *OptionFilters[] = {&ObjdumpCat, &MachOCat}; - cl::HideUnrelatedOptions(OptionFilters); + + ToolName = argv[0]; + std::unique_ptr<CommonOptTable> T; + OptSpecifier Unknown, HelpFlag, HelpHiddenFlag, VersionFlag; + + StringRef Stem = sys::path::stem(ToolName); + auto Is = [=](StringRef Tool) { + // We need to recognize the following filenames: + // + // llvm-objdump -> objdump + // llvm-otool-10.exe -> otool + // powerpc64-unknown-freebsd13-objdump -> objdump + auto I = Stem.rfind_insensitive(Tool); + return I != StringRef::npos && + (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); + }; + if (Is("otool")) { + T = std::make_unique<OtoolOptTable>(); + Unknown = OTOOL_UNKNOWN; + HelpFlag = OTOOL_help; + HelpHiddenFlag = OTOOL_help_hidden; + VersionFlag = OTOOL_version; + } else { + T = std::make_unique<ObjdumpOptTable>(); + Unknown = OBJDUMP_UNKNOWN; + HelpFlag = OBJDUMP_help; + HelpHiddenFlag = OBJDUMP_help_hidden; + VersionFlag = OBJDUMP_version; + } + + BumpPtrAllocator A; + StringSaver Saver(A); + opt::InputArgList InputArgs = + T->parseArgs(argc, argv, Unknown, Saver, + [&](StringRef Msg) { reportCmdLineError(Msg); }); + + if (InputArgs.size() == 0 || InputArgs.hasArg(HelpFlag)) { + T->printHelp(ToolName); + return 0; + } + if (InputArgs.hasArg(HelpHiddenFlag)) { + T->printHelp(ToolName, /*show_hidden=*/true); + return 0; + } // Initialize targets and assembly printers/parsers. InitializeAllTargetInfos(); InitializeAllTargetMCs(); InitializeAllDisassemblers(); - // Register the target printer for --version. - cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + if (InputArgs.hasArg(VersionFlag)) { + cl::PrintVersionMessage(); + if (!Is("otool")) { + outs() << '\n'; + TargetRegistry::printRegisteredTargetsForVersion(outs()); + } + return 0; + } - cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n", nullptr, - /*EnvVar=*/nullptr, - /*LongOptionsUseDoubleDash=*/true); + if (Is("otool")) + parseOtoolOptions(InputArgs); + else + parseObjdumpOptions(InputArgs); if (StartAddress >= StopAddress) reportCmdLineError("start address should be less than stop address"); - ToolName = argv[0]; - - // Defaults to a.out if no filenames specified. - if (InputFilenames.empty()) - InputFilenames.push_back("a.out"); - // Removes trailing separators from prefix. while (!Prefix.empty() && sys::path::is_separator(Prefix.back())) Prefix.pop_back(); @@ -2995,10 +2676,10 @@ !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !(MachOOpt && (Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie || - FirstPrivateHeader || IndirectSymbols || InfoPlist || LazyBind || - LinkOptHints || ObjcMetaData || Rebase || UniversalHeaders || - WeakBind || !FilterSections.empty()))) { - cl::PrintHelpMessage(); + FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist || + LazyBind || LinkOptHints || ObjcMetaData || Rebase || Rpaths || + UniversalHeaders || WeakBind || !FilterSections.empty()))) { + T->printHelp(ToolName); return 2; }
diff --git a/src/llvm-project/llvm/tools/llvm-objdump/llvm-objdump.h b/src/llvm-project/llvm/tools/llvm-objdump/llvm-objdump.h index 99bf191..33fb3f2 100644 --- a/src/llvm-project/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/src/llvm-project/llvm/tools/llvm-objdump/llvm-objdump.h
@@ -1,3 +1,4 @@ +//===--- llvm-objdump.h -----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,7 +13,6 @@ #include "llvm/DebugInfo/DIContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/Object/Archive.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DataTypes.h" @@ -30,26 +30,37 @@ namespace objdump { -extern cl::opt<bool> ArchiveHeaders; -extern cl::opt<bool> Demangle; -extern cl::opt<bool> Disassemble; -extern cl::opt<bool> DisassembleAll; -extern cl::opt<DIDumpType> DwarfDumpType; -extern cl::list<std::string> FilterSections; -extern cl::list<std::string> MAttrs; -extern cl::opt<std::string> MCPU; -extern cl::opt<bool> NoShowRawInsn; -extern cl::opt<bool> NoLeadingAddr; -extern cl::opt<std::string> Prefix; -extern cl::opt<bool> PrintImmHex; -extern cl::opt<bool> PrivateHeaders; -extern cl::opt<bool> Relocations; -extern cl::opt<bool> SectionHeaders; -extern cl::opt<bool> SectionContents; -extern cl::opt<bool> SymbolDescription; -extern cl::opt<bool> SymbolTable; -extern cl::opt<std::string> TripleName; -extern cl::opt<bool> UnwindInfo; +enum DebugVarsFormat { + DVDisabled, + DVUnicode, + DVASCII, +}; + +extern bool ArchiveHeaders; +extern int DbgIndent; +extern DebugVarsFormat DbgVariables; +extern bool Demangle; +extern bool Disassemble; +extern bool DisassembleAll; +extern DIDumpType DwarfDumpType; +extern std::vector<std::string> FilterSections; +extern bool LeadingAddr; +extern std::vector<std::string> MAttrs; +extern std::string MCPU; +extern std::string Prefix; +extern uint32_t PrefixStrip; +extern bool PrintImmHex; +extern bool PrintLines; +extern bool PrintSource; +extern bool PrivateHeaders; +extern bool Relocations; +extern bool SectionHeaders; +extern bool SectionContents; +extern bool ShowRawInsn; +extern bool SymbolDescription; +extern bool SymbolTable; +extern std::string TripleName; +extern bool UnwindInfo; extern StringSet<> FoundSectionSet;
diff --git a/src/llvm-project/llvm/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp b/src/llvm-project/llvm/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp index dbe396d..d737f63 100644 --- a/src/llvm-project/llvm/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp +++ b/src/llvm-project/llvm/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp
@@ -136,7 +136,7 @@ // Create pass pipeline // - PassBuilder PB(false, TM.get()); + PassBuilder PB(TM.get()); LoopAnalysisManager LAM; FunctionAnalysisManager FAM; @@ -243,7 +243,7 @@ exit(1); } - PassBuilder PB(false, TM.get()); + PassBuilder PB(TM.get()); ModulePassManager MPM; if (auto Err = PB.parsePassPipeline(MPM, PassPipeline)) { errs() << *argv[0] << ": " << toString(std::move(Err)) << "\n";
diff --git a/src/llvm-project/llvm/tools/llvm-opt-report/OptReport.cpp b/src/llvm-project/llvm/tools/llvm-opt-report/OptReport.cpp index 8d7ecfb..c47212d 100644 --- a/src/llvm-project/llvm/tools/llvm-opt-report/OptReport.cpp +++ b/src/llvm-project/llvm/tools/llvm-opt-report/OptReport.cpp
@@ -247,7 +247,7 @@ static bool writeReport(LocationInfoTy &LocationInfo) { std::error_code EC; - llvm::raw_fd_ostream OS(OutputFileName, EC, llvm::sys::fs::OF_Text); + llvm::raw_fd_ostream OS(OutputFileName, EC, llvm::sys::fs::OF_TextWithCRLF); if (EC) { WithColor::error() << "Can't open file " << OutputFileName << ": " << EC.message() << "\n";
diff --git a/src/llvm-project/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/src/llvm-project/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp index babdb56..ef299ea 100644 --- a/src/llvm-project/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/src/llvm-project/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
@@ -350,13 +350,13 @@ StringRef Name = Group.name(); if (Name.startswith("Import:")) return false; - if (Name.endswith_lower(".dll")) + if (Name.endswith_insensitive(".dll")) return false; - if (Name.equals_lower("* linker *")) + if (Name.equals_insensitive("* linker *")) return false; - if (Name.startswith_lower("f:\\binaries\\Intermediate\\vctools")) + if (Name.startswith_insensitive("f:\\binaries\\Intermediate\\vctools")) return false; - if (Name.startswith_lower("f:\\dd\\vctools\\crt")) + if (Name.startswith_insensitive("f:\\dd\\vctools\\crt")) return false; return true; }
diff --git a/src/llvm-project/llvm/tools/llvm-pdbutil/InputFile.cpp b/src/llvm-project/llvm/tools/llvm-pdbutil/InputFile.cpp index b316882..40b3562 100644 --- a/src/llvm-project/llvm/tools/llvm-pdbutil/InputFile.cpp +++ b/src/llvm-project/llvm/tools/llvm-pdbutil/InputFile.cpp
@@ -288,7 +288,8 @@ formatv("File {0} is not a supported file type", Path), inconvertibleErrorCode()); - auto Result = MemoryBuffer::getFile(Path, -1LL, false); + auto Result = MemoryBuffer::getFile(Path, /*IsText=*/false, + /*RequiresNullTerminator=*/false); if (!Result) return make_error<StringError>( formatv("File {0} could not be opened", Path), Result.getError());
diff --git a/src/llvm-project/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/src/llvm-project/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp index 787785c..3e8b1e8 100644 --- a/src/llvm-project/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/src/llvm-project/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
@@ -261,6 +261,9 @@ RETURN_CASE(CPUType, ARM_WMMX, "arm wmmx"); RETURN_CASE(CPUType, ARM7, "arm 7"); RETURN_CASE(CPUType, ARM64, "arm64"); + RETURN_CASE(CPUType, ARM64EC, "arm64ec"); + RETURN_CASE(CPUType, ARM64X, "arm64x"); + RETURN_CASE(CPUType, HybridX86ARM64, "hybrid x86 arm64"); RETURN_CASE(CPUType, Omni, "omni"); RETURN_CASE(CPUType, Ia64, "intel itanium ia64"); RETURN_CASE(CPUType, Ia64_2, "intel itanium ia64 2"); @@ -559,7 +562,7 @@ P.format(" `{0}`", Constant.Name); AutoIndent Indent(P, 7); P.formatLine("type = {0}, value = {1}", typeIndex(Constant.Type), - Constant.Value.toString(10)); + toString(Constant.Value, 10)); return Error::success(); }
diff --git a/src/llvm-project/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/src/llvm-project/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp index 8e46a97..08006e9 100644 --- a/src/llvm-project/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/src/llvm-project/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
@@ -557,7 +557,7 @@ Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, EnumeratorRecord &Enum) { P.format(" [{0} = {1}]", Enum.Name, - Enum.Value.toString(10, Enum.Value.isSigned())); + toString(Enum.Value, 10, Enum.Value.isSigned())); return Error::success(); }
diff --git a/src/llvm-project/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/src/llvm-project/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp index 19f4880..b152ebd 100644 --- a/src/llvm-project/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/src/llvm-project/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -748,7 +748,7 @@ static void yamlToPdb(StringRef Path) { BumpPtrAllocator Allocator; ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = - MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + MemoryBuffer::getFileOrSTDIN(Path, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (ErrorOrBuffer.getError()) { @@ -868,7 +868,6 @@ auto &File = loadPDB(Path, Session); auto O = std::make_unique<YAMLOutputStyle>(File); - O = std::make_unique<YAMLOutputStyle>(File); ExitOnErr(O->dump()); } @@ -1431,6 +1430,8 @@ InitLLVM X(Argc, Argv); ExitOnErr.setBanner("llvm-pdbutil: "); + cl::HideUnrelatedOptions( + {&opts::TypeCategory, &opts::FilterCategory, &opts::OtherOptions}); cl::ParseCommandLineOptions(Argc, Argv, "LLVM PDB Dumper\n"); if (opts::BytesSubcommand) {
diff --git a/src/llvm-project/llvm/tools/llvm-profdata/llvm-profdata.cpp b/src/llvm-project/llvm/tools/llvm-profdata/llvm-profdata.cpp index 7e53c30..66d7012 100644 --- a/src/llvm-project/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/src/llvm-project/llvm/tools/llvm-profdata/llvm-profdata.cpp
@@ -20,6 +20,7 @@ #include "llvm/ProfileData/SampleProfReader.h" #include "llvm/ProfileData/SampleProfWriter.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Discriminator.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" @@ -54,6 +55,14 @@ WithColor::note() << Hint << "\n"; } +static void warn(Error E, StringRef Whence = "") { + if (E.isA<InstrProfError>()) { + handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { + warn(IPE.message(), std::string(Whence), std::string("")); + }); + } +} + static void exitWithError(Twine Message, std::string Whence = "", std::string Hint = "") { WithColor::error(); @@ -243,7 +252,8 @@ auto Reader = std::move(ReaderOrErr.get()); bool IsIRProfile = Reader->isIRLevelProfile(); bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); - if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { + if (Error E = WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { + consumeError(std::move(E)); WC->Errors.emplace_back( make_error<StringError>( "Merge IR generated profile with Clang generated profile.", @@ -297,16 +307,19 @@ InstrProfWriter &Writer) { std::error_code EC; raw_fd_ostream Output(OutputFilename.data(), EC, - OutputFormat == PF_Text ? sys::fs::OF_Text + OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF : sys::fs::OF_None); if (EC) exitWithErrorCode(EC, OutputFilename); if (OutputFormat == PF_Text) { if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); + warn(std::move(E)); } else { - Writer.write(Output); + if (Output.is_displayed()) + exitWithError("cannot write a non-text format profile to the terminal"); + if (Error E = Writer.write(Output)) + warn(std::move(E)); } } @@ -315,12 +328,9 @@ StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, unsigned NumThreads, FailureMode FailMode) { - if (OutputFilename.compare("-") == 0) - exitWithError("Cannot write indexed profdata format to stdout."); - if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) - exitWithError("Unknown format is specified."); + exitWithError("unknown format is specified"); std::mutex ErrorLock; SmallSet<instrprof_error, 4> WriterErrorCodes; @@ -383,7 +393,7 @@ } if (NumErrors == Inputs.size() || (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) - exitWithError("No profiles could be merged."); + exitWithError("no profile can be merged"); writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); } @@ -441,6 +451,25 @@ const uint64_t ColdPercentileIdx = 15; const uint64_t HotPercentileIdx = 11; +using sampleprof::FSDiscriminatorPass; + +// Internal options to set FSDiscriminatorPass. Used in merge and show +// commands. +static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption( + "fs-discriminator-pass", cl::init(PassLast), cl::Hidden, + cl::desc("Zero out the discriminator bits for the FS discrimiantor " + "pass beyond this value. The enum values are defined in " + "Support/Discriminator.h"), + cl::values(clEnumVal(Base, "Use base discriminators only"), + clEnumVal(Pass1, "Use base and pass 1 discriminators"), + clEnumVal(Pass2, "Use base and pass 1-2 discriminators"), + clEnumVal(Pass3, "Use base and pass 1-3 discriminators"), + clEnumVal(PassLast, "Use all discriminator bits (default)"))); + +static unsigned getDiscriminatorMask() { + return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue())); +} + /// Adjust the instr profile in \p WC based on the sample profile in /// \p Reader. static void @@ -522,18 +551,18 @@ unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, unsigned InstrProfColdThreshold) { if (OutputFilename.compare("-") == 0) - exitWithError("Cannot write indexed profdata format to stdout."); + exitWithError("cannot write indexed profdata format to stdout"); if (Inputs.size() != 1) - exitWithError("Expect one input to be an instr profile."); + exitWithError("expect one input to be an instr profile"); if (Inputs[0].Weight != 1) - exitWithError("Expect instr profile doesn't have weight."); + exitWithError("expect instr profile doesn't have weight"); StringRef InstrFilename = Inputs[0].Filename; // Read sample profile. LLVMContext Context; - auto ReaderOrErr = - sampleprof::SampleProfileReader::create(SampleFilename.str(), Context); + auto ReaderOrErr = sampleprof::SampleProfileReader::create( + SampleFilename.str(), Context, FSDiscriminatorPassOption); if (std::error_code EC = ReaderOrErr.getError()) exitWithErrorCode(EC, SampleFilename); auto Reader = std::move(ReaderOrErr.get()); @@ -564,12 +593,13 @@ Result.addTotalSamples(Samples.getTotalSamples()); Result.addHeadSamples(Samples.getHeadSamples()); for (const auto &BodySample : Samples.getBodySamples()) { - Result.addBodySamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, + uint32_t MaskedDiscriminator = + BodySample.first.Discriminator & getDiscriminatorMask(); + Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator, BodySample.second.getSamples()); for (const auto &Target : BodySample.second.getCallTargets()) { Result.addCalledTargetSamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, + MaskedDiscriminator, Remapper(Target.first()), Target.second); } } @@ -656,15 +686,19 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, StringRef OutputFilename, ProfileFormat OutputFormat, StringRef ProfileSymbolListFile, bool CompressAllSections, - bool UseMD5, bool GenPartialProfile, FailureMode FailMode) { + bool UseMD5, bool GenPartialProfile, + bool SampleMergeColdContext, bool SampleTrimColdContext, + bool SampleColdContextFrameDepth, FailureMode FailMode) { using namespace sampleprof; StringMap<FunctionSamples> ProfileMap; SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; LLVMContext Context; sampleprof::ProfileSymbolList WriterList; Optional<bool> ProfileIsProbeBased; + Optional<bool> ProfileIsCS; for (const auto &Input : Inputs) { - auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); + auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, + FSDiscriminatorPassOption); if (std::error_code EC = ReaderOrErr.getError()) { warnOrExitGivenError(FailMode, EC, Input.Filename); continue; @@ -683,11 +717,14 @@ } StringMap<FunctionSamples> &Profiles = Reader->getProfiles(); - if (ProfileIsProbeBased && + if (ProfileIsProbeBased.hasValue() && ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased) exitWithError( "cannot merge probe-based profile with non-probe-based profile"); ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased; + if (ProfileIsCS.hasValue() && ProfileIsCS != FunctionSamples::ProfileIsCS) + exitWithError("cannot merge CS profile with non-CS profile"); + ProfileIsCS = FunctionSamples::ProfileIsCS; for (StringMap<FunctionSamples>::iterator I = Profiles.begin(), E = Profiles.end(); I != E; ++I) { @@ -696,7 +733,7 @@ Remapper ? remapSamples(I->second, *Remapper, Result) : FunctionSamples(); FunctionSamples &Samples = Remapper ? Remapped : I->second; - StringRef FName = Samples.getNameWithContext(true); + StringRef FName = Samples.getNameWithContext(); MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); if (Result != sampleprof_error::success) { std::error_code EC = make_error_code(Result); @@ -709,6 +746,22 @@ if (ReaderList) WriterList.merge(*ReaderList); } + + if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) { + // Use threshold calculated from profile summary unless specified. + SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); + auto Summary = Builder.computeSummaryForProfiles(ProfileMap); + uint64_t SampleProfColdThreshold = + ProfileSummaryBuilder::getColdCountThreshold( + (Summary->getDetailedSummary())); + + // Trim and merge cold context profile using cold threshold above; + SampleContextTrimmer(ProfileMap) + .trimAndMergeColdContextProfiles( + SampleProfColdThreshold, SampleTrimColdContext, + SampleMergeColdContext, SampleColdContextFrameDepth); + } + auto WriterOrErr = SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); if (std::error_code EC = WriterOrErr.getError()) @@ -720,7 +773,8 @@ auto Buffer = getInputFileBuf(ProfileSymbolListFile); handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, CompressAllSections, UseMD5, GenPartialProfile); - Writer->write(ProfileMap); + if (std::error_code EC = Writer->write(ProfileMap)) + exitWithErrorCode(std::move(EC)); } static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { @@ -729,7 +783,7 @@ uint64_t Weight; if (WeightStr.getAsInteger(10, Weight) || Weight < 1) - exitWithError("Input weight must be a positive integer."); + exitWithError("input weight must be a positive integer"); return {std::string(FileName), Weight}; } @@ -808,8 +862,7 @@ cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), cl::aliasopt(RemappingFile)); cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::Required, - cl::desc("Output file")); + cl::init("-"), cl::desc("Output file")); cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), cl::aliasopt(OutputFilename)); cl::opt<ProfileKinds> ProfileKind( @@ -851,6 +904,18 @@ "use-md5", cl::init(false), cl::Hidden, cl::desc("Choose to use MD5 to represent string in name table (only " "meaningful for -extbinary)")); + cl::opt<bool> SampleMergeColdContext( + "sample-merge-cold-context", cl::init(false), cl::Hidden, + cl::desc( + "Merge context sample profiles whose count is below cold threshold")); + cl::opt<bool> SampleTrimColdContext( + "sample-trim-cold-context", cl::init(false), cl::Hidden, + cl::desc( + "Trim context sample profiles whose count is below cold threshold")); + cl::opt<uint32_t> SampleColdContextFrameDepth( + "sample-frame-depth-for-cold-context", cl::init(1), cl::ZeroOrMore, + cl::desc("Keep the last K frames while merging cold profile. 1 means the " + "context-less base profile")); cl::opt<bool> GenPartialProfile( "gen-partial-profile", cl::init(false), cl::Hidden, cl::desc("Generate a partial profile (only meaningful for -extbinary)")); @@ -866,16 +931,16 @@ "sample profile, if the ratio of the number of zero counters " "divided by the the total number of counters is above the " "threshold, the profile of the function will be regarded as " - "being harmful for performance and will be dropped. ")); + "being harmful for performance and will be dropped.")); cl::opt<unsigned> SupplMinSizeThreshold( "suppl-min-size-threshold", cl::init(10), cl::Hidden, cl::desc("If the size of a function is smaller than the threshold, " "assume it can be inlined by PGO early inliner and it won't " - "be adjusted based on sample profile. ")); + "be adjusted based on sample profile.")); cl::opt<unsigned> InstrProfColdThreshold( "instr-prof-cold-threshold", cl::init(0), cl::Hidden, cl::desc("User specified cold threshold for instr profile which will " - "override the cold threshold got from profile summary. ")); + "override the cold threshold got from profile summary.")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); @@ -891,7 +956,7 @@ parseInputFilenamesFile(Buffer.get(), WeightedInputs); if (WeightedInputs.empty()) - exitWithError("No input files specified. See " + + exitWithError("no input files specified. See " + sys::path::filename(argv[0]) + " -help"); if (DumpInputFileList) { @@ -921,7 +986,9 @@ else mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, OutputFormat, ProfileSymbolListFile, CompressAllSections, - UseMD5, GenPartialProfile, FailureMode); + UseMD5, GenPartialProfile, SampleMergeColdContext, + SampleTrimColdContext, SampleColdContextFrameDepth, + FailureMode); return 0; } @@ -938,7 +1005,7 @@ OverlapStats Overlap; Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); if (E) - exitWithError(std::move(E), "Error in getting profile count sums"); + exitWithError(std::move(E), "error in getting profile count sums"); if (Overlap.Base.CountSum < 1.0f) { OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; exit(0); @@ -1548,14 +1615,15 @@ StringMap<const FunctionSamples *> BaseFuncProf; const auto &BaseProfiles = BaseReader->getProfiles(); for (const auto &BaseFunc : BaseProfiles) { - BaseFuncProf.try_emplace(BaseFunc.second.getName(), &(BaseFunc.second)); + BaseFuncProf.try_emplace(BaseFunc.second.getNameWithContext(), + &(BaseFunc.second)); } ProfOverlap.UnionCount = BaseFuncProf.size(); const auto &TestProfiles = TestReader->getProfiles(); for (const auto &TestFunc : TestProfiles) { SampleOverlapStats FuncOverlap; - FuncOverlap.TestName = TestFunc.second.getName(); + FuncOverlap.TestName = TestFunc.second.getNameWithContext(); assert(TestStats.count(FuncOverlap.TestName) && "TestStats should have records for all functions in test profile " "except inlinees"); @@ -1582,7 +1650,7 @@ // Two functions match with each other. Compute function-level overlap and // aggregate them into profile-level overlap. - FuncOverlap.BaseName = Match->second->getName(); + FuncOverlap.BaseName = Match->second->getNameWithContext(); assert(BaseStats.count(FuncOverlap.BaseName) && "BaseStats should have records for all functions in base profile " "except inlinees"); @@ -1631,10 +1699,11 @@ // Traverse through functions in base profile but not in test profile. for (const auto &F : BaseFuncProf) { - assert(BaseStats.count(F.second->getName()) && + assert(BaseStats.count(F.second->getNameWithContext()) && "BaseStats should have records for all functions in base profile " "except inlinees"); - const FuncSampleStats &FuncStats = BaseStats[F.second->getName()]; + const FuncSampleStats &FuncStats = + BaseStats[F.second->getNameWithContext()]; ++ProfOverlap.BaseUniqueCount; ProfOverlap.BaseUniqueSample += FuncStats.SampleSum; @@ -1665,7 +1734,7 @@ FuncSampleStats FuncStats; getFuncSampleStats(I.second, FuncStats, BaseHotThreshold); ProfOverlap.BaseSample += FuncStats.SampleSum; - BaseStats.try_emplace(I.second.getName(), FuncStats); + BaseStats.try_emplace(I.second.getNameWithContext(), FuncStats); } const auto &TestProf = TestReader->getProfiles(); @@ -1674,7 +1743,7 @@ FuncSampleStats FuncStats; getFuncSampleStats(I.second, FuncStats, TestHotThreshold); ProfOverlap.TestSample += FuncStats.SampleSum; - TestStats.try_emplace(I.second.getName(), FuncStats); + TestStats.try_emplace(I.second.getNameWithContext(), FuncStats); } ProfOverlap.BaseName = StringRef(BaseFilename); @@ -1815,11 +1884,13 @@ using namespace sampleprof; LLVMContext Context; - auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context); + auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, + FSDiscriminatorPassOption); if (std::error_code EC = BaseReaderOrErr.getError()) exitWithErrorCode(EC, BaseFilename); - auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context); + auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, + FSDiscriminatorPassOption); if (std::error_code EC = TestReaderOrErr.getError()) exitWithErrorCode(EC, TestFilename); @@ -1833,6 +1904,8 @@ if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased()) exitWithError( "cannot compare probe-based profile with non-probe-based profile"); + if (BaseReader->profileIsCS() != TestReader->profileIsCS()) + exitWithError("cannot compare CS profile with non-CS profile"); // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in // profile summary. @@ -1888,21 +1961,24 @@ cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"), cl::desc("Output file")); cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); - cl::opt<bool> IsCS("cs", cl::init(false), - cl::desc("For context sensitive counts")); + cl::opt<bool> IsCS( + "cs", cl::init(false), + cl::desc("For context sensitive PGO counts. Does not work with CSSPGO.")); cl::opt<unsigned long long> ValueCutoff( "value-cutoff", cl::init(-1), cl::desc( - "Function level overlap information for every function in test " + "Function level overlap information for every function (with calling " + "context for csspgo) in test " "profile with max count value greater then the parameter value")); cl::opt<std::string> FuncNameFilter( "function", - cl::desc("Function level overlap information for matching functions")); + cl::desc("Function level overlap information for matching functions. For " + "CSSPGO this takes a a function name with calling context")); cl::opt<unsigned long long> SimilarityCutoff( "similarity-cutoff", cl::init(0), - cl::desc( - "For sample profiles, list function names for overlapped functions " - "with similarities below the cutoff (percentage times 10000).")); + cl::desc("For sample profiles, list function names (with calling context " + "for csspgo) for overlapped functions " + "with similarities below the cutoff (percentage times 10000).")); cl::opt<ProfileKinds> ProfileKind( cl::desc("Profile kind:"), cl::init(instr), cl::values(clEnumVal(instr, "Instrumentation profile (default)"), @@ -1910,7 +1986,7 @@ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); std::error_code EC; - raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text); + raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF); if (EC) exitWithErrorCode(EC, Output); @@ -1926,7 +2002,8 @@ return 0; } -typedef struct ValueSitesStats { +namespace { +struct ValueSitesStats { ValueSitesStats() : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), TotalNumValues(0) {} @@ -1934,7 +2011,8 @@ uint64_t TotalNumValueSitesWithValueProfile; uint64_t TotalNumValues; std::vector<unsigned> ValueSitesHistogram; -} ValueSitesStats; +}; +} // namespace static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, ValueSitesStats &Stats, raw_fd_ostream &OS, @@ -1991,7 +2069,7 @@ bool ShowAllFunctions, bool ShowCS, uint64_t ValueCutoff, bool OnlyListBelow, const std::string &ShowFunction, bool TextFormat, - raw_fd_ostream &OS) { + bool ShowBinaryIds, raw_fd_ostream &OS) { auto ReaderOrErr = InstrProfReader::create(Filename); std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs); if (ShowDetailedSummary && Cutoffs.empty()) { @@ -2173,6 +2251,11 @@ OS << "Total count: " << PS->getTotalCount() << "\n"; PS->printDetailedSummary(OS); } + + if (ShowBinaryIds) + if (Error E = Reader->printBinaryIds(OS)) + exitWithError(std::move(E), Filename); + return 0; } @@ -2307,9 +2390,9 @@ (ProfileTotalSample > 0) ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample : 0; - PrintValues.emplace_back( - HotFuncInfo(Func.getName(), Func.getTotalSamples(), TotalSamplePercent, - FuncPair.second.second, Func.getEntrySamples())); + PrintValues.emplace_back(HotFuncInfo( + Func.getNameWithContext(), Func.getTotalSamples(), TotalSamplePercent, + FuncPair.second.second, Func.getEntrySamples())); } dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, Profiles.size(), HotFuncSample, ProfileTotalSample, @@ -2326,12 +2409,12 @@ raw_fd_ostream &OS) { using namespace sampleprof; LLVMContext Context; - auto ReaderOrErr = SampleProfileReader::create(Filename, Context); + auto ReaderOrErr = + SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption); if (std::error_code EC = ReaderOrErr.getError()) exitWithErrorCode(EC, Filename); auto Reader = std::move(ReaderOrErr.get()); - if (ShowSectionInfoOnly) { showSectionInfo(Reader.get(), OS); return 0; @@ -2423,12 +2506,11 @@ cl::desc("Show the information of each section in the sample profile. " "The flag is only usable when the sample profile is in " "extbinary format")); + cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false), + cl::desc("Show binary ids in the profile. ")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - if (OutputFilename.empty()) - OutputFilename = "-"; - if (Filename == OutputFilename) { errs() << sys::path::filename(argv[0]) << ": Input file name cannot be the same as the output file name!\n"; @@ -2436,7 +2518,7 @@ } std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); if (EC) exitWithErrorCode(EC, OutputFilename); @@ -2444,11 +2526,11 @@ WithColor::warning() << "-function argument ignored: showing all functions\n"; if (ProfileKind == instr) - return showInstrProfile(Filename, ShowCounts, TopNFunctions, - ShowIndirectCallTargets, ShowMemOPSizes, - ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ShowCS, ValueCutoff, - OnlyListBelow, ShowFunction, TextFormat, OS); + return showInstrProfile( + Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, + ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, + ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction, + TextFormat, ShowBinaryIds, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, ShowDetailedSummary, ShowFunction,
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-profgen/CMakeLists.txt index e7705eb..949b45f 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-profgen/CMakeLists.txt
@@ -5,6 +5,7 @@ AllTargetsInfos Core MC + IPO MCDisassembler Object ProfileData @@ -15,6 +16,7 @@ add_llvm_tool(llvm-profgen llvm-profgen.cpp PerfReader.cpp + CSPreInliner.cpp ProfiledBinary.cpp ProfileGenerator.cpp PseudoProbe.cpp
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/CSPreInliner.cpp b/src/llvm-project/llvm/tools/llvm-profgen/CSPreInliner.cpp new file mode 100644 index 0000000..23bdba8 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-profgen/CSPreInliner.cpp
@@ -0,0 +1,224 @@ +//===-- CSPreInliner.cpp - Profile guided preinliner -------------- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CSPreInliner.h" +#include "llvm/ADT/SCCIterator.h" +#include <cstdint> +#include <queue> + +#define DEBUG_TYPE "cs-preinliner" + +using namespace llvm; +using namespace sampleprof; + +// The switches specify inline thresholds used in SampleProfileLoader inlining. +// TODO: the actual threshold to be tuned here because the size here is based +// on machine code not LLVM IR. +extern cl::opt<int> SampleHotCallSiteThreshold; +extern cl::opt<int> SampleColdCallSiteThreshold; +extern cl::opt<int> ProfileInlineGrowthLimit; +extern cl::opt<int> ProfileInlineLimitMin; +extern cl::opt<int> ProfileInlineLimitMax; + +static cl::opt<bool> SamplePreInlineReplay( + "csspgo-replay-preinline", cl::Hidden, cl::init(false), + cl::desc( + "Replay previous inlining and adjust context profile accordingly")); + +CSPreInliner::CSPreInliner(StringMap<FunctionSamples> &Profiles, + uint64_t HotThreshold, uint64_t ColdThreshold) + : ContextTracker(Profiles), ProfileMap(Profiles), + HotCountThreshold(HotThreshold), ColdCountThreshold(ColdThreshold) {} + +std::vector<StringRef> CSPreInliner::buildTopDownOrder() { + std::vector<StringRef> Order; + ProfiledCallGraph ProfiledCG(ContextTracker); + + // Now that we have a profiled call graph, construct top-down order + // by building up SCC and reversing SCC order. + scc_iterator<ProfiledCallGraph *> I = scc_begin(&ProfiledCG); + while (!I.isAtEnd()) { + for (ProfiledCallGraphNode *Node : *I) { + if (Node != ProfiledCG.getEntryNode()) + Order.push_back(Node->Name); + } + ++I; + } + std::reverse(Order.begin(), Order.end()); + + return Order; +} + +bool CSPreInliner::getInlineCandidates(ProfiledCandidateQueue &CQueue, + const FunctionSamples *CallerSamples) { + assert(CallerSamples && "Expect non-null caller samples"); + + // Ideally we want to consider everything a function calls, but as far as + // context profile is concerned, only those frames that are children of + // current one in the trie is relavent. So we walk the trie instead of call + // targets from function profile. + ContextTrieNode *CallerNode = + ContextTracker.getContextFor(CallerSamples->getContext()); + + bool HasNewCandidate = false; + for (auto &Child : CallerNode->getAllChildContext()) { + ContextTrieNode *CalleeNode = &Child.second; + FunctionSamples *CalleeSamples = CalleeNode->getFunctionSamples(); + if (!CalleeSamples) + continue; + + // Call site count is more reliable, so we look up the corresponding call + // target profile in caller's context profile to retrieve call site count. + uint64_t CalleeEntryCount = CalleeSamples->getEntrySamples(); + uint64_t CallsiteCount = 0; + LineLocation Callsite = CalleeNode->getCallSiteLoc(); + if (auto CallTargets = CallerSamples->findCallTargetMapAt(Callsite)) { + SampleRecord::CallTargetMap &TargetCounts = CallTargets.get(); + auto It = TargetCounts.find(CalleeSamples->getName()); + if (It != TargetCounts.end()) + CallsiteCount = It->second; + } + + // TODO: call site and callee entry count should be mostly consistent, add + // check for that. + HasNewCandidate = true; + CQueue.emplace(CalleeSamples, std::max(CallsiteCount, CalleeEntryCount)); + } + + return HasNewCandidate; +} + +bool CSPreInliner::shouldInline(ProfiledInlineCandidate &Candidate) { + // If replay inline is requested, simply follow the inline decision of the + // profiled binary. + if (SamplePreInlineReplay) + return Candidate.CalleeSamples->getContext().hasAttribute( + ContextWasInlined); + + // Adjust threshold based on call site hotness, only do this for callsite + // prioritized inliner because otherwise cost-benefit check is done earlier. + unsigned int SampleThreshold = SampleColdCallSiteThreshold; + if (Candidate.CallsiteCount > HotCountThreshold) + SampleThreshold = SampleHotCallSiteThreshold; + + // TODO: for small cold functions, we may inlined them and we need to keep + // context profile accordingly. + if (Candidate.CallsiteCount < ColdCountThreshold) + SampleThreshold = SampleColdCallSiteThreshold; + + return (Candidate.SizeCost < SampleThreshold); +} + +void CSPreInliner::processFunction(const StringRef Name) { + LLVM_DEBUG(dbgs() << "Process " << Name + << " for context-sensitive pre-inlining\n"); + + FunctionSamples *FSamples = ContextTracker.getBaseSamplesFor(Name); + if (!FSamples) + return; + + // Use the number of lines/probes as proxy for function size for now. + // TODO: retrieve accurate size from dwarf or binary instead. + unsigned FuncSize = FSamples->getBodySamples().size(); + unsigned FuncFinalSize = FuncSize; + unsigned SizeLimit = FuncSize * ProfileInlineGrowthLimit; + SizeLimit = std::min(SizeLimit, (unsigned)ProfileInlineLimitMax); + SizeLimit = std::max(SizeLimit, (unsigned)ProfileInlineLimitMin); + + ProfiledCandidateQueue CQueue; + getInlineCandidates(CQueue, FSamples); + + while (!CQueue.empty() && FuncFinalSize < SizeLimit) { + ProfiledInlineCandidate Candidate = CQueue.top(); + CQueue.pop(); + bool ShouldInline = false; + if ((ShouldInline = shouldInline(Candidate))) { + // We mark context as inlined as the corresponding context profile + // won't be merged into that function's base profile. + ContextTracker.markContextSamplesInlined(Candidate.CalleeSamples); + Candidate.CalleeSamples->getContext().setAttribute( + ContextShouldBeInlined); + FuncFinalSize += Candidate.SizeCost; + getInlineCandidates(CQueue, Candidate.CalleeSamples); + } + LLVM_DEBUG(dbgs() << (ShouldInline ? " Inlined" : " Outlined") + << " context profile for: " + << Candidate.CalleeSamples->getNameWithContext() + << " (callee size: " << Candidate.SizeCost + << ", call count:" << Candidate.CallsiteCount << ")\n"); + } + + LLVM_DEBUG({ + if (!CQueue.empty()) + dbgs() << " Inline candidates ignored due to size limit (inliner " + "original size: " + << FuncSize << ", inliner final size: " << FuncFinalSize + << ", size limit: " << SizeLimit << ")\n"; + + while (!CQueue.empty()) { + ProfiledInlineCandidate Candidate = CQueue.top(); + CQueue.pop(); + bool WasInlined = + Candidate.CalleeSamples->getContext().hasAttribute(ContextWasInlined); + dbgs() << " " << Candidate.CalleeSamples->getNameWithContext() + << " (candidate size:" << Candidate.SizeCost + << ", call count: " << Candidate.CallsiteCount << ", previously " + << (WasInlined ? "inlined)\n" : "not inlined)\n"); + } + }); +} + +void CSPreInliner::run() { +#ifndef NDEBUG + auto printProfileNames = [](StringMap<FunctionSamples> &Profiles, + bool IsInput) { + dbgs() << (IsInput ? "Input" : "Output") << " context-sensitive profiles (" + << Profiles.size() << " total):\n"; + for (auto &It : Profiles) { + const FunctionSamples &Samples = It.second; + dbgs() << " [" << Samples.getNameWithContext() << "] " + << Samples.getTotalSamples() << ":" << Samples.getHeadSamples() + << "\n"; + } + }; +#endif + + LLVM_DEBUG(printProfileNames(ProfileMap, true)); + + // Execute global pre-inliner to estimate a global top-down inline + // decision and merge profiles accordingly. This helps with profile + // merge for ThinLTO otherwise we won't be able to merge profiles back + // to base profile across module/thin-backend boundaries. + // It also helps better compress context profile to control profile + // size, as we now only need context profile for functions going to + // be inlined. + for (StringRef FuncName : buildTopDownOrder()) { + processFunction(FuncName); + } + + // Not inlined context profiles are merged into its base, so we can + // trim out such profiles from the output. + std::vector<StringRef> ProfilesToBeRemoved; + for (auto &It : ProfileMap) { + SampleContext Context = It.second.getContext(); + if (!Context.isBaseContext() && !Context.hasState(InlinedContext)) { + assert(Context.hasState(MergedContext) && + "Not inlined context profile should be merged already"); + ProfilesToBeRemoved.push_back(It.first()); + } + } + + for (StringRef ContextName : ProfilesToBeRemoved) { + ProfileMap.erase(ContextName); + } + + // Make sure ProfileMap's key is consistent with FunctionSamples' name. + SampleContextTrimmer(ProfileMap).canonicalizeContextProfiles(); + + LLVM_DEBUG(printProfileNames(ProfileMap, false)); +}
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/CSPreInliner.h b/src/llvm-project/llvm/tools/llvm-profgen/CSPreInliner.h new file mode 100644 index 0000000..5c65d8f --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-profgen/CSPreInliner.h
@@ -0,0 +1,92 @@ +//===-- CSPreInliner.h - Profile guided preinliner ---------------- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H +#define LLVM_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H + +#include "llvm/ADT/PriorityQueue.h" +#include "llvm/ProfileData/ProfileCommon.h" +#include "llvm/ProfileData/SampleProf.h" +#include "llvm/Transforms/IPO/ProfiledCallGraph.h" +#include "llvm/Transforms/IPO/SampleContextTracker.h" + +using namespace llvm; +using namespace sampleprof; + +namespace llvm { +namespace sampleprof { + +// Inline candidate seen from profile +struct ProfiledInlineCandidate { + ProfiledInlineCandidate(const FunctionSamples *Samples, uint64_t Count) + : CalleeSamples(Samples), CallsiteCount(Count), + SizeCost(Samples->getBodySamples().size()) {} + // Context-sensitive function profile for inline candidate + const FunctionSamples *CalleeSamples; + // Call site count for an inline candidate + // TODO: make sure entry count for context profile and call site + // target count for corresponding call are consistent. + uint64_t CallsiteCount; + // Size proxy for function under particular call context. + // TODO: use post-inline callee size from debug info. + uint64_t SizeCost; +}; + +// Inline candidate comparer using call site weight +struct ProfiledCandidateComparer { + bool operator()(const ProfiledInlineCandidate &LHS, + const ProfiledInlineCandidate &RHS) { + if (LHS.CallsiteCount != RHS.CallsiteCount) + return LHS.CallsiteCount < RHS.CallsiteCount; + + if (LHS.SizeCost != RHS.SizeCost) + return LHS.SizeCost > RHS.SizeCost; + + // Tie breaker using GUID so we have stable/deterministic inlining order + assert(LHS.CalleeSamples && RHS.CalleeSamples && + "Expect non-null FunctionSamples"); + return LHS.CalleeSamples->getGUID(LHS.CalleeSamples->getName()) < + RHS.CalleeSamples->getGUID(RHS.CalleeSamples->getName()); + } +}; + +using ProfiledCandidateQueue = + PriorityQueue<ProfiledInlineCandidate, std::vector<ProfiledInlineCandidate>, + ProfiledCandidateComparer>; + +// Pre-compilation inliner based on context-sensitive profile. +// The PreInliner estimates inline decision using hotness from profile +// and cost estimation from machine code size. It helps merges context +// profile globally and achieves better post-inine profile quality, which +// otherwise won't be possible for ThinLTO. It also reduce context profile +// size by only keep context that is estimated to be inlined. +class CSPreInliner { +public: + CSPreInliner(StringMap<FunctionSamples> &Profiles, uint64_t HotThreshold, + uint64_t ColdThreshold); + void run(); + +private: + bool getInlineCandidates(ProfiledCandidateQueue &CQueue, + const FunctionSamples *FCallerContextSamples); + std::vector<StringRef> buildTopDownOrder(); + void processFunction(StringRef Name); + bool shouldInline(ProfiledInlineCandidate &Candidate); + SampleContextTracker ContextTracker; + StringMap<FunctionSamples> &ProfileMap; + + // Count thresholds to answer isHotCount and isColdCount queries. + // Mirrors the threshold in ProfileSummaryInfo. + uint64_t HotCountThreshold; + uint64_t ColdCountThreshold; +}; + +} // end namespace sampleprof +} // end namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/PerfReader.cpp b/src/llvm-project/llvm/tools/llvm-profgen/PerfReader.cpp index 1f84200..f6b394e 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/PerfReader.cpp +++ b/src/llvm-project/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "PerfReader.h" #include "ProfileGenerator.h" +#include "llvm/Support/FileSystem.h" static cl::opt<bool> ShowMmapEvents("show-mmap-events", cl::ReallyHidden, cl::init(false), cl::ZeroOrMore, @@ -92,7 +93,8 @@ std::shared_ptr<StringBasedCtxKey> FrameStack::getContextKey() { std::shared_ptr<StringBasedCtxKey> KeyStr = std::make_shared<StringBasedCtxKey>(); - KeyStr->Context = Binary->getExpandedContextStr(Stack); + KeyStr->Context = + Binary->getExpandedContextStr(Stack, KeyStr->WasLeafInlined); if (KeyStr->Context.empty()) return nullptr; KeyStr->genHashCode(); @@ -141,9 +143,11 @@ if (!Cur->isDummyRoot()) { if (!Stack.pushFrame(Cur)) { // Process truncated context + // Start a new traversal ignoring its bottom context + T EmptyStack(Binary); + collectSamplesFromFrame(Cur, EmptyStack); for (const auto &Item : Cur->Children) { - // Start a new traversal ignoring its bottom context - collectSamplesFromFrameTrie(Item.second.get()); + collectSamplesFromFrameTrie(Item.second.get(), EmptyStack); } return; } @@ -307,18 +311,46 @@ auto I = BinaryTable.find(BinaryName); // Drop the event which doesn't belong to user-provided binaries // or if its image is loaded at the same address - if (I == BinaryTable.end() || Event.BaseAddress == I->second.getBaseAddress()) + if (I == BinaryTable.end() || Event.Address == I->second.getBaseAddress()) return; ProfiledBinary &Binary = I->second; - // A binary image could be uploaded and then reloaded at different - // place, so update the address map here - AddrToBinaryMap.erase(Binary.getBaseAddress()); - AddrToBinaryMap[Event.BaseAddress] = &Binary; + if (Event.Offset == Binary.getTextSegmentOffset()) { + // A binary image could be unloaded and then reloaded at different + // place, so update the address map here. + // Only update for the first executable segment and assume all other + // segments are loaded at consecutive memory addresses, which is the case on + // X64. + AddrToBinaryMap.erase(Binary.getBaseAddress()); + AddrToBinaryMap[Event.Address] = &Binary; - // Update binary load address. - Binary.setBaseAddress(Event.BaseAddress); + // Update binary load address. + Binary.setBaseAddress(Event.Address); + } else { + // Verify segments are loaded consecutively. + const auto &Offsets = Binary.getTextSegmentOffsets(); + auto It = std::lower_bound(Offsets.begin(), Offsets.end(), Event.Offset); + if (It != Offsets.end() && *It == Event.Offset) { + // The event is for loading a separate executable segment. + auto I = std::distance(Offsets.begin(), It); + const auto &PreferredAddrs = Binary.getPreferredTextSegmentAddresses(); + if (PreferredAddrs[I] - Binary.getPreferredBaseAddress() != + Event.Address - Binary.getBaseAddress()) + exitWithError("Executable segments not loaded consecutively"); + } else { + if (It == Offsets.begin()) + exitWithError("File offset not found"); + else { + // Find the segment the event falls in. A large segment could be loaded + // via multiple mmap calls with consecutive memory addresses. + --It; + assert(*It < Event.Offset); + if (Event.Offset - *It != Event.Address - Binary.getBaseAddress()) + exitWithError("Segment not loaded by consecutive mmaps"); + } + } + } } ProfiledBinary *PerfReader::getBinary(uint64_t Address) { @@ -440,27 +472,57 @@ bool SrcIsInternal = Binary->addressIsCode(Src); bool DstIsInternal = Binary->addressIsCode(Dst); + bool IsExternal = !SrcIsInternal && !DstIsInternal; + bool IsIncoming = !SrcIsInternal && DstIsInternal; + bool IsOutgoing = SrcIsInternal && !DstIsInternal; bool IsArtificial = false; + // Ignore branches outside the current binary. - if (!SrcIsInternal && !DstIsInternal) + if (IsExternal) continue; - if (!SrcIsInternal && DstIsInternal) { - // For transition from external code (such as dynamic libraries) to - // the current binary, keep track of the branch target which will be - // grouped with the Source of the last transition from the current - // binary. - PrevTrDst = Dst; - continue; - } - if (SrcIsInternal && !DstIsInternal) { + + if (IsOutgoing) { + if (!PrevTrDst) { + // This is unpaired outgoing jump which is likely due to interrupt or + // incomplete LBR trace. Ignore current and subsequent entries since + // they are likely in different contexts. + break; + } + + if (Binary->addressIsReturn(Src)) { + // In a callback case, a return from internal code, say A, to external + // runtime can happen. The external runtime can then call back to + // another internal routine, say B. Making an artificial branch that + // looks like a return from A to B can confuse the unwinder to treat + // the instruction before B as the call instruction. + break; + } + // For transition to external code, group the Source with the next // availabe transition target. - if (!PrevTrDst) - continue; Dst = PrevTrDst; PrevTrDst = 0; IsArtificial = true; + } else { + if (PrevTrDst) { + // If we have seen an incoming transition from external code to internal + // code, but not a following outgoing transition, the incoming + // transition is likely due to interrupt which is usually unpaired. + // Ignore current and subsequent entries since they are likely in + // different contexts. + break; + } + + if (IsIncoming) { + // For transition from external code (such as dynamic libraries) to + // the current binary, keep track of the branch target which will be + // grouped with the Source of the last transition from the current + // binary. + PrevTrDst = Dst; + continue; + } } + // TODO: filter out buggy duplicate branches on Skylake LBRStack.emplace_back(LBREntry(Src, Dst, IsArtificial)); @@ -586,7 +648,7 @@ enum EventIndex { WHOLE_LINE = 0, PID = 1, - BASE_ADDRESS = 2, + MMAPPED_ADDRESS = 2, MMAPPED_SIZE = 3, PAGE_OFFSET = 4, BINARY_PATH = 5 @@ -603,14 +665,14 @@ } MMapEvent Event; Fields[PID].getAsInteger(10, Event.PID); - Fields[BASE_ADDRESS].getAsInteger(0, Event.BaseAddress); + Fields[MMAPPED_ADDRESS].getAsInteger(0, Event.Address); Fields[MMAPPED_SIZE].getAsInteger(0, Event.Size); Fields[PAGE_OFFSET].getAsInteger(0, Event.Offset); Event.BinaryPath = Fields[BINARY_PATH]; updateBinaryAddress(Event); if (ShowMmapEvents) { outs() << "Mmap: Binary " << Event.BinaryPath << " loaded at " - << format("0x%" PRIx64 ":", Event.BaseAddress) << " \n"; + << format("0x%" PRIx64 ":", Event.Address) << " \n"; } TraceIt.advance(); }
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/PerfReader.h b/src/llvm-project/llvm/tools/llvm-profgen/PerfReader.h index b802c21..05bf2d6 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/PerfReader.h +++ b/src/llvm-project/llvm/tools/llvm-profgen/PerfReader.h
@@ -75,8 +75,33 @@ bool IsArtificial = false; LBREntry(uint64_t S, uint64_t T, bool I) : Source(S), Target(T), IsArtificial(I) {} + +#ifndef NDEBUG + void print() const { + dbgs() << "from " << format("%#010x", Source) << " to " + << format("%#010x", Target); + if (IsArtificial) + dbgs() << " Artificial"; + } +#endif }; +#ifndef NDEBUG +static inline void printLBRStack(const SmallVectorImpl<LBREntry> &LBRStack) { + for (size_t I = 0; I < LBRStack.size(); I++) { + dbgs() << "[" << I << "] "; + LBRStack[I].print(); + dbgs() << "\n"; + } +} + +static inline void printCallStack(const SmallVectorImpl<uint64_t> &CallStack) { + for (size_t I = 0; I < CallStack.size(); I++) { + dbgs() << "[" << I << "] " << format("%#010x", CallStack[I]) << "\n"; + } +} +#endif + // Hash interface for generic data of type T // Data should implement a \fn getHashCode and a \fn isEqual // Currently getHashCode is non-virtual to avoid the overhead of calling vtable, @@ -185,6 +210,15 @@ } HashCode = Hash; } + +#ifndef NDEBUG + void print() const { + dbgs() << "LBR stack\n"; + printLBRStack(LBRStack); + dbgs() << "Call stack\n"; + printCallStack(CallStack); + } +#endif }; // After parsing the sample, we record the samples by aggregating them @@ -224,6 +258,7 @@ BranchSamples.emplace_back(std::make_tuple(Source, Target, Count)); } bool isDummyRoot() { return Address == 0; } + bool isLeafFrame() { return Children.empty(); } }; ProfiledFrame DummyTrieRoot; @@ -311,7 +346,8 @@ // String based context id struct StringBasedCtxKey : public ContextKey { std::string Context; - StringBasedCtxKey() : ContextKey(CK_StringBased){}; + bool WasLeafInlined; + StringBasedCtxKey() : ContextKey(CK_StringBased), WasLeafInlined(false){}; static bool classof(const ContextKey *K) { return K->getKind() == CK_StringBased; } @@ -405,8 +441,13 @@ // Callsite merging may cause the loss of original probe IDs. // Cutting off the context from here since the inliner will // not know how to consume a context with unknown callsites. - if (!CallProbe) + if (!CallProbe) { + if (!Cur->isLeafFrame()) + WithColor::warning() + << "Untracked frame at " << format("%" PRIx64, Cur->Address) + << " due to missing call probe\n"; return false; + } Stack.push_back(CallProbe); return true; } @@ -554,7 +595,7 @@ // The parsed MMap event struct MMapEvent { uint64_t PID = 0; - uint64_t BaseAddress = 0; + uint64_t Address = 0; uint64_t Size = 0; uint64_t Offset = 0; StringRef BinaryPath; @@ -607,7 +648,6 @@ BinaryMap BinaryTable; AddressBinaryMap AddrToBinaryMap; // Used by address-based lookup. -private: BinarySampleCounterMap BinarySampleCounters; // Samples with the repeating time generated by the perf reader AggregatedCounter AggregatedSamples;
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/ProfileGenerator.cpp b/src/llvm-project/llvm/tools/llvm-profgen/ProfileGenerator.cpp index 4cfadff..861dd17 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/ProfileGenerator.cpp +++ b/src/llvm-project/llvm/tools/llvm-profgen/ProfileGenerator.cpp
@@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ProfileGenerator.h" +#include "llvm/ProfileData/ProfileCommon.h" static cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), cl::Required, @@ -15,7 +16,7 @@ cl::aliasopt(OutputFilename)); static cl::opt<SampleProfileFormat> OutputFormat( - "format", cl::desc("Format of output profile"), cl::init(SPF_Text), + "format", cl::desc("Format of output profile"), cl::init(SPF_Ext_Binary), cl::values( clEnumValN(SPF_Binary, "binary", "Binary encoding (default)"), clEnumValN(SPF_Compact_Binary, "compbinary", "Compact binary encoding"), @@ -31,18 +32,28 @@ cl::Hidden, cl::location(llvm::sampleprof::CSProfileGenerator::MaxCompressionSize)); -static cl::opt<uint64_t> CSProfColdThres( - "csprof-cold-thres", cl::init(100), cl::ZeroOrMore, - cl::desc("Specify the total samples threshold for a context profile to " - "be considered cold, any cold profiles will be merged into " - "context-less base profiles")); +static cl::opt<bool> CSProfMergeColdContext( + "csprof-merge-cold-context", cl::init(true), cl::ZeroOrMore, + cl::desc("If the total count of context profile is smaller than " + "the threshold, it will be merged into context-less base " + "profile.")); -static cl::opt<bool> CSProfKeepCold( - "csprof-keep-cold", cl::init(false), cl::ZeroOrMore, - cl::desc("This works together with --csprof-cold-thres. If the total count " - "of the profile after all merge is done is still smaller than the " - "csprof-cold-thres, it will be trimmed unless csprof-keep-cold " - "flag is specified.")); +static cl::opt<bool> CSProfTrimColdContext( + "csprof-trim-cold-context", cl::init(true), cl::ZeroOrMore, + cl::desc("If the total count of the profile after all merge is done " + "is still smaller than threshold, it will be trimmed.")); + +static cl::opt<uint32_t> CSProfColdContextFrameDepth( + "csprof-frame-depth-for-cold-context", cl::init(1), cl::ZeroOrMore, + cl::desc("Keep the last K frames while merging cold profile. 1 means the " + "context-less base profile")); + +static cl::opt<bool> EnableCSPreInliner( + "csspgo-preinliner", cl::Hidden, cl::init(false), + cl::desc("Run a global pre-inliner to merge context profile based on " + "estimated global top-down inline decisions")); + +extern cl::opt<int> ProfileSummaryCutoffCold; using namespace llvm; using namespace sampleprof; @@ -80,7 +91,8 @@ void ProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer, StringMap<FunctionSamples> &ProfileMap) { - Writer->write(ProfileMap); + if (std::error_code EC = Writer->write(ProfileMap)) + exitWithError(std::move(EC)); } void ProfileGenerator::write() { @@ -167,19 +179,20 @@ Boundaries[End].addEndCount(Count); } - uint64_t BeginAddress = 0; + uint64_t BeginAddress = UINT64_MAX; int Count = 0; for (auto Item : Boundaries) { uint64_t Address = Item.first; BoundaryPoint &Point = Item.second; if (Point.BeginCount) { - if (BeginAddress) + if (BeginAddress != UINT64_MAX) DisjointRanges[{BeginAddress, Address - 1}] = Count; Count += Point.BeginCount; BeginAddress = Address; } if (Point.EndCount) { - assert(BeginAddress && "First boundary point cannot be 'end' point"); + assert((BeginAddress != UINT64_MAX) && + "First boundary point cannot be 'end' point"); DisjointRanges[{BeginAddress, Address}] = Count; Count -= Point.EndCount; BeginAddress = Address + 1; @@ -188,16 +201,52 @@ } FunctionSamples & -CSProfileGenerator::getFunctionProfileForContext(StringRef ContextStr) { +CSProfileGenerator::getFunctionProfileForContext(StringRef ContextStr, + bool WasLeafInlined) { auto Ret = ProfileMap.try_emplace(ContextStr, FunctionSamples()); if (Ret.second) { - SampleContext FContext(Ret.first->first(), RawContext); + // Make a copy of the underlying context string in string table + // before StringRef wrapper is used for context. + auto It = ContextStrings.insert(ContextStr.str()); + SampleContext FContext(*It.first, RawContext); + if (WasLeafInlined) + FContext.setAttribute(ContextWasInlined); FunctionSamples &FProfile = Ret.first->second; FProfile.setContext(FContext); + FProfile.setName(FContext.getNameWithoutContext()); } return Ret.first->second; } +void CSProfileGenerator::generateProfile() { + FunctionSamples::ProfileIsCS = true; + for (const auto &BI : BinarySampleCounters) { + ProfiledBinary *Binary = BI.first; + for (const auto &CI : BI.second) { + const StringBasedCtxKey *CtxKey = + dyn_cast<StringBasedCtxKey>(CI.first.getPtr()); + StringRef ContextId(CtxKey->Context); + // Get or create function profile for the range + FunctionSamples &FunctionProfile = + getFunctionProfileForContext(ContextId, CtxKey->WasLeafInlined); + + // Fill in function body samples + populateFunctionBodySamples(FunctionProfile, CI.second.RangeCounter, + Binary); + // Fill in boundary sample counts as well as call site samples for calls + populateFunctionBoundarySamples(ContextId, FunctionProfile, + CI.second.BranchCounter, Binary); + } + } + // Fill in call site value sample for inlined calls and also use context to + // infer missing samples. Since we don't have call count for inlined + // functions, we estimate it from inlinee's profile using the entry of the + // body sample. + populateInferredFunctionSamples(); + + postProcessProfiles(); +} + void CSProfileGenerator::updateBodySamplesforFunctionProfile( FunctionSamples &FunctionProfile, const FrameLocation &LeafLoc, uint64_t Count) { @@ -351,60 +400,42 @@ } } -void CSProfileGenerator::mergeAndTrimColdProfile( - StringMap<FunctionSamples> &ProfileMap) { - // Nothing to merge if sample threshold is zero - if (!CSProfColdThres) - return; +void CSProfileGenerator::postProcessProfiles() { + // Compute hot/cold threshold based on profile. This will be used for cold + // context profile merging/trimming. + computeSummaryAndThreshold(); - // Filter the cold profiles from ProfileMap and move them into a tmp - // container - std::vector<std::pair<StringRef, const FunctionSamples *>> ToRemoveVec; - for (const auto &I : ProfileMap) { - const FunctionSamples &FunctionProfile = I.second; - if (FunctionProfile.getTotalSamples() >= CSProfColdThres) - continue; - ToRemoveVec.emplace_back(I.getKey(), &I.second); - } + // Run global pre-inliner to adjust/merge context profile based on estimated + // inline decisions. + if (EnableCSPreInliner) + CSPreInliner(ProfileMap, HotCountThreshold, ColdCountThreshold).run(); - // Remove the code profile from ProfileMap and merge them into BaseProileMap - StringMap<FunctionSamples> BaseProfileMap; - for (const auto &I : ToRemoveVec) { - auto Ret = BaseProfileMap.try_emplace( - I.second->getContext().getNameWithoutContext(), FunctionSamples()); - FunctionSamples &BaseProfile = Ret.first->second; - BaseProfile.merge(*I.second); - ProfileMap.erase(I.first); - } + // Trim and merge cold context profile using cold threshold above; + SampleContextTrimmer(ProfileMap) + .trimAndMergeColdContextProfiles( + ColdCountThreshold, CSProfTrimColdContext, CSProfMergeColdContext, + CSProfColdContextFrameDepth); +} - // Merge the base profiles into ProfileMap; - for (const auto &I : BaseProfileMap) { - // Filter the cold base profile - if (!CSProfKeepCold && I.second.getTotalSamples() < CSProfColdThres && - ProfileMap.find(I.getKey()) == ProfileMap.end()) - continue; - // Merge the profile if the original profile exists, otherwise just insert - // as a new profile - FunctionSamples &OrigProfile = getFunctionProfileForContext(I.getKey()); - OrigProfile.merge(I.second); - } +void CSProfileGenerator::computeSummaryAndThreshold() { + // Update the default value of cold cutoff for llvm-profgen. + // Do it here because we don't want to change the global default, + // which would lead CS profile size too large. + if (!ProfileSummaryCutoffCold.getNumOccurrences()) + ProfileSummaryCutoffCold = 999000; + + SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); + auto Summary = Builder.computeSummaryForProfiles(ProfileMap); + HotCountThreshold = ProfileSummaryBuilder::getHotCountThreshold( + (Summary->getDetailedSummary())); + ColdCountThreshold = ProfileSummaryBuilder::getColdCountThreshold( + (Summary->getDetailedSummary())); } void CSProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer, StringMap<FunctionSamples> &ProfileMap) { - mergeAndTrimColdProfile(ProfileMap); - // Add bracket for context key to support different profile binary format - StringMap<FunctionSamples> CxtWithBracketPMap; - for (const auto &Item : ProfileMap) { - std::string ContextWithBracket = "[" + Item.first().str() + "]"; - auto Ret = CxtWithBracketPMap.try_emplace(ContextWithBracket, Item.second); - assert(Ret.second && "Must be a unique context"); - SampleContext FContext(Ret.first->first(), RawContext); - FunctionSamples &FProfile = Ret.first->second; - FProfile.setName(FContext.getNameWithContext(true)); - FProfile.setContext(FContext); - } - Writer->write(CxtWithBracketPMap); + if (std::error_code EC = Writer->write(ProfileMap)) + exitWithError(std::move(EC)); } // Helper function to extract context prefix string stack @@ -422,6 +453,7 @@ void PseudoProbeCSProfileGenerator::generateProfile() { // Enable pseudo probe functionalities in SampleProf FunctionSamples::ProfileIsProbeBased = true; + FunctionSamples::ProfileIsCS = true; for (const auto &BI : BinarySampleCounters) { ProfiledBinary *Binary = BI.first; for (const auto &CI : BI.second) { @@ -438,6 +470,8 @@ ContextStrStack, Binary); } } + + postProcessProfiles(); } void PseudoProbeCSProfileGenerator::extractProbesFromRange( @@ -486,13 +520,17 @@ // Extract the top frame probes by looking up each address among the range in // the Address2ProbeMap extractProbesFromRange(RangeCounter, ProbeCounter, Binary); + std::unordered_map<PseudoProbeInlineTree *, FunctionSamples *> FrameSamples; for (auto PI : ProbeCounter) { const PseudoProbe *Probe = PI.first; uint64_t Count = PI.second; FunctionSamples &FunctionProfile = getFunctionProfileForLeafProbe(ContextStrStack, Probe, Binary); - - FunctionProfile.addBodySamples(Probe->Index, 0, Count); + // Record the current frame and FunctionProfile whenever samples are + // collected for non-danglie probes. This is for reporting all of the + // zero count probes of the frame later. + FrameSamples[Probe->getInlineTreeNode()] = &FunctionProfile; + FunctionProfile.addBodySamplesForProbe(Probe->Index, Count); FunctionProfile.addTotalSamples(Count); if (Probe->isEntry()) { FunctionProfile.addHeadSamples(Count); @@ -503,7 +541,7 @@ // context id to infer caller's context id to ensure they share the // same context prefix. StringRef CalleeContextId = - FunctionProfile.getContext().getNameWithContext(true); + FunctionProfile.getContext().getNameWithContext(); StringRef CallerContextId; FrameLocation &&CallerLeafFrameLoc = getCallerContext(CalleeContextId, CallerContextId); @@ -520,6 +558,16 @@ FunctionProfile.getContext().getNameWithoutContext(), Count); } } + + // Assign zero count for remaining probes without sample hits to + // differentiate from probes optimized away, of which the counts are unknown + // and will be inferred by the compiler. + for (auto &I : FrameSamples) { + auto *FunctionProfile = I.second; + for (auto *Probe : I.first->getProbes()) { + FunctionProfile->addBodySamplesForProbe(Probe->Index, 0); + } + } } } @@ -549,7 +597,7 @@ FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( SmallVectorImpl<std::string> &ContextStrStack, - const PseudoProbeFuncDesc *LeafFuncDesc) { + const PseudoProbeFuncDesc *LeafFuncDesc, bool WasLeafInlined) { assert(ContextStrStack.size() && "Profile context must have the leaf frame"); // Compress the context string except for the leaf frame std::string LeafFrame = ContextStrStack.back(); @@ -570,7 +618,7 @@ OContextStr << StringRef(LeafFrame).split(":").first.str(); FunctionSamples &FunctionProile = - getFunctionProfileForContext(OContextStr.str()); + getFunctionProfileForContext(OContextStr.str(), WasLeafInlined); FunctionProile.setFunctionHash(LeafFuncDesc->FuncHash); return FunctionProile; } @@ -581,13 +629,11 @@ // Explicitly copy the context for appending the leaf context SmallVector<std::string, 16> ContextStrStackCopy(ContextStrStack.begin(), ContextStrStack.end()); - Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy); - // Note that the context from probe doesn't include leaf frame, - // hence we need to retrieve and append the leaf frame. + Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy, true); const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->GUID); - ContextStrStackCopy.emplace_back(FuncDesc->FuncName + ":" + - Twine(LeafProbe->Index).str()); - return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc); + bool WasLeafInlined = LeafProbe->InlineTree->hasInlineSite(); + return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc, + WasLeafInlined); } } // end namespace sampleprof
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/ProfileGenerator.h b/src/llvm-project/llvm/tools/llvm-profgen/ProfileGenerator.h index ff014ed..66ccf49 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/ProfileGenerator.h +++ b/src/llvm-project/llvm/tools/llvm-profgen/ProfileGenerator.h
@@ -8,10 +8,13 @@ #ifndef LLVM_TOOLS_LLVM_PROGEN_PROFILEGENERATOR_H #define LLVM_TOOLS_LLVM_PROGEN_PROFILEGENERATOR_H +#include "CSPreInliner.h" #include "ErrorHandling.h" #include "PerfReader.h" #include "ProfiledBinary.h" #include "llvm/ProfileData/SampleProfWriter.h" +#include <memory> +#include <unordered_set> using namespace llvm; using namespace sampleprof; @@ -65,31 +68,7 @@ : BinarySampleCounters(Counters){}; public: - void generateProfile() override { - for (const auto &BI : BinarySampleCounters) { - ProfiledBinary *Binary = BI.first; - for (const auto &CI : BI.second) { - const StringBasedCtxKey *CtxKey = - dyn_cast<StringBasedCtxKey>(CI.first.getPtr()); - StringRef ContextId(CtxKey->Context); - // Get or create function profile for the range - FunctionSamples &FunctionProfile = - getFunctionProfileForContext(ContextId); - - // Fill in function body samples - populateFunctionBodySamples(FunctionProfile, CI.second.RangeCounter, - Binary); - // Fill in boundary sample counts as well as call site samples for calls - populateFunctionBoundarySamples(ContextId, FunctionProfile, - CI.second.BranchCounter, Binary); - } - } - // Fill in call site value sample for inlined calls and also use context to - // infer missing samples. Since we don't have call count for inlined - // functions, we estimate it from inlinee's profile using the entry of the - // body sample. - populateInferredFunctionSamples(); - } + void generateProfile() override; // Remove adjacent repeated context sequences up to a given sequence length, // -1 means no size limit. Note that repeated sequences are identified based @@ -198,13 +177,22 @@ protected: // Lookup or create FunctionSamples for the context - FunctionSamples &getFunctionProfileForContext(StringRef ContextId); - // Merge cold context profile whose total sample is below threshold - // into base profile. - void mergeAndTrimColdProfile(StringMap<FunctionSamples> &ProfileMap); + FunctionSamples &getFunctionProfileForContext(StringRef ContextId, + bool WasLeafInlined = false); + // Post processing for profiles before writing out, such as mermining + // and trimming cold profiles, running preinliner on profiles. + void postProcessProfiles(); + void computeSummaryAndThreshold(); void write(std::unique_ptr<SampleProfileWriter> Writer, StringMap<FunctionSamples> &ProfileMap) override; + // Thresholds from profile summary to answer isHotCount/isColdCount queries. + uint64_t HotCountThreshold; + uint64_t ColdCountThreshold; + + // String table owning context strings created from profile generation. + std::unordered_set<std::string> ContextStrings; + private: // Helper function for updating body sample for a leaf location in // FunctionProfile @@ -253,7 +241,8 @@ // Helper function to get FunctionSamples for the leaf inlined context FunctionSamples & getFunctionProfileForLeafProbe(SmallVectorImpl<std::string> &ContextStrStack, - const PseudoProbeFuncDesc *LeafFuncDesc); + const PseudoProbeFuncDesc *LeafFuncDesc, + bool WasLeafInlined); // Helper function to get FunctionSamples for the leaf probe FunctionSamples & getFunctionProfileForLeafProbe(SmallVectorImpl<std::string> &ContextStrStack,
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/ProfiledBinary.cpp b/src/llvm-project/llvm/tools/llvm-profgen/ProfiledBinary.cpp index df6ef2a..3f0ee4a 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/ProfiledBinary.cpp +++ b/src/llvm-project/llvm/tools/llvm-profgen/ProfiledBinary.cpp
@@ -30,6 +30,10 @@ cl::init(false), cl::ZeroOrMore, cl::desc("Print source locations.")); +cl::opt<bool> ShowCanonicalFnName("show-canonical-fname", cl::ReallyHidden, + cl::init(false), cl::ZeroOrMore, + cl::desc("Print canonical function name.")); + cl::opt<bool> ShowPseudoProbe( "show-pseudo-probe", cl::ReallyHidden, cl::init(false), cl::ZeroOrMore, cl::desc("Print pseudo probe section and disassembled info.")); @@ -48,38 +52,6 @@ return TheTarget; } -template <class ELFT> -static uint64_t getELFImageLMAForSec(const ELFFile<ELFT> &Obj, - const object::ELFSectionRef &Sec, - StringRef FileName) { - // Search for a PT_LOAD segment containing the requested section. Return this - // segment's p_addr as the image load address for the section. - const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName); - for (const typename ELFT::Phdr &Phdr : PhdrRange) - if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_vaddr <= Sec.getAddress()) && - (Phdr.p_vaddr + Phdr.p_memsz > Sec.getAddress())) - // Segments will always be loaded at a page boundary. - return Phdr.p_paddr & ~(Phdr.p_align - 1U); - return 0; -} - -// Get the image load address for a specific section. Note that an image is -// loaded by segments (a group of sections) and segments may not be consecutive -// in memory. -static uint64_t getELFImageLMAForSec(const object::ELFSectionRef &Sec) { - if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Sec.getObject())) - return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, - ELFObj->getFileName()); - else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Sec.getObject())) - return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, - ELFObj->getFileName()); - else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Sec.getObject())) - return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, - ELFObj->getFileName()); - const auto *ELFObj = cast<ELF64BEObjectFile>(Sec.getObject()); - return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, ELFObj->getFileName()); -} - void ProfiledBinary::load() { // Attempt to open the binary. OwningBinary<Binary> OBinary = unwrapOrError(createBinary(Path), Path); @@ -95,8 +67,8 @@ exitWithError("unsupported target", TheTriple.getTriple()); LLVM_DEBUG(dbgs() << "Loading " << Path << "\n"); - // Find the preferred base address for text sections. - setPreferredBaseAddress(Obj); + // Find the preferred load address for text sections. + setPreferredTextSegmentAddresses(Obj); // Decode pseudo probe related section decodePseudoProbe(Obj); @@ -127,8 +99,9 @@ Context2.begin(), Context2.begin() + Context2.size() - 1); } -std::string ProfiledBinary::getExpandedContextStr( - const SmallVectorImpl<uint64_t> &Stack) const { +std::string +ProfiledBinary::getExpandedContextStr(const SmallVectorImpl<uint64_t> &Stack, + bool &WasLeafInlined) const { std::string ContextStr; SmallVector<std::string, 16> ContextVec; // Process from frame root to leaf @@ -139,6 +112,9 @@ // processing if (ExpandedContext.empty()) return std::string(); + // Set WasLeafInlined to the size of inlined frame count for the last + // address which is leaf + WasLeafInlined = (ExpandedContext.size() > 1); for (const auto &Loc : ExpandedContext) { ContextVec.push_back(getCallSite(Loc)); } @@ -164,16 +140,32 @@ return OContextStr.str(); } -void ProfiledBinary::setPreferredBaseAddress(const ELFObjectFileBase *Obj) { - for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end(); - SI != SE; ++SI) { - const SectionRef &Section = *SI; - if (Section.isText()) { - PreferredBaseAddress = getELFImageLMAForSec(Section); - return; - } +template <class ELFT> +void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj, StringRef FileName) { + const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName); + for (const typename ELFT::Phdr &Phdr : PhdrRange) { + if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_flags & ELF::PF_X)) { + // Segments will always be loaded at a page boundary. + PreferredTextSegmentAddresses.push_back(Phdr.p_vaddr & ~(Phdr.p_align - 1U)); + TextSegmentOffsets.push_back(Phdr.p_offset & ~(Phdr.p_align - 1U)); + } } - exitWithError("no text section found", Obj->getFileName()); + + if (PreferredTextSegmentAddresses.empty()) + exitWithError("no executable segment found", FileName); +} + +void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFObjectFileBase *Obj) { + if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) + setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) + setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) + setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); + else if (const auto *ELFObj = cast<ELF64BEObjectFile>(Obj)) + setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); + else + llvm_unreachable("invalid ELF object format"); } void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) { @@ -204,67 +196,95 @@ SectionSymbolsTy &Symbols, const SectionRef &Section) { std::size_t SE = Symbols.size(); - uint64_t SectionOffset = Section.getAddress() - PreferredBaseAddress; + uint64_t SectionOffset = Section.getAddress() - getPreferredBaseAddress(); uint64_t SectSize = Section.getSize(); - uint64_t StartOffset = Symbols[SI].Addr - PreferredBaseAddress; + uint64_t StartOffset = Symbols[SI].Addr - getPreferredBaseAddress(); uint64_t EndOffset = (SI + 1 < SE) - ? Symbols[SI + 1].Addr - PreferredBaseAddress + ? Symbols[SI + 1].Addr - getPreferredBaseAddress() : SectionOffset + SectSize; if (StartOffset >= EndOffset) return true; - std::string &&SymbolName = Symbols[SI].Name.str(); + StringRef SymbolName = + ShowCanonicalFnName + ? FunctionSamples::getCanonicalFnName(Symbols[SI].Name) + : Symbols[SI].Name; if (ShowDisassemblyOnly) outs() << '<' << SymbolName << ">:\n"; + auto WarnInvalidInsts = [](uint64_t Start, uint64_t End) { + WithColor::warning() << "Invalid instructions at " + << format("%8" PRIx64, Start) << " - " + << format("%8" PRIx64, End) << "\n"; + }; + uint64_t Offset = StartOffset; + // Size of a consecutive invalid instruction range starting from Offset -1 + // backwards. + uint64_t InvalidInstLength = 0; while (Offset < EndOffset) { MCInst Inst; uint64_t Size; // Disassemble an instruction. - if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset), - Offset + PreferredBaseAddress, nulls())) - return false; + bool Disassembled = + DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset), + Offset + getPreferredBaseAddress(), nulls()); + if (Size == 0) + Size = 1; if (ShowDisassemblyOnly) { if (ShowPseudoProbe) { ProbeDecoder.printProbeForAddress(outs(), - Offset + PreferredBaseAddress); + Offset + getPreferredBaseAddress()); } - outs() << format("%8" PRIx64 ":", Offset); + outs() << format("%8" PRIx64 ":", Offset + getPreferredBaseAddress()); size_t Start = outs().tell(); - IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs()); + if (Disassembled) + IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs()); + else + outs() << "\t<unknown>"; if (ShowSourceLocations) { unsigned Cur = outs().tell() - Start; if (Cur < 40) outs().indent(40 - Cur); - InstructionPointer Inst(this, Offset); - outs() << getReversedLocWithContext(symbolize(Inst)); + InstructionPointer IP(this, Offset); + outs() << getReversedLocWithContext(symbolize(IP, ShowCanonicalFnName)); } outs() << "\n"; } - const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode()); + if (Disassembled) { + const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode()); + // Populate a vector of the symbolized callsite at this location + // We don't need symbolized info for probe-based profile, just use an + // empty stack as an entry to indicate a valid binary offset + FrameLocationStack SymbolizedCallStack; + if (!UsePseudoProbes) { + InstructionPointer IP(this, Offset); + SymbolizedCallStack = symbolize(IP, true); + } + Offset2LocStackMap[Offset] = SymbolizedCallStack; + // Populate address maps. + CodeAddrs.push_back(Offset); + if (MCDesc.isCall()) + CallAddrs.insert(Offset); + else if (MCDesc.isReturn()) + RetAddrs.insert(Offset); - // Populate a vector of the symbolized callsite at this location - // We don't need symbolized info for probe-based profile, just use an empty - // stack as an entry to indicate a valid binary offset - FrameLocationStack SymbolizedCallStack; - if (!UsePseudoProbes) { - InstructionPointer IP(this, Offset); - SymbolizedCallStack = symbolize(IP, true); + if (InvalidInstLength) { + WarnInvalidInsts(Offset - InvalidInstLength, Offset - 1); + InvalidInstLength = 0; + } + } else { + InvalidInstLength += Size; } - Offset2LocStackMap[Offset] = SymbolizedCallStack; - // Populate address maps. - CodeAddrs.push_back(Offset); - if (MCDesc.isCall()) - CallAddrs.insert(Offset); - else if (MCDesc.isReturn()) - RetAddrs.insert(Offset); Offset += Size; } + if (InvalidInstLength) + WarnInvalidInsts(Offset - InvalidInstLength, Offset - 1); + if (ShowDisassemblyOnly) outs() << "\n"; @@ -296,9 +316,10 @@ if (!MII) exitWithError("no instruction info for target " + TripleName, FileName); - MCObjectFileInfo MOFI; - MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI); - MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx); + MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get()); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); + Ctx.setObjectFileInfo(MOFI.get()); DisAsm.reset(TheTarget->createMCDisassembler(*STI, Ctx)); if (!DisAsm) exitWithError("no disassembler for target " + TripleName, FileName); @@ -341,7 +362,7 @@ if (!Section.isText()) continue; - uint64_t ImageLoadAddr = PreferredBaseAddress; + uint64_t ImageLoadAddr = getPreferredBaseAddress(); uint64_t SectionOffset = Section.getAddress() - ImageLoadAddr; uint64_t SectSize = Section.getSize(); if (!SectSize) @@ -353,8 +374,9 @@ if (ShowDisassemblyOnly) { StringRef SectionName = unwrapOrError(Section.getName(), FileName); outs() << "\nDisassembly of section " << SectionName; - outs() << " [" << format("0x%" PRIx64, SectionOffset) << ", " - << format("0x%" PRIx64, SectionOffset + SectSize) << "]:\n\n"; + outs() << " [" << format("0x%" PRIx64, Section.getAddress()) << ", " + << format("0x%" PRIx64, Section.getAddress() + SectSize) + << "]:\n\n"; } // Get the section data. @@ -387,7 +409,7 @@ bool UseCanonicalFnName) { assert(this == IP.Binary && "Binary should only symbolize its own instruction"); - auto Addr = object::SectionedAddress{IP.Offset + PreferredBaseAddress, + auto Addr = object::SectionedAddress{IP.Offset + getPreferredBaseAddress(), object::SectionedAddress::UndefSection}; DIInliningInfo InlineStack = unwrapOrError(Symbolizer->symbolizeInlinedCode(Path, Addr), getName()); @@ -403,7 +425,8 @@ FunctionName = FunctionSamples::getCanonicalFnName(FunctionName); LineLocation Line(CallerFrame.Line - CallerFrame.StartLine, DILocation::getBaseDiscriminatorFromDiscriminator( - CallerFrame.Discriminator)); + CallerFrame.Discriminator, + /* IsFSDiscriminator */ false)); FrameLocation Callsite(FunctionName.str(), Line); CallStack.push_back(Callsite); }
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/ProfiledBinary.h b/src/llvm-project/llvm/tools/llvm-profgen/ProfiledBinary.h index ccb1c9d..be1169e 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/ProfiledBinary.h +++ b/src/llvm-project/llvm/tools/llvm-profgen/ProfiledBinary.h
@@ -99,10 +99,13 @@ std::string Path; // The target triple. Triple TheTriple; - // The runtime base address that the executable sections are loaded at. - mutable uint64_t BaseAddress = 0; - // The preferred base address that the executable sections are loaded at. - uint64_t PreferredBaseAddress = 0; + // The runtime base address that the first executable segment is loaded at. + uint64_t BaseAddress = 0; + // The preferred load address of each executable segment. + std::vector<uint64_t> PreferredTextSegmentAddresses; + // The file offset of each executable segment. + std::vector<uint64_t> TextSegmentOffsets; + // Mutiple MC component info std::unique_ptr<const MCRegisterInfo> MRI; std::unique_ptr<const MCAsmInfo> AsmInfo; @@ -136,7 +139,10 @@ bool UsePseudoProbes = false; - void setPreferredBaseAddress(const ELFObjectFileBase *O); + void setPreferredTextSegmentAddresses(const ELFObjectFileBase *O); + + template <class ELFT> + void setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj, StringRef FileName); void decodePseudoProbe(const ELFObjectFileBase *Obj); @@ -174,17 +180,27 @@ setupSymbolizer(); load(); } - uint64_t virtualAddrToOffset(uint64_t VitualAddress) const { - return VitualAddress - BaseAddress; + uint64_t virtualAddrToOffset(uint64_t VirtualAddress) const { + return VirtualAddress - BaseAddress; } uint64_t offsetToVirtualAddr(uint64_t Offset) const { return Offset + BaseAddress; } - const StringRef getPath() const { return Path; } - const StringRef getName() const { return llvm::sys::path::filename(Path); } + StringRef getPath() const { return Path; } + StringRef getName() const { return llvm::sys::path::filename(Path); } uint64_t getBaseAddress() const { return BaseAddress; } void setBaseAddress(uint64_t Address) { BaseAddress = Address; } - uint64_t getPreferredBaseAddress() const { return PreferredBaseAddress; } + + // Return the preferred load address for the first executable segment. + uint64_t getPreferredBaseAddress() const { return PreferredTextSegmentAddresses[0]; } + // Return the file offset for the first executable segment. + uint64_t getTextSegmentOffset() const { return TextSegmentOffsets[0]; } + const std::vector<uint64_t> &getPreferredTextSegmentAddresses() const { + return PreferredTextSegmentAddresses; + } + const std::vector<uint64_t> &getTextSegmentOffsets() const { + return TextSegmentOffsets; + } bool addressIsCode(uint64_t Address) const { uint64_t Offset = virtualAddrToOffset(Address); @@ -226,7 +242,7 @@ return FuncStartAddrMap[Offset]; } - Optional<const FrameLocation> getInlineLeafFrameLoc(uint64_t Offset) { + Optional<FrameLocation> getInlineLeafFrameLoc(uint64_t Offset) { const auto &Stack = getFrameLocationStack(Offset); if (Stack.empty()) return {}; @@ -239,8 +255,8 @@ // Get the context string of the current stack with inline context filled in. // It will search the disassembling info stored in Offset2LocStackMap. This is // used as the key of function sample map - std::string - getExpandedContextStr(const SmallVectorImpl<uint64_t> &Stack) const; + std::string getExpandedContextStr(const SmallVectorImpl<uint64_t> &Stack, + bool &WasLeafInlined) const; const PseudoProbe *getCallProbeForAddr(uint64_t Address) const { return ProbeDecoder.getCallProbeForAddr(Address);
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/PseudoProbe.cpp b/src/llvm-project/llvm/tools/llvm-profgen/PseudoProbe.cpp index a537d90..02af3f0 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/PseudoProbe.cpp +++ b/src/llvm-project/llvm/tools/llvm-profgen/PseudoProbe.cpp
@@ -88,12 +88,7 @@ } OS << "Index: " << Index << " "; OS << "Type: " << PseudoProbeTypeStr[static_cast<uint8_t>(Type)] << " "; - if (isDangling()) { - OS << "Dangling "; - } - if (isTailCall()) { - OS << "TailCall "; - } + std::string InlineContextStr = getInlineContextStr(GUID2FuncMAP, ShowName); if (InlineContextStr.size()) { OS << "Inlined: @ "; @@ -162,7 +157,7 @@ uint64_t GUID = readUnencodedNumber<uint64_t>(); uint64_t Hash = readUnencodedNumber<uint64_t>(); uint32_t NameSize = readUnsignedNumber<uint32_t>(); - StringRef Name = readString(NameSize); + StringRef Name = FunctionSamples::getCanonicalFnName(readString(NameSize)); // Initialize PseudoProbeFuncDesc and populate it into GUID2FuncDescMap GUID2FuncDescMap.emplace(GUID, PseudoProbeFuncDesc(GUID, Hash, Name)); @@ -189,7 +184,7 @@ // TYPE (uint4) // 0 - block probe, 1 - indirect call, 2 - direct call // ATTRIBUTE (uint3) - // 1 - tail call, 2 - dangling + // 1 - reserved // ADDRESS_TYPE (uint1) // 0 - code address, 1 - address delta // CODE_ADDRESS (uint64 or ULEB128) @@ -198,7 +193,6 @@ // A list of NUM_INLINED_FUNCTIONS entries describing each of the // inlined callees. Each record contains: // INLINE SITE - // GUID of the inlinee (uint64) // Index of the callsite probe (ULEB128) // FUNCTION BODY // A FUNCTION BODY entry describing the inlined function. @@ -214,8 +208,11 @@ uint32_t Index = 0; // A DFS-based decoding while (Data < End) { - // Read inline site for inlinees - if (Root != Cur) { + if (Root == Cur) { + // Use a sequential id for top level inliner. + Index = Root->getChildren().size(); + } else { + // Read inline site for inlinees Index = readUnsignedNumber<uint32_t>(); } // Switch/add to a new tree node(inlinee) @@ -243,10 +240,10 @@ Addr = readUnencodedNumber<int64_t>(); } // Populate Address2ProbesMap - std::vector<PseudoProbe> &ProbeVec = Address2ProbesMap[Addr]; - ProbeVec.emplace_back(Addr, Cur->GUID, Index, PseudoProbeType(Kind), Attr, - Cur); - Cur->addProbes(&ProbeVec.back()); + auto &Probes = Address2ProbesMap[Addr]; + Probes.emplace_back(Addr, Cur->GUID, Index, PseudoProbeType(Kind), Attr, + Cur); + Cur->addProbes(&Probes.back()); LastAddr = Addr; } @@ -298,7 +295,7 @@ auto It = Address2ProbesMap.find(Address); if (It == Address2ProbesMap.end()) return nullptr; - const std::vector<PseudoProbe> &Probes = It->second; + const auto &Probes = It->second; const PseudoProbe *CallProbe = nullptr; for (const auto &Probe : Probes) {
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/PseudoProbe.h b/src/llvm-project/llvm/tools/llvm-profgen/PseudoProbe.h index 2077724..62d46d3 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/PseudoProbe.h +++ b/src/llvm-project/llvm/tools/llvm-profgen/PseudoProbe.h
@@ -24,7 +24,7 @@ namespace llvm { namespace sampleprof { -enum PseudoProbeAttributes { TAILCALL = 1, DANGLING = 2 }; +enum PseudoProbeAttributes { RESERVED = 1 }; // Use func GUID and index as the location info of the inline site using InlineSite = std::tuple<uint64_t, uint32_t>; @@ -45,9 +45,10 @@ return std::get<0>(Site) ^ std::get<1>(Site); } }; - std::unordered_map<InlineSite, std::unique_ptr<PseudoProbeInlineTree>, - InlineSiteHash> - Children; + using InlinedProbeTreeMap = + std::unordered_map<InlineSite, std::unique_ptr<PseudoProbeInlineTree>, + InlineSiteHash>; + InlinedProbeTreeMap Children; public: // Inlinee function GUID @@ -71,6 +72,8 @@ return Ret.first->second.get(); } + InlinedProbeTreeMap &getChildren() { return Children; } + std::vector<PseudoProbe *> &getProbes() { return ProbeVector; } void addProbes(PseudoProbe *Probe) { ProbeVector.push_back(Probe); } // Return false if it's a dummy inline site bool hasInlineSite() const { return std::get<0>(ISite) != 0; } @@ -91,7 +94,7 @@ // GUID to PseudoProbeFuncDesc map using GUIDProbeFunctionMap = std::unordered_map<uint64_t, PseudoProbeFuncDesc>; // Address to pseudo probes map. -using AddressProbesMap = std::unordered_map<uint64_t, std::vector<PseudoProbe>>; +using AddressProbesMap = std::unordered_map<uint64_t, std::list<PseudoProbe>>; /* A pseudo probe has the format like below: @@ -99,7 +102,7 @@ TYPE (uint4) 0 - block probe, 1 - indirect call, 2 - direct call ATTRIBUTE (uint3) - 1 - tail call, 2 - dangling + 1 - reserved ADDRESS_TYPE (uint1) 0 - code address, 1 - address delta CODE_ADDRESS (uint64 or ULEB128) @@ -121,20 +124,13 @@ InlineTree(Tree){}; bool isEntry() const { return Index == PseudoProbeFirstId; } - - bool isDangling() const { - return Attribute & static_cast<uint8_t>(PseudoProbeAttributes::DANGLING); - } - - bool isTailCall() const { - return Attribute & static_cast<uint8_t>(PseudoProbeAttributes::TAILCALL); - } - bool isBlock() const { return Type == PseudoProbeType::Block; } bool isIndirectCall() const { return Type == PseudoProbeType::IndirectCall; } bool isDirectCall() const { return Type == PseudoProbeType::DirectCall; } bool isCall() const { return isIndirectCall() || isDirectCall(); } + PseudoProbeInlineTree *getInlineTreeNode() const { return InlineTree; } + // Get the inlined context by traversing current inline tree backwards, // each tree node has its InlineSite which is taken as the context. // \p ContextStack is populated in root to leaf order
diff --git a/src/llvm-project/llvm/tools/llvm-profgen/llvm-profgen.cpp b/src/llvm-project/llvm/tools/llvm-profgen/llvm-profgen.cpp index 081f1bb..8ea0156 100644 --- a/src/llvm-project/llvm/tools/llvm-profgen/llvm-profgen.cpp +++ b/src/llvm-project/llvm/tools/llvm-profgen/llvm-profgen.cpp
@@ -18,16 +18,20 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/TargetSelect.h" +static cl::OptionCategory ProfGenCategory("ProfGen Options"); + static cl::list<std::string> PerfTraceFilenames( "perfscript", cl::value_desc("perfscript"), cl::OneOrMore, llvm::cl::MiscFlags::CommaSeparated, cl::desc("Path of perf-script trace created by Linux perf tool with " - "`script` command(the raw perf.data should be profiled with -b)")); + "`script` command(the raw perf.data should be profiled with -b)"), + cl::cat(ProfGenCategory)); static cl::list<std::string> BinaryFilenames("binary", cl::value_desc("binary"), cl::OneOrMore, llvm::cl::MiscFlags::CommaSeparated, - cl::desc("Path of profiled binary files")); + cl::desc("Path of profiled binary files"), + cl::cat(ProfGenCategory)); extern cl::opt<bool> ShowDisassemblyOnly; @@ -42,6 +46,7 @@ InitializeAllTargetMCs(); InitializeAllDisassemblers(); + cl::HideUnrelatedOptions({&ProfGenCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm SPGO profile generator\n"); // Load binaries and parse perf events and samples
diff --git a/src/llvm-project/llvm/tools/llvm-rc/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-rc/CMakeLists.txt index 4cadc17..71b7994 100644 --- a/src/llvm-project/llvm/tools/llvm-rc/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-rc/CMakeLists.txt
@@ -1,12 +1,16 @@ set(LLVM_LINK_COMPONENTS + Object Option Support ) set(LLVM_TARGET_DEFINITIONS Opts.td) - tablegen(LLVM Opts.inc -gen-opt-parser-defs) -add_public_tablegen_target(RcTableGen) +add_public_tablegen_target(RcOptsTableGen) + +set(LLVM_TARGET_DEFINITIONS WindresOpts.td) +tablegen(LLVM WindresOpts.inc -gen-opt-parser-defs) +add_public_tablegen_target(WindresOptsTableGen) add_llvm_tool(llvm-rc llvm-rc.cpp @@ -16,3 +20,9 @@ ResourceScriptStmt.cpp ResourceScriptToken.cpp ) + +add_llvm_tool_symlink(llvm-windres llvm-rc) + +if(LLVM_INSTALL_BINUTILS_SYMLINKS) + add_llvm_tool_symlink(windres llvm-rc) +endif()
diff --git a/src/llvm-project/llvm/tools/llvm-rc/Opts.td b/src/llvm-project/llvm/tools/llvm-rc/Opts.td index 613f0a0..6d9c0e2 100644 --- a/src/llvm-project/llvm/tools/llvm-rc/Opts.td +++ b/src/llvm-project/llvm/tools/llvm-rc/Opts.td
@@ -4,55 +4,62 @@ // These options seem to be important for the tool // and should be implemented. -def fileout : JoinedOrSeparate<[ "/", "-" ], "FO">, - HelpText<"Change the output file location.">; +class S<string name, string help> : + Separate<["/", "-"], name>, HelpText<help>; -def define : Separate<[ "/", "-" ], "D">, - HelpText<"Define a symbol for the C preprocessor.">; -def undef : Separate<[ "/", "-" ], "U">, - HelpText<"Undefine a symbol for the C preprocessor.">; +class JS<string name, string help> : + JoinedOrSeparate<["/", "-"], name>, HelpText<help>; -def lang_id : JoinedOrSeparate<[ "/", "-" ], "L">, - HelpText<"Set the default language identifier.">; -def lang_name : Separate<[ "/", "-" ], "LN">, - HelpText<"Set the default language name.">; +class F<string name, string help> : Flag<["/", "-"], name>, HelpText<help>; -def includepath : Separate<[ "/", "-" ], "I">, HelpText<"Add an include path.">; -def noinclude : Flag<[ "/", "-" ], "X">, HelpText<"Ignore 'include' variable.">; +class F_nodoc<string name> : Flag<["/", "-"], name>; +class S_nodoc<string name> : Separate<["/", "-"], name>; -def add_null : Flag<[ "/", "-" ], "N">, - HelpText<"Null-terminate all strings in the string table.">; +def fileout : JS<"FO", "Change the output file location.">; -def dupid_nowarn : Flag<[ "/", "-" ], "Y">, - HelpText<"Suppress warnings on duplicate resource IDs.">; +def define : JS<"D", "Define a symbol for the C preprocessor.">; +def undef : JS<"U", "Undefine a symbol for the C preprocessor.">; -def verbose : Flag<[ "/", "-" ], "V">, HelpText<"Be verbose.">; -def help : Flag<[ "/", "-" ], "?">, HelpText<"Display this help and exit.">; -def h : Flag<[ "/", "-" ], "H">, - Alias<help>, - HelpText<"Display this help and exit.">; +def lang_id : JS<"L", "Set the default language identifier.">; +def lang_name : S<"LN", "Set the default language name.">; -def dry_run : Flag<[ "/", "-" ], "dry-run">, - HelpText<"Don't compile the input; only try to parse it.">; +def includepath : JS<"I", "Add an include path.">; +def noinclude : F<"X", "Ignore 'include' variable.">; -def codepage : JoinedOrSeparate<[ "/", "-" ], "C">, - HelpText<"Set the codepage used for input strings.">; +def add_null : F<"N", "Null-terminate all strings in the string table.">; + +def dupid_nowarn : F<"Y", "Suppress warnings on duplicate resource IDs.">; + +def verbose : F<"V", "Be verbose.">; +def help : F<"?", "Display this help and exit.">; +def h : F<"H", "Display this help and exit.">, Alias<help>; + +def codepage : JS<"C", "Set the codepage used for input strings.">; + +// llvm-rc specific options: + +def dry_run : F<"dry-run", "Don't compile the input; only try to parse it.">; + +def no_preprocess : F<"no-preprocess", "Don't try to preprocess the input file.">; + +// Print (but do not run) the commands to run for preprocessing +def _HASH_HASH_HASH : F_nodoc<"###">; // Unused switches (at least for now). These will stay unimplemented // in an early stage of development and can be ignored. However, we need to // parse them in order to preserve the compatibility with the original tool. -def nologo : Flag<[ "/", "-" ], "NOLOGO">; -def r : Flag<[ "/", "-" ], "R">; -def sl : Flag<[ "/", "-" ], "SL">; +def nologo : F_nodoc<"NOLOGO">; +def r : F_nodoc<"R">; +def sl : F_nodoc<"SL">; // (Codepages support.) -def w : Flag<[ "/", "-" ], "W">; +def w : F_nodoc<"W">; // (Support of MUI and similar.) -def fm : Separate<[ "/", "-" ], "FM">; -def q : Separate<[ "/", "-" ], "Q">; -def g : Flag<[ "/", "-" ], "G">; -def gn : Flag<[ "/", "-" ], "GN">; -def g1 : Flag<[ "/", "-" ], "G1">; -def g2 : Flag<[ "/", "-" ], "G2">; +def fm : S_nodoc<"FM">; +def q : S_nodoc<"Q">; +def g : F_nodoc<"G">; +def gn : F_nodoc<"GN">; +def g1 : F_nodoc<"G1">; +def g2 : F_nodoc<"G2">;
diff --git a/src/llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.cpp b/src/llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.cpp index 553bb75..60287a3 100644 --- a/src/llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.cpp +++ b/src/llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.cpp
@@ -99,7 +99,7 @@ return false; // Just take the contents of the string, checking if it's been marked long. - IsLongString = Str.startswith_lower("L"); + IsLongString = Str.startswith_insensitive("L"); if (IsLongString) Str = Str.drop_front(); @@ -1524,14 +1524,16 @@ // properly though, so if using that to append paths below, this early // exception case could be removed.) if (sys::path::has_root_directory(File)) - return errorOrToExpected(MemoryBuffer::getFile(File, -1, false)); + return errorOrToExpected(MemoryBuffer::getFile( + File, /*IsText=*/false, /*RequiresNullTerminator=*/false)); // 1. The current working directory. sys::fs::current_path(Cwd); Path.assign(Cwd.begin(), Cwd.end()); sys::path::append(Path, File); if (sys::fs::exists(Path)) - return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); + return errorOrToExpected(MemoryBuffer::getFile( + Path, /*IsText=*/false, /*RequiresNullTerminator=*/false)); // 2. The directory of the input resource file, if it is different from the // current working directory. @@ -1539,19 +1541,23 @@ Path.assign(InputFileDir.begin(), InputFileDir.end()); sys::path::append(Path, File); if (sys::fs::exists(Path)) - return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); + return errorOrToExpected(MemoryBuffer::getFile( + Path, /*IsText=*/false, /*RequiresNullTerminator=*/false)); // 3. All of the include directories specified on the command line. for (StringRef ForceInclude : Params.Include) { Path.assign(ForceInclude.begin(), ForceInclude.end()); sys::path::append(Path, File); if (sys::fs::exists(Path)) - return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); + return errorOrToExpected(MemoryBuffer::getFile( + Path, /*IsText=*/false, /*RequiresNullTerminator=*/false)); } - if (auto Result = - llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude)) - return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false)); + if (!Params.NoInclude) { + if (auto Result = llvm::sys::Process::FindInEnvPath("INCLUDE", File)) + return errorOrToExpected(MemoryBuffer::getFile( + *Result, /*IsText=*/false, /*RequiresNullTerminator=*/false)); + } return make_error<StringError>("error : file not found : " + Twine(File), inconvertibleErrorCode());
diff --git a/src/llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.h b/src/llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.h index d545a7a..0f3d593 100644 --- a/src/llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.h +++ b/src/llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.h
@@ -35,7 +35,7 @@ struct WriterParams { std::vector<std::string> Include; // Additional folders to search for files. - std::vector<std::string> NoInclude; // Folders to exclude from file search. + bool NoInclude; // Ignore the INCLUDE variable. StringRef InputFilePath; // The full path of the input file. int CodePage = CpAcp; // The codepage for interpreting characters. };
diff --git a/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptCppFilter.cpp b/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptCppFilter.cpp index e610be9..6657aa5 100644 --- a/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptCppFilter.cpp +++ b/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptCppFilter.cpp
@@ -91,7 +91,7 @@ StringRef Ext = Line.rsplit('.').second; - if (Ext.equals_lower("h") || Ext.equals_lower("c")) { + if (Ext.equals_insensitive("h") || Ext.equals_insensitive("c")) { Outputting = false; } else { Outputting = true;
diff --git a/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp b/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp index 5141ac0..4045131 100644 --- a/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp +++ b/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp
@@ -215,7 +215,7 @@ } case Kind::Identifier: { - if (!read().value().equals_lower("not")) + if (!read().value().equals_insensitive("not")) return getExpectedError(ErrorMsg, true); ASSIGN_OR_RETURN(Result, parseIntExpr2()); return IntWithNotMask(0, (*Result).getValue()); @@ -330,7 +330,7 @@ bool FoundFlag = false; for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { - if (!FlagResult->equals_lower(FlagDesc[FlagId])) + if (!FlagResult->equals_insensitive(FlagDesc[FlagId])) continue; Result |= FlagValues[FlagId]; @@ -351,23 +351,23 @@ if (Token.kind() != Kind::Identifier) return Flags; const StringRef Ident = Token.value(); - if (Ident.equals_lower("PRELOAD")) + if (Ident.equals_insensitive("PRELOAD")) Flags |= MfPreload; - else if (Ident.equals_lower("LOADONCALL")) + else if (Ident.equals_insensitive("LOADONCALL")) Flags &= ~MfPreload; - else if (Ident.equals_lower("FIXED")) + else if (Ident.equals_insensitive("FIXED")) Flags &= ~(MfMoveable | MfDiscardable); - else if (Ident.equals_lower("MOVEABLE")) + else if (Ident.equals_insensitive("MOVEABLE")) Flags |= MfMoveable; - else if (Ident.equals_lower("DISCARDABLE")) + else if (Ident.equals_insensitive("DISCARDABLE")) Flags |= MfDiscardable | MfMoveable | MfPure; - else if (Ident.equals_lower("PURE")) + else if (Ident.equals_insensitive("PURE")) Flags |= MfPure; - else if (Ident.equals_lower("IMPURE")) + else if (Ident.equals_insensitive("IMPURE")) Flags &= ~(MfPure | MfDiscardable); - else if (Ident.equals_lower("SHARED")) + else if (Ident.equals_insensitive("SHARED")) Flags |= MfPure; - else if (Ident.equals_lower("NONSHARED")) + else if (Ident.equals_insensitive("NONSHARED")) Flags &= ~(MfPure | MfDiscardable); else return Flags; @@ -392,23 +392,23 @@ Expected<std::unique_ptr<OptionalStmt>> RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { ASSIGN_OR_RETURN(TypeToken, readIdentifier()); - if (TypeToken->equals_lower("CHARACTERISTICS")) + if (TypeToken->equals_insensitive("CHARACTERISTICS")) return parseCharacteristicsStmt(); - if (TypeToken->equals_lower("LANGUAGE")) + if (TypeToken->equals_insensitive("LANGUAGE")) return parseLanguageStmt(); - if (TypeToken->equals_lower("VERSION")) + if (TypeToken->equals_insensitive("VERSION")) return parseVersionStmt(); if (StmtsType != OptStmtType::BasicStmt) { - if (TypeToken->equals_lower("CAPTION")) + if (TypeToken->equals_insensitive("CAPTION")) return parseCaptionStmt(); - if (TypeToken->equals_lower("CLASS")) + if (TypeToken->equals_insensitive("CLASS")) return parseClassStmt(); - if (TypeToken->equals_lower("EXSTYLE")) + if (TypeToken->equals_insensitive("EXSTYLE")) return parseExStyleStmt(); - if (TypeToken->equals_lower("FONT")) + if (TypeToken->equals_insensitive("FONT")) return parseFontStmt(StmtsType); - if (TypeToken->equals_lower("STYLE")) + if (TypeToken->equals_insensitive("STYLE")) return parseStyleStmt(); } @@ -506,15 +506,14 @@ RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); std::vector<IntOrString> Data; - // Consume comma before each consecutive token except the first one. - bool ConsumeComma = false; while (!consumeOptionalType(Kind::BlockEnd)) { - if (ConsumeComma) - RETURN_IF_ERROR(consumeType(Kind::Comma)); - ConsumeComma = true; - ASSIGN_OR_RETURN(Item, readIntOrString()); Data.push_back(*Item); + + // There can be zero or more commas after each token (but not before + // the first one). + while (consumeOptionalType(Kind::Comma)) { + } } return std::make_unique<UserDefinedResource>(Type, std::move(Data), @@ -635,15 +634,15 @@ while (!consumeOptionalType(Kind::BlockEnd)) { ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); - bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM"); - bool IsPopup = ItemTypeResult->equals_lower("POPUP"); + bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM"); + bool IsPopup = ItemTypeResult->equals_insensitive("POPUP"); if (!IsMenuItem && !IsPopup) return getExpectedError("MENUITEM, POPUP, END or '}'", true); if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { // Now, expecting SEPARATOR. ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); - if (SeparatorResult->equals_lower("SEPARATOR")) { + if (SeparatorResult->equals_insensitive("SEPARATOR")) { List.addDefinition(std::make_unique<MenuSeparator>()); continue; } @@ -731,12 +730,12 @@ // Expect either BLOCK or VALUE, then a name or a key (a string). ASSIGN_OR_RETURN(TypeResult, readIdentifier()); - if (TypeResult->equals_lower("BLOCK")) { + if (TypeResult->equals_insensitive("BLOCK")) { ASSIGN_OR_RETURN(NameResult, readString()); return parseVersionInfoBlockContents(*NameResult); } - if (TypeResult->equals_lower("VALUE")) { + if (TypeResult->equals_insensitive("VALUE")) { ASSIGN_OR_RETURN(KeyResult, readString()); // Read a non-empty list of strings and/or ints, each // possibly preceded by a comma. Unfortunately, the tool behavior depends
diff --git a/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptStmt.h b/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptStmt.h index 27fbea3..9f77d36 100644 --- a/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptStmt.h +++ b/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptStmt.h
@@ -145,7 +145,7 @@ : Data(Token), IsInt(Token.kind() == RCToken::Kind::Int) {} bool equalsLower(const char *Str) { - return !IsInt && Data.String.equals_lower(Str); + return !IsInt && Data.String.equals_insensitive(Str); } bool isInt() const { return IsInt; }
diff --git a/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptToken.cpp b/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptToken.cpp index 2e21f67..a8f40ab 100644 --- a/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptToken.cpp +++ b/src/llvm-project/llvm/tools/llvm-rc/ResourceScriptToken.cpp
@@ -288,7 +288,7 @@ assert(!streamEof()); const char CurChar = Data[Pos]; return std::isalnum(CurChar) || CurChar == '_' || CurChar == '.' || - CurChar == '/' || CurChar == '\\'; + CurChar == '/' || CurChar == '\\' || CurChar == '-'; } bool Tokenizer::canStartInt() const { @@ -350,9 +350,9 @@ assert(Token.kind() == Kind::Identifier); StringRef Name = Token.value(); - if (Name.equals_lower("begin")) + if (Name.equals_insensitive("begin")) Token = RCToken(Kind::BlockBegin, Name); - else if (Name.equals_lower("end")) + else if (Name.equals_insensitive("end")) Token = RCToken(Kind::BlockEnd, Name); }
diff --git a/src/llvm-project/llvm/tools/llvm-rc/WindresOpts.td b/src/llvm-project/llvm/tools/llvm-rc/WindresOpts.td new file mode 100644 index 0000000..3c75c85 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-rc/WindresOpts.td
@@ -0,0 +1,62 @@ +include "llvm/Option/OptParser.td" + +multiclass Long<string name, string help> { + def NAME: Separate<["--"], name>; + def NAME # _eq: Joined<["--"], name # "=">, Alias<!cast<Separate>(NAME)>, + HelpText<help>; +} + +multiclass LongAlias<string name, Option orig> { + def NAME: Separate<["--"], name>, Alias<orig>; + def NAME # _eq: Joined<["--"], name # "=">, Alias<orig>; +} + +multiclass LongShort<string short, string long, string help> { + def NAME: Separate<["--"], long>; + def NAME # _eq: Joined<["--"], long # "=">, Alias<!cast<Separate>(NAME)>, + HelpText<help>; + def NAME # _short: JoinedOrSeparate<["-"], short>, Alias<!cast<Separate>(NAME)>; +} + +multiclass F<string short, string long, string help> { + def NAME: Flag<["-"], short>; + def NAME # _long: Flag<["--"], long>, Alias<!cast<Flag>(NAME)>, + HelpText<help>; +} + +defm input : LongShort<"i", "input", "Input file">; + +defm output : LongShort<"o", "output", "Output file">; + +defm input_format : LongShort<"J", "input-format", "Input format">; + +defm output_format : LongShort<"O", "output-format", "Output format">; + +defm preprocessor : Long<"preprocessor", "Custom preprocessor command">; +defm preprocessor_arg : Long<"preprocessor-arg", "Preprocessor command argument">; + +defm target : LongShort<"F", "target", "Target BFD format name">; + +defm include_dir : LongShort<"I", "include-dir", "Include directory">; +defm include_alias : LongAlias<"include", include_dir>; + +defm define : LongShort<"D", "define", "Define to pass to the preprocessor">; + +defm undef : LongShort<"U", "undefine", "Undefine to pass to the preprocessor">; + +defm codepage : LongShort<"c", "codepage", "Default codepage to use">; + +defm language : LongShort<"l", "language", "Default language to use (0x0-0xffff)">; + +defm verbose : F<"v", "verbose", "Enable verbose output">; +defm version : F<"V", "version", "Display version">; + +defm help : F<"h", "help", "Display this message and exit">; + +// Print (but do not run) the commands to run for preprocessing +def _HASH_HASH_HASH : Flag<["-"], "###">; + +def no_preprocess : Flag<["--"], "no-preprocess">; + +// Unimplemented options for compatibility +def use_temp_file: Flag<["--"], "use-temp-file">;
diff --git a/src/llvm-project/llvm/tools/llvm-rc/llvm-rc.cpp b/src/llvm-project/llvm/tools/llvm-rc/llvm-rc.cpp index e9027a21..83925c2 100644 --- a/src/llvm-project/llvm/tools/llvm-rc/llvm-rc.cpp +++ b/src/llvm-project/llvm/tools/llvm-rc/llvm-rc.cpp
@@ -17,17 +17,24 @@ #include "ResourceScriptStmt.h" #include "ResourceScriptToken.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Object/WindowsResource.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -70,47 +77,530 @@ RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {} }; +enum Windres_ID { + WINDRES_INVALID = 0, // This is not a correct option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + WINDRES_##ID, +#include "WindresOpts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const WINDRES_##NAME[] = VALUE; +#include "WindresOpts.inc" +#undef PREFIX + +static const opt::OptTable::Info WindresInfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + WINDRES_##PREFIX, NAME, HELPTEXT, \ + METAVAR, WINDRES_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, WINDRES_##GROUP, \ + WINDRES_##ALIAS, ALIASARGS, VALUES}, +#include "WindresOpts.inc" +#undef OPTION +}; + +class WindresOptTable : public opt::OptTable { +public: + WindresOptTable() : OptTable(WindresInfoTable, /* IgnoreCase = */ false) {} +}; + static ExitOnError ExitOnErr; +static FileRemover TempPreprocFile; +static FileRemover TempResFile; LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) { errs() << Message << "\n"; exit(1); } -} // anonymous namespace +std::string createTempFile(const Twine &Prefix, StringRef Suffix) { + std::error_code EC; + SmallString<128> FileName; + if ((EC = sys::fs::createTemporaryFile(Prefix, Suffix, FileName))) + fatalError("Unable to create temp file: " + EC.message()); + return static_cast<std::string>(FileName); +} -int main(int Argc, const char **Argv) { - InitLLVM X(Argc, Argv); - ExitOnErr.setBanner("llvm-rc: "); +ErrorOr<std::string> findClang(const char *Argv0) { + StringRef Parent = llvm::sys::path::parent_path(Argv0); + ErrorOr<std::string> Path = std::error_code(); + if (!Parent.empty()) { + // First look for the tool with all potential names in the specific + // directory of Argv0, if known + for (const auto *Name : {"clang", "clang-cl"}) { + Path = sys::findProgramByName(Name, Parent); + if (Path) + return Path; + } + } + // If no parent directory known, or not found there, look everywhere in PATH + for (const auto *Name : {"clang", "clang-cl"}) { + Path = sys::findProgramByName(Name); + if (Path) + return Path; + } + return Path; +} - RcOptTable T; +bool isUsableArch(Triple::ArchType Arch) { + switch (Arch) { + case Triple::x86: + case Triple::x86_64: + case Triple::arm: + case Triple::thumb: + case Triple::aarch64: + // These work properly with the clang driver, setting the expected + // defines such as _WIN32 etc. + return true; + default: + // Other archs aren't set up for use with windows as target OS, (clang + // doesn't define e.g. _WIN32 etc), so with them we need to set a + // different default arch. + return false; + } +} + +Triple::ArchType getDefaultFallbackArch() { + return Triple::x86_64; +} + +std::string getClangClTriple() { + Triple T(sys::getDefaultTargetTriple()); + if (!isUsableArch(T.getArch())) + T.setArch(getDefaultFallbackArch()); + T.setOS(Triple::Win32); + T.setVendor(Triple::PC); + T.setEnvironment(Triple::MSVC); + T.setObjectFormat(Triple::COFF); + return T.str(); +} + +std::string getMingwTriple() { + Triple T(sys::getDefaultTargetTriple()); + if (!isUsableArch(T.getArch())) + T.setArch(getDefaultFallbackArch()); + if (T.isWindowsGNUEnvironment()) + return T.str(); + // Write out the literal form of the vendor/env here, instead of + // constructing them with enum values (which end up with them in + // normalized form). The literal form of the triple can matter for + // finding include files. + return (Twine(T.getArchName()) + "-w64-mingw32").str(); +} + +enum Format { Rc, Res, Coff, Unknown }; + +struct RcOptions { + bool Preprocess = true; + bool PrintCmdAndExit = false; + std::string Triple; + std::vector<std::string> PreprocessCmd; + std::vector<std::string> PreprocessArgs; + + std::string InputFile; + Format InputFormat = Rc; + std::string OutputFile; + Format OutputFormat = Res; + + bool BeVerbose = false; + WriterParams Params; + bool AppendNull = false; + bool IsDryRun = false; + // Set the default language; choose en-US arbitrarily. + unsigned LangId = (/*PrimaryLangId*/ 0x09) | (/*SubLangId*/ 0x01 << 10); +}; + +bool preprocess(StringRef Src, StringRef Dst, const RcOptions &Opts, + const char *Argv0) { + std::string Clang; + if (Opts.PrintCmdAndExit) { + Clang = "clang"; + } else { + ErrorOr<std::string> ClangOrErr = findClang(Argv0); + if (ClangOrErr) { + Clang = *ClangOrErr; + } else { + errs() << "llvm-rc: Unable to find clang, skipping preprocessing." + << "\n"; + errs() << "Pass -no-cpp to disable preprocessing. This will be an error " + "in the future." + << "\n"; + return false; + } + } + + SmallVector<StringRef, 8> Args = { + Clang, "--driver-mode=gcc", "-target", Opts.Triple, "-E", + "-xc", "-DRC_INVOKED"}; + if (!Opts.PreprocessCmd.empty()) { + Args.clear(); + for (const auto &S : Opts.PreprocessCmd) + Args.push_back(S); + } + Args.push_back(Src); + Args.push_back("-o"); + Args.push_back(Dst); + for (const auto &S : Opts.PreprocessArgs) + Args.push_back(S); + if (Opts.PrintCmdAndExit || Opts.BeVerbose) { + for (const auto &A : Args) { + outs() << " "; + sys::printArg(outs(), A, Opts.PrintCmdAndExit); + } + outs() << "\n"; + if (Opts.PrintCmdAndExit) + exit(0); + } + // The llvm Support classes don't handle reading from stdout of a child + // process; otherwise we could avoid using a temp file. + int Res = sys::ExecuteAndWait(Clang, Args); + if (Res) { + fatalError("llvm-rc: Preprocessing failed."); + } + return true; +} + +static std::pair<bool, std::string> isWindres(llvm::StringRef Argv0) { + StringRef ProgName = llvm::sys::path::stem(Argv0); + // x86_64-w64-mingw32-windres -> x86_64-w64-mingw32, windres + // llvm-rc -> "", llvm-rc + // aarch64-w64-mingw32-llvm-windres-10.exe -> aarch64-w64-mingw32, llvm-windres + ProgName = ProgName.rtrim("0123456789.-"); + if (!ProgName.consume_back_insensitive("windres")) + return std::make_pair<bool, std::string>(false, ""); + ProgName.consume_back_insensitive("llvm-"); + ProgName.consume_back_insensitive("-"); + return std::make_pair<bool, std::string>(true, ProgName.str()); +} + +Format parseFormat(StringRef S) { + Format F = StringSwitch<Format>(S.lower()) + .Case("rc", Rc) + .Case("res", Res) + .Case("coff", Coff) + .Default(Unknown); + if (F == Unknown) + fatalError("Unable to parse '" + Twine(S) + "' as a format"); + return F; +} + +void deduceFormat(Format &Dest, StringRef File) { + Format F = StringSwitch<Format>(sys::path::extension(File.lower())) + .Case(".rc", Rc) + .Case(".res", Res) + .Case(".o", Coff) + .Case(".obj", Coff) + .Default(Unknown); + if (F != Unknown) + Dest = F; +} + +std::string unescape(StringRef S) { + std::string Out; + Out.reserve(S.size()); + for (int I = 0, E = S.size(); I < E; I++) { + if (S[I] == '\\') { + if (I + 1 < E) + Out.push_back(S[++I]); + else + fatalError("Unterminated escape"); + continue; + } + Out.push_back(S[I]); + } + return Out; +} + +std::vector<std::string> unescapeSplit(StringRef S) { + std::vector<std::string> OutArgs; + std::string Out; + bool InQuote = false; + for (int I = 0, E = S.size(); I < E; I++) { + if (S[I] == '\\') { + if (I + 1 < E) + Out.push_back(S[++I]); + else + fatalError("Unterminated escape"); + continue; + } + if (S[I] == '"') { + InQuote = !InQuote; + continue; + } + if (S[I] == ' ' && !InQuote) { + OutArgs.push_back(Out); + Out.clear(); + continue; + } + Out.push_back(S[I]); + } + if (InQuote) + fatalError("Unterminated quote"); + if (!Out.empty()) + OutArgs.push_back(Out); + return OutArgs; +} + +RcOptions parseWindresOptions(ArrayRef<const char *> ArgsArr, + ArrayRef<const char *> InputArgsArray, + std::string Prefix) { + WindresOptTable T; + RcOptions Opts; unsigned MAI, MAC; - const char **DashDash = std::find_if( - Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; }); - ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash); + opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); + // The tool prints nothing when invoked with no command-line arguments. + if (InputArgs.hasArg(WINDRES_help)) { + T.printHelp(outs(), "windres [options] file...", + "LLVM windres (GNU windres compatible)", false, true); + exit(0); + } + + if (InputArgs.hasArg(WINDRES_version)) { + outs() << "llvm-windres, compatible with GNU windres\n"; + cl::PrintVersionMessage(); + exit(0); + } + + std::vector<std::string> FileArgs = InputArgs.getAllArgValues(WINDRES_INPUT); + FileArgs.insert(FileArgs.end(), InputArgsArray.begin(), InputArgsArray.end()); + + if (InputArgs.hasArg(WINDRES_input)) { + Opts.InputFile = InputArgs.getLastArgValue(WINDRES_input).str(); + } else if (!FileArgs.empty()) { + Opts.InputFile = FileArgs.front(); + FileArgs.erase(FileArgs.begin()); + } else { + // TODO: GNU windres takes input on stdin in this case. + fatalError("Missing input file"); + } + + if (InputArgs.hasArg(WINDRES_output)) { + Opts.OutputFile = InputArgs.getLastArgValue(WINDRES_output).str(); + } else if (!FileArgs.empty()) { + Opts.OutputFile = FileArgs.front(); + FileArgs.erase(FileArgs.begin()); + } else { + // TODO: GNU windres writes output in rc form to stdout in this case. + fatalError("Missing output file"); + } + + if (InputArgs.hasArg(WINDRES_input_format)) { + Opts.InputFormat = + parseFormat(InputArgs.getLastArgValue(WINDRES_input_format)); + } else { + deduceFormat(Opts.InputFormat, Opts.InputFile); + } + if (Opts.InputFormat == Coff) + fatalError("Unsupported input format"); + + if (InputArgs.hasArg(WINDRES_output_format)) { + Opts.OutputFormat = + parseFormat(InputArgs.getLastArgValue(WINDRES_output_format)); + } else { + // The default in windres differs from the default in RcOptions + Opts.OutputFormat = Coff; + deduceFormat(Opts.OutputFormat, Opts.OutputFile); + } + if (Opts.OutputFormat == Rc) + fatalError("Unsupported output format"); + if (Opts.InputFormat == Opts.OutputFormat) { + outs() << "Nothing to do.\n"; + exit(0); + } + + Opts.PrintCmdAndExit = InputArgs.hasArg(WINDRES__HASH_HASH_HASH); + Opts.Preprocess = !InputArgs.hasArg(WINDRES_no_preprocess); + Triple TT(Prefix); + if (InputArgs.hasArg(WINDRES_target)) { + StringRef Value = InputArgs.getLastArgValue(WINDRES_target); + if (Value == "pe-i386") + Opts.Triple = "i686-w64-mingw32"; + else if (Value == "pe-x86-64") + Opts.Triple = "x86_64-w64-mingw32"; + else + // Implicit extension; if the --target value isn't one of the known + // BFD targets, allow setting the full triple string via this instead. + Opts.Triple = Value.str(); + } else if (TT.getArch() != Triple::UnknownArch) + Opts.Triple = Prefix; + else + Opts.Triple = getMingwTriple(); + + for (const auto *Arg : + InputArgs.filtered(WINDRES_include_dir, WINDRES_define, WINDRES_undef, + WINDRES_preprocessor_arg)) { + // GNU windres passes the arguments almost as-is on to popen() (it only + // backslash escapes spaces in the arguments), where a shell would + // unescape backslash escapes for quotes and similar. This means that + // when calling GNU windres, callers need to double escape chars like + // quotes, e.g. as -DSTRING=\\\"1.2.3\\\". + // + // Exactly how the arguments are interpreted depends on the platform + // though - but the cases where this matters (where callers would have + // done this double escaping) probably is confined to cases like these + // quoted string defines, and those happen to work the same across unix + // and windows. + std::string Unescaped = unescape(Arg->getValue()); + switch (Arg->getOption().getID()) { + case WINDRES_include_dir: + // Technically, these are handled the same way as e.g. defines, but + // the way we consistently unescape the unix way breaks windows paths + // with single backslashes. Alternatively, our unescape function would + // need to mimic the platform specific command line parsing/unescaping + // logic. + Opts.Params.Include.push_back(Arg->getValue()); + Opts.PreprocessArgs.push_back("-I"); + Opts.PreprocessArgs.push_back(Arg->getValue()); + break; + case WINDRES_define: + Opts.PreprocessArgs.push_back("-D"); + Opts.PreprocessArgs.push_back(Unescaped); + break; + case WINDRES_undef: + Opts.PreprocessArgs.push_back("-U"); + Opts.PreprocessArgs.push_back(Unescaped); + break; + case WINDRES_preprocessor_arg: + Opts.PreprocessArgs.push_back(Unescaped); + break; + } + } + if (InputArgs.hasArg(WINDRES_preprocessor)) + Opts.PreprocessCmd = + unescapeSplit(InputArgs.getLastArgValue(WINDRES_preprocessor)); + + Opts.Params.CodePage = CpWin1252; // Different default + if (InputArgs.hasArg(WINDRES_codepage)) { + if (InputArgs.getLastArgValue(WINDRES_codepage) + .getAsInteger(0, Opts.Params.CodePage)) + fatalError("Invalid code page: " + + InputArgs.getLastArgValue(WINDRES_codepage)); + } + if (InputArgs.hasArg(WINDRES_language)) { + StringRef Val = InputArgs.getLastArgValue(WINDRES_language); + Val.consume_front_insensitive("0x"); + if (Val.getAsInteger(16, Opts.LangId)) + fatalError("Invalid language id: " + + InputArgs.getLastArgValue(WINDRES_language)); + } + + Opts.BeVerbose = InputArgs.hasArg(WINDRES_verbose); + + return Opts; +} + +RcOptions parseRcOptions(ArrayRef<const char *> ArgsArr, + ArrayRef<const char *> InputArgsArray) { + RcOptTable T; + RcOptions Opts; + unsigned MAI, MAC; opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); // The tool prints nothing when invoked with no command-line arguments. if (InputArgs.hasArg(OPT_help)) { - T.PrintHelp(outs(), "rc [options] file...", "Resource Converter", false); - return 0; + T.printHelp(outs(), "rc [options] file...", "Resource Converter", false); + exit(0); } - const bool BeVerbose = InputArgs.hasArg(OPT_verbose); - std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT); - if (DashDash != Argv + Argc) - InArgsInfo.insert(InArgsInfo.end(), DashDash + 1, Argv + Argc); + InArgsInfo.insert(InArgsInfo.end(), InputArgsArray.begin(), + InputArgsArray.end()); if (InArgsInfo.size() != 1) { fatalError("Exactly one input file should be provided."); } + Opts.PrintCmdAndExit = InputArgs.hasArg(OPT__HASH_HASH_HASH); + Opts.Triple = getClangClTriple(); + for (const auto *Arg : + InputArgs.filtered(OPT_includepath, OPT_define, OPT_undef)) { + switch (Arg->getOption().getID()) { + case OPT_includepath: + Opts.PreprocessArgs.push_back("-I"); + break; + case OPT_define: + Opts.PreprocessArgs.push_back("-D"); + break; + case OPT_undef: + Opts.PreprocessArgs.push_back("-U"); + break; + } + Opts.PreprocessArgs.push_back(Arg->getValue()); + } + + Opts.InputFile = InArgsInfo[0]; + Opts.BeVerbose = InputArgs.hasArg(OPT_verbose); + Opts.Preprocess = !InputArgs.hasArg(OPT_no_preprocess); + Opts.Params.Include = InputArgs.getAllArgValues(OPT_includepath); + Opts.Params.NoInclude = InputArgs.hasArg(OPT_noinclude); + if (Opts.Params.NoInclude) { + // Clear the INLCUDE variable for the external preprocessor +#ifdef _WIN32 + ::_putenv("INCLUDE="); +#else + ::unsetenv("INCLUDE"); +#endif + } + if (InputArgs.hasArg(OPT_codepage)) { + if (InputArgs.getLastArgValue(OPT_codepage) + .getAsInteger(10, Opts.Params.CodePage)) + fatalError("Invalid code page: " + + InputArgs.getLastArgValue(OPT_codepage)); + } + Opts.IsDryRun = InputArgs.hasArg(OPT_dry_run); + auto OutArgsInfo = InputArgs.getAllArgValues(OPT_fileout); + if (OutArgsInfo.empty()) { + SmallString<128> OutputFile(Opts.InputFile); + llvm::sys::fs::make_absolute(OutputFile); + llvm::sys::path::replace_extension(OutputFile, "res"); + OutArgsInfo.push_back(std::string(OutputFile.str())); + } + if (!Opts.IsDryRun) { + if (OutArgsInfo.size() != 1) + fatalError( + "No more than one output file should be provided (using /FO flag)."); + Opts.OutputFile = OutArgsInfo[0]; + } + Opts.AppendNull = InputArgs.hasArg(OPT_add_null); + if (InputArgs.hasArg(OPT_lang_id)) { + StringRef Val = InputArgs.getLastArgValue(OPT_lang_id); + Val.consume_front_insensitive("0x"); + if (Val.getAsInteger(16, Opts.LangId)) + fatalError("Invalid language id: " + + InputArgs.getLastArgValue(OPT_lang_id)); + } + return Opts; +} + +RcOptions getOptions(const char *Argv0, ArrayRef<const char *> ArgsArr, + ArrayRef<const char *> InputArgs) { + std::string Prefix; + bool IsWindres; + std::tie(IsWindres, Prefix) = isWindres(Argv0); + if (IsWindres) + return parseWindresOptions(ArgsArr, InputArgs, Prefix); + else + return parseRcOptions(ArgsArr, InputArgs); +} + +void doRc(std::string Src, std::string Dest, RcOptions &Opts, + const char *Argv0) { + std::string PreprocessedFile = Src; + if (Opts.Preprocess) { + std::string OutFile = createTempFile("preproc", "rc"); + TempPreprocFile.setFile(OutFile); + if (preprocess(Src, OutFile, Opts, Argv0)) + PreprocessedFile = OutFile; + } + // Read and tokenize the input file. ErrorOr<std::unique_ptr<MemoryBuffer>> File = - MemoryBuffer::getFile(InArgsInfo[0]); + MemoryBuffer::getFile(PreprocessedFile); if (!File) { - fatalError("Error opening file '" + Twine(InArgsInfo[0]) + + fatalError("Error opening file '" + Twine(PreprocessedFile) + "': " + File.getError().message()); } @@ -120,7 +610,7 @@ std::string FilteredContents = filterCppOutput(Contents); std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents)); - if (BeVerbose) { + if (Opts.BeVerbose) { const Twine TokenNames[] = { #define TOKEN(Name) #Name, #define SHORT_TOKEN(Name, Ch) #Name, @@ -137,80 +627,129 @@ } } - WriterParams Params; - SmallString<128> InputFile(InArgsInfo[0]); + WriterParams &Params = Opts.Params; + SmallString<128> InputFile(Src); llvm::sys::fs::make_absolute(InputFile); Params.InputFilePath = InputFile; - Params.Include = InputArgs.getAllArgValues(OPT_includepath); - Params.NoInclude = InputArgs.getAllArgValues(OPT_noinclude); - if (InputArgs.hasArg(OPT_codepage)) { - if (InputArgs.getLastArgValue(OPT_codepage) - .getAsInteger(10, Params.CodePage)) - fatalError("Invalid code page: " + - InputArgs.getLastArgValue(OPT_codepage)); - switch (Params.CodePage) { - case CpAcp: - case CpWin1252: - case CpUtf8: - break; - default: - fatalError( - "Unsupported code page, only 0, 1252 and 65001 are supported!"); - } + switch (Params.CodePage) { + case CpAcp: + case CpWin1252: + case CpUtf8: + break; + default: + fatalError("Unsupported code page, only 0, 1252 and 65001 are supported!"); } std::unique_ptr<ResourceFileWriter> Visitor; - bool IsDryRun = InputArgs.hasArg(OPT_dry_run); - if (!IsDryRun) { - auto OutArgsInfo = InputArgs.getAllArgValues(OPT_fileout); - if (OutArgsInfo.empty()) { - SmallString<128> OutputFile = InputFile; - llvm::sys::path::replace_extension(OutputFile, "res"); - OutArgsInfo.push_back(std::string(OutputFile.str())); - } - - if (OutArgsInfo.size() != 1) - fatalError( - "No more than one output file should be provided (using /FO flag)."); - + if (!Opts.IsDryRun) { std::error_code EC; auto FOut = std::make_unique<raw_fd_ostream>( - OutArgsInfo[0], EC, sys::fs::FA_Read | sys::fs::FA_Write); + Dest, EC, sys::fs::FA_Read | sys::fs::FA_Write); if (EC) - fatalError("Error opening output file '" + OutArgsInfo[0] + - "': " + EC.message()); + fatalError("Error opening output file '" + Dest + "': " + EC.message()); Visitor = std::make_unique<ResourceFileWriter>(Params, std::move(FOut)); - Visitor->AppendNull = InputArgs.hasArg(OPT_add_null); + Visitor->AppendNull = Opts.AppendNull; ExitOnErr(NullResource().visit(Visitor.get())); - // Set the default language; choose en-US arbitrarily. - unsigned PrimaryLangId = 0x09, SubLangId = 0x01; - if (InputArgs.hasArg(OPT_lang_id)) { - unsigned LangId; - if (InputArgs.getLastArgValue(OPT_lang_id).getAsInteger(16, LangId)) - fatalError("Invalid language id: " + - InputArgs.getLastArgValue(OPT_lang_id)); - PrimaryLangId = LangId & 0x3ff; - SubLangId = LangId >> 10; - } + unsigned PrimaryLangId = Opts.LangId & 0x3ff; + unsigned SubLangId = Opts.LangId >> 10; ExitOnErr(LanguageResource(PrimaryLangId, SubLangId).visit(Visitor.get())); } rc::RCParser Parser{std::move(Tokens)}; while (!Parser.isEof()) { auto Resource = ExitOnErr(Parser.parseSingleResource()); - if (BeVerbose) + if (Opts.BeVerbose) Resource->log(outs()); - if (!IsDryRun) + if (!Opts.IsDryRun) ExitOnErr(Resource->visit(Visitor.get())); } // STRINGTABLE resources come at the very end. - if (!IsDryRun) + if (!Opts.IsDryRun) ExitOnErr(Visitor->dumpAllStringTables()); +} + +void doCvtres(std::string Src, std::string Dest, std::string TargetTriple) { + object::WindowsResourceParser Parser; + + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = + MemoryBuffer::getFile(Src); + if (!BufferOrErr) + fatalError("Error opening file '" + Twine(Src) + + "': " + BufferOrErr.getError().message()); + std::unique_ptr<MemoryBuffer> &Buffer = BufferOrErr.get(); + std::unique_ptr<object::WindowsResource> Binary = + ExitOnErr(object::WindowsResource::createWindowsResource( + Buffer->getMemBufferRef())); + + std::vector<std::string> Duplicates; + ExitOnErr(Parser.parse(Binary.get(), Duplicates)); + for (const auto &DupeDiag : Duplicates) + fatalError("Duplicate resources: " + DupeDiag); + + Triple T(TargetTriple); + COFF::MachineTypes MachineType; + switch (T.getArch()) { + case Triple::x86: + MachineType = COFF::IMAGE_FILE_MACHINE_I386; + break; + case Triple::x86_64: + MachineType = COFF::IMAGE_FILE_MACHINE_AMD64; + break; + case Triple::arm: + case Triple::thumb: + MachineType = COFF::IMAGE_FILE_MACHINE_ARMNT; + break; + case Triple::aarch64: + MachineType = COFF::IMAGE_FILE_MACHINE_ARM64; + break; + default: + fatalError("Unsupported architecture in target '" + Twine(TargetTriple) + + "'"); + } + + std::unique_ptr<MemoryBuffer> OutputBuffer = + ExitOnErr(object::writeWindowsResourceCOFF(MachineType, Parser, + /*DateTimeStamp*/ 0)); + std::unique_ptr<FileOutputBuffer> FileBuffer = + ExitOnErr(FileOutputBuffer::create(Dest, OutputBuffer->getBufferSize())); + std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(), + FileBuffer->getBufferStart()); + ExitOnErr(FileBuffer->commit()); +} + +} // anonymous namespace + +int main(int Argc, const char **Argv) { + InitLLVM X(Argc, Argv); + ExitOnErr.setBanner("llvm-rc: "); + + const char **DashDash = std::find_if( + Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; }); + ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash); + ArrayRef<const char *> FileArgsArr; + if (DashDash != Argv + Argc) + FileArgsArr = makeArrayRef(DashDash + 1, Argv + Argc); + + RcOptions Opts = getOptions(Argv[0], ArgsArr, FileArgsArr); + + std::string ResFile = Opts.OutputFile; + if (Opts.InputFormat == Rc) { + if (Opts.OutputFormat == Coff) { + ResFile = createTempFile("rc", "res"); + TempResFile.setFile(ResFile); + } + doRc(Opts.InputFile, ResFile, Opts, Argv[0]); + } else { + ResFile = Opts.InputFile; + } + if (Opts.OutputFormat == Coff) { + doCvtres(ResFile, Opts.OutputFile, Opts.Triple); + } return 0; }
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/src/llvm-project/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp index 5995a09..99ee639 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/src/llvm-project/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -100,7 +100,7 @@ OS << Name << " "; if (Offset) - OS << format("+0x%X (0x%" PRIX64 ")", Offset, Address); + OS << format("+0x%" PRIX64 " (0x%" PRIX64 ")", Offset, Address); else if (!Name.empty()) OS << format("(0x%" PRIX64 ")", Address); else @@ -184,31 +184,16 @@ const uint16_t VFPMask = std::get<1>(RegisterMask); OS << '{'; - bool Comma = false; - for (unsigned RI = 0, RE = 11; RI < RE; ++RI) { - if (GPRMask & (1 << RI)) { - if (Comma) - OS << ", "; - OS << GPRRegisterNames[RI]; - Comma = true; - } - } - for (unsigned RI = 0, RE = 32; RI < RE; ++RI) { - if (VFPMask & (1 << RI)) { - if (Comma) - OS << ", "; - OS << "d" << unsigned(RI); - Comma = true; - } - } - for (unsigned RI = 11, RE = 16; RI < RE; ++RI) { - if (GPRMask & (1 << RI)) { - if (Comma) - OS << ", "; - OS << GPRRegisterNames[RI]; - Comma = true; - } - } + ListSeparator LS; + for (unsigned RI = 0, RE = 11; RI < RE; ++RI) + if (GPRMask & (1 << RI)) + OS << LS << GPRRegisterNames[RI]; + for (unsigned RI = 0, RE = 32; RI < RE; ++RI) + if (VFPMask & (1 << RI)) + OS << LS << "d" << unsigned(RI); + for (unsigned RI = 11, RE = 16; RI < RE; ++RI) + if (GPRMask & (1 << RI)) + OS << LS << GPRRegisterNames[RI]; OS << '}'; } @@ -253,6 +238,65 @@ return inconvertibleErrorCode(); } +SymbolRef Decoder::getPreferredSymbol(const COFFObjectFile &COFF, + SymbolRef Sym) { + // The symbol resolved by getRelocatedSymbol can be any internal + // nondescriptive symbol; try to resolve a more descriptive one. + COFFSymbolRef CoffSym = COFF.getCOFFSymbol(Sym); + if (CoffSym.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) + return Sym; + for (const auto &S : COFF.symbols()) { + COFFSymbolRef CS = COFF.getCOFFSymbol(S); + if (CS.getSectionNumber() == CoffSym.getSectionNumber() && + CS.getValue() == CoffSym.getValue()) { + if (CS.isExternal()) + return S; + if (CS.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) { + Sym = S; + CoffSym = CS; + } + } + } + return Sym; +} + +ErrorOr<SymbolRef> Decoder::getSymbolForLocation( + const COFFObjectFile &COFF, const SectionRef &Section, + uint64_t OffsetInSection, uint64_t ImmediateOffset, uint64_t &SymbolAddress, + uint64_t &SymbolOffset, bool FunctionOnly) { + // Try to locate a relocation that points at the offset in the section + ErrorOr<SymbolRef> SymOrErr = + getRelocatedSymbol(COFF, Section, OffsetInSection); + if (SymOrErr) { + // We found a relocation symbol; the immediate offset needs to be added + // to the symbol address. + SymbolOffset = ImmediateOffset; + + Expected<uint64_t> AddressOrErr = SymOrErr->getAddress(); + if (!AddressOrErr) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + logAllUnhandledErrors(AddressOrErr.takeError(), OS); + OS.flush(); + report_fatal_error(Buf); + } + // We apply SymbolOffset here directly. We return it separately to allow + // the caller to print it as an offset on the symbol name. + SymbolAddress = *AddressOrErr + SymbolOffset; + } else { + // No matching relocation found; operating on a linked image. Try to + // find a descriptive symbol if possible. The immediate offset contains + // the image relative address, and we shouldn't add any offset to the + // symbol. + SymbolAddress = COFF.getImageBase() + ImmediateOffset; + SymbolOffset = 0; + SymOrErr = getSymbol(COFF, SymbolAddress, FunctionOnly); + } + if (SymOrErr && FunctionOnly) // Resolve label symbols into function names + SymOrErr = getPreferredSymbol(COFF, *SymOrErr); + return SymOrErr; +} + bool Decoder::opcode_0xxxxxxx(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { uint8_t Imm = OC[Offset] & 0x7f; @@ -934,16 +978,16 @@ } if (XData.X()) { - const uint64_t Address = COFF.getImageBase() + XData.ExceptionHandlerRVA(); const uint32_t Parameter = XData.ExceptionHandlerParameter(); - const size_t HandlerOffset = HeaderWords(XData) - + (XData.E() ? 0 : XData.EpilogueCount()) - + XData.CodeWords(); + const size_t HandlerOffset = HeaderWords(XData) + + (XData.E() ? 0 : XData.EpilogueCount()) + + XData.CodeWords(); - ErrorOr<SymbolRef> Symbol = getRelocatedSymbol( - COFF, Section, Offset + HandlerOffset * sizeof(uint32_t)); - if (!Symbol) - Symbol = getSymbol(COFF, Address, /*FunctionOnly=*/true); + uint64_t Address, SymbolOffset; + ErrorOr<SymbolRef> Symbol = getSymbolForLocation( + COFF, Section, Offset + HandlerOffset * sizeof(uint32_t), + XData.ExceptionHandlerRVA(), Address, SymbolOffset, + /*FunctionOnly=*/true); if (!Symbol) { ListScope EHS(SW, "ExceptionHandler"); SW.printHex("Routine", Address); @@ -961,7 +1005,7 @@ } ListScope EHS(SW, "ExceptionHandler"); - SW.printString("Routine", formatSymbol(*Name, Address)); + SW.printString("Routine", formatSymbol(*Name, Address, SymbolOffset)); SW.printHex("Parameter", Parameter); } @@ -974,14 +1018,15 @@ assert(RF.Flag() == RuntimeFunctionFlag::RFF_Unpacked && "packed entry cannot be treated as an unpacked entry"); - ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset); - if (!Function) - Function = getSymbol(COFF, COFF.getImageBase() + RF.BeginAddress, - /*FunctionOnly=*/true); + uint64_t FunctionAddress, FunctionOffset; + ErrorOr<SymbolRef> Function = getSymbolForLocation( + COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset, + /*FunctionOnly=*/true); - ErrorOr<SymbolRef> XDataRecord = getRelocatedSymbol(COFF, Section, Offset + 4); - if (!XDataRecord) - XDataRecord = getSymbol(COFF, RF.ExceptionInformationRVA()); + uint64_t XDataAddress, XDataOffset; + ErrorOr<SymbolRef> XDataRecord = getSymbolForLocation( + COFF, Section, Offset + 4, RF.ExceptionInformationRVA(), XDataAddress, + XDataOffset); if (!RF.BeginAddress && !Function) return false; @@ -989,7 +1034,6 @@ return false; StringRef FunctionName; - uint64_t FunctionAddress; if (Function) { Expected<StringRef> FunctionNameOrErr = Function->getName(); if (!FunctionNameOrErr) { @@ -1000,20 +1044,10 @@ report_fatal_error(Buf); } FunctionName = *FunctionNameOrErr; - Expected<uint64_t> FunctionAddressOrErr = Function->getAddress(); - if (!FunctionAddressOrErr) { - std::string Buf; - llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); - OS.flush(); - report_fatal_error(Buf); - } - FunctionAddress = *FunctionAddressOrErr; - } else { - FunctionAddress = COFF.getImageBase() + RF.BeginAddress; } - SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); + SW.printString("Function", + formatSymbol(FunctionName, FunctionAddress, FunctionOffset)); if (XDataRecord) { Expected<StringRef> Name = XDataRecord->getName(); @@ -1025,17 +1059,8 @@ report_fatal_error(Buf); } - Expected<uint64_t> AddressOrErr = XDataRecord->getAddress(); - if (!AddressOrErr) { - std::string Buf; - llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(AddressOrErr.takeError(), OS); - OS.flush(); - report_fatal_error(Buf); - } - uint64_t Address = *AddressOrErr; - - SW.printString("ExceptionRecord", formatSymbol(*Name, Address)); + SW.printString("ExceptionRecord", + formatSymbol(*Name, XDataAddress, XDataOffset)); Expected<section_iterator> SIOrErr = XDataRecord->getSection(); if (!SIOrErr) { @@ -1045,18 +1070,15 @@ } section_iterator SI = *SIOrErr; - // FIXME: Do we need to add an offset from the relocation? - return dumpXDataRecord(COFF, *SI, FunctionAddress, - RF.ExceptionInformationRVA()); + return dumpXDataRecord(COFF, *SI, FunctionAddress, XDataAddress); } else { - uint64_t Address = COFF.getImageBase() + RF.ExceptionInformationRVA(); - SW.printString("ExceptionRecord", formatSymbol("", Address)); + SW.printString("ExceptionRecord", formatSymbol("", XDataAddress)); - ErrorOr<SectionRef> Section = getSectionContaining(COFF, Address); + ErrorOr<SectionRef> Section = getSectionContaining(COFF, XDataAddress); if (!Section) return false; - return dumpXDataRecord(COFF, *Section, FunctionAddress, Address); + return dumpXDataRecord(COFF, *Section, FunctionAddress, XDataAddress); } } @@ -1067,12 +1089,12 @@ RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && "unpacked entry cannot be treated as a packed entry"); - ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset); - if (!Function) - Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true); + uint64_t FunctionAddress, FunctionOffset; + ErrorOr<SymbolRef> Function = getSymbolForLocation( + COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset, + /*FunctionOnly=*/true); StringRef FunctionName; - uint64_t FunctionAddress; if (Function) { Expected<StringRef> FunctionNameOrErr = Function->getName(); if (!FunctionNameOrErr) { @@ -1083,20 +1105,10 @@ report_fatal_error(Buf); } FunctionName = *FunctionNameOrErr; - Expected<uint64_t> FunctionAddressOrErr = Function->getAddress(); - if (!FunctionAddressOrErr) { - std::string Buf; - llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); - OS.flush(); - report_fatal_error(Buf); - } - FunctionAddress = *FunctionAddressOrErr; - } else { - FunctionAddress = COFF.getPE32Header()->ImageBase + RF.BeginAddress; } - SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); + SW.printString("Function", + formatSymbol(FunctionName, FunctionAddress, FunctionOffset)); if (!isAArch64) SW.printBoolean("Fragment", RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); @@ -1119,12 +1131,12 @@ RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && "unpacked entry cannot be treated as a packed entry"); - ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset); - if (!Function) - Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true); + uint64_t FunctionAddress, FunctionOffset; + ErrorOr<SymbolRef> Function = getSymbolForLocation( + COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset, + /*FunctionOnly=*/true); StringRef FunctionName; - uint64_t FunctionAddress; if (Function) { Expected<StringRef> FunctionNameOrErr = Function->getName(); if (!FunctionNameOrErr) { @@ -1135,20 +1147,10 @@ report_fatal_error(Buf); } FunctionName = *FunctionNameOrErr; - Expected<uint64_t> FunctionAddressOrErr = Function->getAddress(); - if (!FunctionAddressOrErr) { - std::string Buf; - llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); - OS.flush(); - report_fatal_error(Buf); - } - FunctionAddress = *FunctionAddressOrErr; - } else { - FunctionAddress = COFF.getPE32PlusHeader()->ImageBase + RF.BeginAddress; } - SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); + SW.printString("Function", + formatSymbol(FunctionName, FunctionAddress, FunctionOffset)); SW.printBoolean("Fragment", RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); SW.printNumber("FunctionLength", RF.FunctionLength());
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/ARMWinEHPrinter.h b/src/llvm-project/llvm/tools/llvm-readobj/ARMWinEHPrinter.h index 3263841..efe1685 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/ARMWinEHPrinter.h +++ b/src/llvm-project/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
@@ -146,6 +146,16 @@ getRelocatedSymbol(const object::COFFObjectFile &COFF, const object::SectionRef &Section, uint64_t Offset); + ErrorOr<object::SymbolRef> + getSymbolForLocation(const object::COFFObjectFile &COFF, + const object::SectionRef &Section, + uint64_t OffsetInSection, uint64_t ImmediateOffset, + uint64_t &SymbolAddress, uint64_t &SymbolOffset, + bool FunctionOnly = false); + + object::SymbolRef getPreferredSymbol(const object::COFFObjectFile &COFF, + object::SymbolRef Sym); + bool dumpXDataRecord(const object::COFFObjectFile &COFF, const object::SectionRef &Section, uint64_t FunctionAddress, uint64_t VA);
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-readobj/CMakeLists.txt index 9e310f0..9d2d888 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-readobj/CMakeLists.txt
@@ -4,12 +4,17 @@ Demangle Object BinaryFormat + Option Support DebugInfoCodeView DebugInfoMSF DebugInfoPDB ) +set(LLVM_TARGET_DEFINITIONS Opts.td) +tablegen(LLVM Opts.inc -gen-opt-parser-defs) +add_public_tablegen_target(ReadobjOptsTableGen) + add_llvm_tool(llvm-readobj ARMWinEHPrinter.cpp COFFDumper.cpp
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/COFFDumper.cpp b/src/llvm-project/llvm/tools/llvm-readobj/COFFDumper.cpp index 684967f..96124cc 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/src/llvm-project/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -71,6 +71,8 @@ uint64_t GuardIatTableCount = 0; uint64_t GuardLJmpTableVA = 0; uint64_t GuardLJmpTableCount = 0; + uint64_t GuardEHContTableVA = 0; + uint64_t GuardEHContTableCount = 0; }; class COFFDumper : public ObjDumper { @@ -593,8 +595,7 @@ for (const SectionRef &S : Obj->sections()) { const coff_section *Section = Obj->getCOFFSection(S); - for (const RelocationRef &Reloc : S.relocations()) - RelocMap[Section].push_back(Reloc); + append_range(RelocMap[Section], S.relocations()); // Sort relocations by address. llvm::sort(RelocMap[Section], [](RelocationRef L, RelocationRef R) { @@ -794,19 +795,19 @@ printRVATable(Tables.SEHTableVA, Tables.SEHTableCount, 4); } + auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) { + uint8_t Flags = *reinterpret_cast<const uint8_t *>(Entry + 4); + if (Flags) + OS << " flags " << utohexstr(Flags); + }; + if (Tables.GuardFidTableVA) { ListScope LS(W, "GuardFidTable"); - if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags)) { - auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) { - uint8_t Flags = *reinterpret_cast<const uint8_t *>(Entry + 4); - if (Flags) - OS << " flags " << utohexstr(Flags); - }; + if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags)) printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 5, PrintGuardFlags); - } else { + else printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 4); - } } if (Tables.GuardIatTableVA) { @@ -818,6 +819,12 @@ ListScope LS(W, "GuardLJmpTable"); printRVATable(Tables.GuardLJmpTableVA, Tables.GuardLJmpTableCount, 4); } + + if (Tables.GuardEHContTableVA) { + ListScope LS(W, "GuardEHContTable"); + printRVATable(Tables.GuardEHContTableVA, Tables.GuardEHContTableCount, 5, + PrintGuardFlags); + } } template <typename T> @@ -876,8 +883,8 @@ Tables.GuardFidTableCount = Conf->GuardCFFunctionCount; Tables.GuardFlags = Conf->GuardFlags; - // Print the rest. (2017) - if (Conf->Size < sizeof(T)) + // Print everything before Reserved3. (2017) + if (Conf->Size < offsetof(T, Reserved3)) return; W.printHex("GuardAddressTakenIatEntryTable", Conf->GuardAddressTakenIatEntryTable); @@ -903,6 +910,17 @@ Tables.GuardLJmpTableVA = Conf->GuardLongJumpTargetTable; Tables.GuardLJmpTableCount = Conf->GuardLongJumpTargetCount; + + // Print the rest. (2019) + if (Conf->Size < sizeof(T)) + return; + W.printHex("EnclaveConfigurationPointer", Conf->EnclaveConfigurationPointer); + W.printHex("VolatileMetadataPointer", Conf->VolatileMetadataPointer); + W.printHex("GuardEHContinuationTable", Conf->GuardEHContinuationTable); + W.printNumber("GuardEHContinuationCount", Conf->GuardEHContinuationCount); + + Tables.GuardEHContTableVA = Conf->GuardEHContinuationTable; + Tables.GuardEHContTableCount = Conf->GuardEHContinuationCount; } void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) { @@ -1856,7 +1874,7 @@ OS << ": (ID " << Entry.Identifier.ID << ")"; } } - Name = StringRef(IDStr); + Name = IDStr; ListScope ResourceType(W, Level.str() + Name.str()); if (Entry.Offset.isSubDir()) { W.printHex("Table Offset", Entry.Offset.value());
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/ELFDumper.cpp b/src/llvm-project/llvm/tools/llvm-readobj/ELFDumper.cpp index 0f508f8..f221acb 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/src/llvm-project/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -298,6 +298,13 @@ std::vector<GroupSection> getGroups(); + // Returns the function symbol index for the given address. Matches the + // symbol's section with FunctionSec when specified. + // Returns None if no function symbol can be found for the address or in case + // it is not defined in the specified section. + SmallVector<uint32_t> + getSymbolIndexesForFunctionAddress(uint64_t SymValue, + Optional<const Elf_Shdr *> FunctionSec); bool printFunctionStackSize(uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec, const Elf_Shdr &StackSizeSec, DataExtractor Data, @@ -306,11 +313,18 @@ unsigned Ndx, const Elf_Shdr *SymTab, const Elf_Shdr *FunctionSec, const Elf_Shdr &StackSizeSec, const RelocationResolver &Resolver, DataExtractor Data); - virtual void printStackSizeEntry(uint64_t Size, StringRef FuncName) = 0; + virtual void printStackSizeEntry(uint64_t Size, + ArrayRef<std::string> FuncNames) = 0; void printRelocatableStackSizes(std::function<void()> PrintHeader); void printNonRelocatableStackSizes(std::function<void()> PrintHeader); + /// Retrieves sections with corresponding relocation sections based on + /// IsMatch. + void getSectionAndRelocations( + std::function<bool(const Elf_Shdr &)> IsMatch, + llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> &SecToRelocMap); + const object::ELFObjectFile<ELFT> &ObjF; const ELFFile<ELFT> &Obj; StringRef FileName; @@ -349,10 +363,10 @@ const Elf_GnuHash *GnuHashTable = nullptr; const Elf_Shdr *DotSymtabSec = nullptr; const Elf_Shdr *DotDynsymSec = nullptr; - const Elf_Shdr *DotCGProfileSec = nullptr; const Elf_Shdr *DotAddrsigSec = nullptr; DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables; Optional<uint64_t> SONameOffset; + Optional<DenseMap<uint64_t, std::vector<uint32_t>>> AddressToIndexMap; const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r @@ -550,6 +564,7 @@ void printVersionDependencySection(const Elf_Shdr *Sec) override; void printHashHistograms() override; void printCGProfile() override; + void printBBAddrMaps() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -631,7 +646,8 @@ void printGNUVersionSectionProlog(const typename ELFT::Shdr &Sec, const Twine &Label, unsigned EntriesNum); - void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; + void printStackSizeEntry(uint64_t Size, + ArrayRef<std::string> FuncNames) override; void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override; @@ -660,6 +676,7 @@ void printVersionDependencySection(const Elf_Shdr *Sec) override; void printHashHistograms() override; void printCGProfile() override; + void printBBAddrMaps() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -678,7 +695,8 @@ bool /*NonVisibilityBitsUsed*/) const override; void printProgramHeaders() override; void printSectionMapping() override {} - void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; + void printStackSizeEntry(uint64_t Size, + ArrayRef<std::string> FuncNames) override; void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override; @@ -1138,7 +1156,7 @@ ENUM_ENT(EM_METAG, "Imagination Technologies Meta processor architecture"), ENUM_ENT(EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture"), ENUM_ENT(EM_ECOG16, "Cyan Technology eCOG16 family"), - ENUM_ENT(EM_CR16, "Xilinx MicroBlaze"), + ENUM_ENT(EM_CR16, "National Semiconductor CompactRISC 16-bit processor"), ENUM_ENT(EM_ETPU, "Freescale Extended Time Processing Unit"), ENUM_ENT(EM_SLE9X, "Infineon Technologies SLE9X core"), ENUM_ENT(EM_L10M, "EM_L10M"), @@ -1148,6 +1166,7 @@ ENUM_ENT(EM_STM8, "STMicroeletronics STM8 8-bit microcontroller"), ENUM_ENT(EM_TILE64, "Tilera TILE64 multicore architecture family"), ENUM_ENT(EM_TILEPRO, "Tilera TILEPro multicore architecture family"), + ENUM_ENT(EM_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core"), ENUM_ENT(EM_CUDA, "NVIDIA CUDA architecture"), ENUM_ENT(EM_TILEGX, "Tilera TILE-Gx multicore architecture family"), ENUM_ENT(EM_CLOUDSHIELD, "EM_CLOUDSHIELD"), @@ -1201,6 +1220,7 @@ ENUM_ENT(SHF_GROUP, "G"), ENUM_ENT(SHF_TLS, "T"), ENUM_ENT(SHF_COMPRESSED, "C"), + ENUM_ENT(SHF_GNU_RETAIN, "R"), ENUM_ENT(SHF_EXCLUDE, "E"), }; @@ -1427,7 +1447,7 @@ ENUM_ENT(EF_MIPS_ARCH_64R6, "mips64r6") }; -static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = { +static const EnumEntry<unsigned> ElfHeaderAMDGPUFlagsABIVersion3[] = { LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630), @@ -1465,16 +1485,78 @@ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX906), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX908), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90A), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90C), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1013), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1030), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1031), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1032), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1033), - LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK), - LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_SRAM_ECC) + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1034), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1035), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_V3), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_V3) +}; + +static const EnumEntry<unsigned> ElfHeaderAMDGPUFlagsABIVersion4[] = { + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RS880), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV670), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV710), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV730), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV770), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CEDAR), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CYPRESS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_JUNIPER), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_REDWOOD), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_SUMO), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_BARTS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAICOS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAYMAN), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_TURKS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX600), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX601), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX602), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX700), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX701), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX702), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX703), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX704), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX705), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX801), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX802), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX803), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX805), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX810), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX900), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX902), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX904), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX906), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX908), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90A), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90C), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1013), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1030), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1031), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1032), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1033), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1034), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1035), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_ANY_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_OFF_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_ON_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_ANY_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_OFF_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_ON_V4) }; static const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = { @@ -1485,6 +1567,29 @@ ENUM_ENT(EF_RISCV_RVE, "RVE") }; +static const EnumEntry<unsigned> ElfHeaderAVRFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR1), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR25), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR3), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR31), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR35), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR5), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR51), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR6), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVRTINY), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA1), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA3), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA5), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA6), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA7), + ENUM_ENT(EF_AVR_LINKRELAX_PREPARED, "relaxable"), +}; + + static const EnumEntry<unsigned> ElfSymOtherFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, STV_INTERNAL), LLVM_READOBJ_ENUM_ENT(ELF, STV_HIDDEN), @@ -1743,10 +1848,6 @@ if (!SymbolVersionNeedSection) SymbolVersionNeedSection = &Sec; break; - case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: - if (!DotCGProfileSec) - DotCGProfileSec = &Sec; - break; case ELF::SHT_LLVM_ADDRSIG: if (!DotAddrsigSec) DotAddrsigSec = &Sec; @@ -3170,6 +3271,9 @@ unsigned(ELF::EF_MIPS_MACH)); else if (e.e_machine == EM_RISCV) ElfFlags = printFlags(e.e_flags, makeArrayRef(ElfHeaderRISCVFlags)); + else if (e.e_machine == EM_AVR) + ElfFlags = printFlags(e.e_flags, makeArrayRef(ElfHeaderAVRFlags), + unsigned(ELF::EF_AVR_ARCH_MASK)); Str = "0x" + to_hexString(e.e_flags); if (!ElfFlags.empty()) Str = Str + ", " + ElfFlags; @@ -3483,15 +3587,14 @@ OS << " L (link order), O (extra OS processing required), G (group), T " "(TLS),\n"; OS << " C (compressed), x (unknown), o (OS specific), E (exclude),\n"; + OS << " R (retain)"; if (EMachine == EM_X86_64) - OS << " l (large), "; + OS << ", l (large)"; else if (EMachine == EM_ARM) - OS << " y (purecode), "; - else - OS << " "; + OS << ", y (purecode)"; - OS << "p (processor specific)\n"; + OS << ", p (processor specific)\n"; } template <class ELFT> void GNUELFDumper<ELFT>::printSectionHeaders() { @@ -3934,21 +4037,17 @@ uint64_t Flags = S.sh_flags; uint64_t UnknownFlags = 0; - bool NeedsComma = false; + ListSeparator LS; while (Flags) { // Take the least significant bit as a flag. uint64_t Flag = Flags & -Flags; Flags -= Flag; auto It = FlagToName.find(Flag); - if (It != FlagToName.end()) { - if (NeedsComma) - OS << ", "; - NeedsComma = true; - OS << It->second; - } else { + if (It != FlagToName.end()) + OS << LS << It->second; + else UnknownFlags |= Flag; - } } auto PrintUnknownFlags = [&](uint64_t Mask, StringRef Name) { @@ -3956,12 +4055,9 @@ if (!FlagsToPrint) return; - if (NeedsComma) - OS << ", "; - OS << Name << " (" + OS << LS << Name << " (" << to_string(format_hex_no_prefix(FlagsToPrint, AddrSize)) << ")"; UnknownFlags &= ~Mask; - NeedsComma = true; }; PrintUnknownFlags(SHF_MASKOS, "OS"); @@ -4623,6 +4719,10 @@ OS << "GNUStyle::printCGProfile not implemented\n"; } +template <class ELFT> void GNUELFDumper<ELFT>::printBBAddrMaps() { + OS << "GNUStyle::printBBAddrMaps not implemented\n"; +} + static Expected<std::vector<uint64_t>> toULEB128Array(ArrayRef<uint8_t> Data) { std::vector<uint64_t> Ret; const uint8_t *Cur = Data.begin(); @@ -4736,47 +4836,6 @@ if (PrData) OS << format("<unknown flags: 0x%x>", PrData); return OS.str(); - case GNU_PROPERTY_X86_ISA_1_NEEDED: - case GNU_PROPERTY_X86_ISA_1_USED: - OS << "x86 ISA " - << (Type == GNU_PROPERTY_X86_ISA_1_NEEDED ? "needed: " : "used: "); - if (DataSize != 4) { - OS << format("<corrupt length: 0x%x>", DataSize); - return OS.str(); - } - PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data()); - if (PrData == 0) { - OS << "<None>"; - return OS.str(); - } - DumpBit(GNU_PROPERTY_X86_ISA_1_CMOV, "CMOV"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE, "SSE"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE2, "SSE2"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE3, "SSE3"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSSE3, "SSSE3"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE4_1, "SSE4_1"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE4_2, "SSE4_2"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX, "AVX"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX2, "AVX2"); - DumpBit(GNU_PROPERTY_X86_ISA_1_FMA, "FMA"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512F, "AVX512F"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512CD, "AVX512CD"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512ER, "AVX512ER"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512PF, "AVX512PF"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512VL, "AVX512VL"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512DQ, "AVX512DQ"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512BW, "AVX512BW"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_4FMAPS, "AVX512_4FMAPS"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_4VNNIW, "AVX512_4VNNIW"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_BITALG, "AVX512_BITALG"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_IFMA, "AVX512_IFMA"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VBMI, "AVX512_VBMI"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VBMI2, "AVX512_VBMI2"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VNNI, "AVX512_VNNI"); - if (PrData) - OS << format("<unknown flags: 0x%x>", PrData); - return OS.str(); - break; case GNU_PROPERTY_X86_FEATURE_2_NEEDED: case GNU_PROPERTY_X86_FEATURE_2_USED: OS << "x86 feature " @@ -4803,6 +4862,26 @@ if (PrData) OS << format("<unknown flags: 0x%x>", PrData); return OS.str(); + case GNU_PROPERTY_X86_ISA_1_NEEDED: + case GNU_PROPERTY_X86_ISA_1_USED: + OS << "x86 ISA " + << (Type == GNU_PROPERTY_X86_ISA_1_NEEDED ? "needed: " : "used: "); + if (DataSize != 4) { + OS << format("<corrupt length: 0x%x>", DataSize); + return OS.str(); + } + PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data()); + if (PrData == 0) { + OS << "<None>"; + return OS.str(); + } + DumpBit(GNU_PROPERTY_X86_ISA_1_BASELINE, "x86-64-baseline"); + DumpBit(GNU_PROPERTY_X86_ISA_1_V2, "x86-64-v2"); + DumpBit(GNU_PROPERTY_X86_ISA_1_V3, "x86-64-v3"); + DumpBit(GNU_PROPERTY_X86_ISA_1_V4, "x86-64-v4"); + if (PrData) + OS << format("<unknown flags: 0x%x>", PrData); + return OS.str(); } } @@ -4877,11 +4956,12 @@ } template <typename ELFT> -static void printGNUNote(raw_ostream &OS, uint32_t NoteType, +static bool printGNUNote(raw_ostream &OS, uint32_t NoteType, ArrayRef<uint8_t> Desc) { + // Return true if we were able to pretty-print the note, false otherwise. switch (NoteType) { default: - return; + return false; case ELF::NT_GNU_ABI_TAG: { const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc); if (!AbiTag.IsValid) @@ -4904,6 +4984,54 @@ break; } OS << '\n'; + return true; +} + +static const EnumEntry<unsigned> FreeBSDFeatureCtlFlags[] = { + {"ASLR_DISABLE", NT_FREEBSD_FCTL_ASLR_DISABLE}, + {"PROTMAX_DISABLE", NT_FREEBSD_FCTL_PROTMAX_DISABLE}, + {"STKGAP_DISABLE", NT_FREEBSD_FCTL_STKGAP_DISABLE}, + {"WXNEEDED", NT_FREEBSD_FCTL_WXNEEDED}, + {"LA48", NT_FREEBSD_FCTL_LA48}, + {"ASG_DISABLE", NT_FREEBSD_FCTL_ASG_DISABLE}, +}; + +struct FreeBSDNote { + std::string Type; + std::string Value; +}; + +template <typename ELFT> +static Optional<FreeBSDNote> +getFreeBSDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc, bool IsCore) { + if (IsCore) + return None; // No pretty-printing yet. + switch (NoteType) { + case ELF::NT_FREEBSD_ABI_TAG: + if (Desc.size() != 4) + return None; + return FreeBSDNote{ + "ABI tag", + utostr(support::endian::read32<ELFT::TargetEndianness>(Desc.data()))}; + case ELF::NT_FREEBSD_ARCH_TAG: + return FreeBSDNote{"Arch tag", toStringRef(Desc).str()}; + case ELF::NT_FREEBSD_FEATURE_CTL: { + if (Desc.size() != 4) + return None; + unsigned Value = + support::endian::read32<ELFT::TargetEndianness>(Desc.data()); + std::string FlagsStr; + raw_string_ostream OS(FlagsStr); + printFlags(Value, makeArrayRef(FreeBSDFeatureCtlFlags), OS); + if (OS.str().empty()) + OS << "0x" << utohexstr(Value); + else + OS << "(0x" << utohexstr(Value) << ")"; + return FreeBSDNote{"Feature flags", OS.str()}; + } + default: + return None; + } } struct AMDNote { @@ -4916,15 +5044,98 @@ switch (NoteType) { default: return {"", ""}; - case ELF::NT_AMD_AMDGPU_HSA_METADATA: + case ELF::NT_AMD_HSA_CODE_OBJECT_VERSION: { + struct CodeObjectVersion { + uint32_t MajorVersion; + uint32_t MinorVersion; + }; + if (Desc.size() != sizeof(CodeObjectVersion)) + return {"AMD HSA Code Object Version", + "Invalid AMD HSA Code Object Version"}; + std::string VersionString; + raw_string_ostream StrOS(VersionString); + auto Version = reinterpret_cast<const CodeObjectVersion *>(Desc.data()); + StrOS << "[Major: " << Version->MajorVersion + << ", Minor: " << Version->MinorVersion << "]"; + return {"AMD HSA Code Object Version", VersionString}; + } + case ELF::NT_AMD_HSA_HSAIL: { + struct HSAILProperties { + uint32_t HSAILMajorVersion; + uint32_t HSAILMinorVersion; + uint8_t Profile; + uint8_t MachineModel; + uint8_t DefaultFloatRound; + }; + if (Desc.size() != sizeof(HSAILProperties)) + return {"AMD HSA HSAIL Properties", "Invalid AMD HSA HSAIL Properties"}; + auto Properties = reinterpret_cast<const HSAILProperties *>(Desc.data()); + std::string HSAILPropetiesString; + raw_string_ostream StrOS(HSAILPropetiesString); + StrOS << "[HSAIL Major: " << Properties->HSAILMajorVersion + << ", HSAIL Minor: " << Properties->HSAILMinorVersion + << ", Profile: " << uint32_t(Properties->Profile) + << ", Machine Model: " << uint32_t(Properties->MachineModel) + << ", Default Float Round: " + << uint32_t(Properties->DefaultFloatRound) << "]"; + return {"AMD HSA HSAIL Properties", HSAILPropetiesString}; + } + case ELF::NT_AMD_HSA_ISA_VERSION: { + struct IsaVersion { + uint16_t VendorNameSize; + uint16_t ArchitectureNameSize; + uint32_t Major; + uint32_t Minor; + uint32_t Stepping; + }; + if (Desc.size() < sizeof(IsaVersion)) + return {"AMD HSA ISA Version", "Invalid AMD HSA ISA Version"}; + auto Isa = reinterpret_cast<const IsaVersion *>(Desc.data()); + if (Desc.size() < sizeof(IsaVersion) + + Isa->VendorNameSize + Isa->ArchitectureNameSize || + Isa->VendorNameSize == 0 || Isa->ArchitectureNameSize == 0) + return {"AMD HSA ISA Version", "Invalid AMD HSA ISA Version"}; + std::string IsaString; + raw_string_ostream StrOS(IsaString); + StrOS << "[Vendor: " + << StringRef((const char*)Desc.data() + sizeof(IsaVersion), Isa->VendorNameSize - 1) + << ", Architecture: " + << StringRef((const char*)Desc.data() + sizeof(IsaVersion) + Isa->VendorNameSize, + Isa->ArchitectureNameSize - 1) + << ", Major: " << Isa->Major << ", Minor: " << Isa->Minor + << ", Stepping: " << Isa->Stepping << "]"; + return {"AMD HSA ISA Version", IsaString}; + } + case ELF::NT_AMD_HSA_METADATA: { + if (Desc.size() == 0) + return {"AMD HSA Metadata", ""}; return { - "HSA Metadata", - std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())}; - case ELF::NT_AMD_AMDGPU_ISA: + "AMD HSA Metadata", + std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size() - 1)}; + } + case ELF::NT_AMD_HSA_ISA_NAME: { + if (Desc.size() == 0) + return {"AMD HSA ISA Name", ""}; return { - "ISA Version", + "AMD HSA ISA Name", std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())}; } + case ELF::NT_AMD_PAL_METADATA: { + struct PALMetadata { + uint32_t Key; + uint32_t Value; + }; + if (Desc.size() % sizeof(PALMetadata) != 0) + return {"AMD PAL Metadata", "Invalid AMD PAL Metadata"}; + auto Isa = reinterpret_cast<const PALMetadata *>(Desc.data()); + std::string MetadataString; + raw_string_ostream StrOS(MetadataString); + for (size_t I = 0, E = Desc.size() / sizeof(PALMetadata); I < E; ++I) { + StrOS << "[" << Isa[I].Key << ": " << Isa[I].Value << "]"; + } + return {"AMD PAL Metadata", MetadataString}; + } + } } struct AMDGPUNote { @@ -4942,16 +5153,22 @@ StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size()); msgpack::Document MsgPackDoc; if (!MsgPackDoc.readFromBlob(MsgPackString, /*Multi=*/false)) - return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; + return {"", ""}; AMDGPU::HSAMD::V3::MetadataVerifier Verifier(true); - std::string HSAMetadataString; + std::string MetadataString; if (!Verifier.verify(MsgPackDoc.getRoot())) - HSAMetadataString = "Invalid AMDGPU Metadata\n"; + MetadataString = "Invalid AMDGPU Metadata\n"; - raw_string_ostream StrOS(HSAMetadataString); + raw_string_ostream StrOS(MetadataString); + if (MsgPackDoc.getRoot().isScalar()) { + // TODO: passing a scalar root to toYAML() asserts: + // (PolymorphicTraits<T>::getKind(Val) != NodeKind::Scalar && + // "plain scalar documents are not supported") + // To avoid this crash we print the raw data instead. + return {"", ""}; + } MsgPackDoc.toYAML(StrOS); - return {"AMDGPU Metadata", StrOS.str()}; } } @@ -5049,7 +5266,7 @@ {ELF::NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0 (property note)"}, }; -static const NoteType FreeBSDNoteTypes[] = { +static const NoteType FreeBSDCoreNoteTypes[] = { {ELF::NT_FREEBSD_THRMISC, "NT_THRMISC (thrmisc structure)"}, {ELF::NT_FREEBSD_PROCSTAT_PROC, "NT_PROCSTAT_PROC (proc data)"}, {ELF::NT_FREEBSD_PROCSTAT_FILES, "NT_PROCSTAT_FILES (files data)"}, @@ -5063,12 +5280,22 @@ {ELF::NT_FREEBSD_PROCSTAT_AUXV, "NT_PROCSTAT_AUXV (auxv data)"}, }; +static const NoteType FreeBSDNoteTypes[] = { + {ELF::NT_FREEBSD_ABI_TAG, "NT_FREEBSD_ABI_TAG (ABI version tag)"}, + {ELF::NT_FREEBSD_NOINIT_TAG, "NT_FREEBSD_NOINIT_TAG (no .init tag)"}, + {ELF::NT_FREEBSD_ARCH_TAG, "NT_FREEBSD_ARCH_TAG (architecture tag)"}, + {ELF::NT_FREEBSD_FEATURE_CTL, + "NT_FREEBSD_FEATURE_CTL (FreeBSD feature control)"}, +}; + static const NoteType AMDNoteTypes[] = { - {ELF::NT_AMD_AMDGPU_HSA_METADATA, - "NT_AMD_AMDGPU_HSA_METADATA (HSA Metadata)"}, - {ELF::NT_AMD_AMDGPU_ISA, "NT_AMD_AMDGPU_ISA (ISA Version)"}, - {ELF::NT_AMD_AMDGPU_PAL_METADATA, - "NT_AMD_AMDGPU_PAL_METADATA (PAL Metadata)"}, + {ELF::NT_AMD_HSA_CODE_OBJECT_VERSION, + "NT_AMD_HSA_CODE_OBJECT_VERSION (AMD HSA Code Object Version)"}, + {ELF::NT_AMD_HSA_HSAIL, "NT_AMD_HSA_HSAIL (AMD HSA HSAIL Properties)"}, + {ELF::NT_AMD_HSA_ISA_VERSION, "NT_AMD_HSA_ISA_VERSION (AMD HSA ISA Version)"}, + {ELF::NT_AMD_HSA_METADATA, "NT_AMD_HSA_METADATA (AMD HSA Metadata)"}, + {ELF::NT_AMD_HSA_ISA_NAME, "NT_AMD_HSA_ISA_NAME (AMD HSA ISA Name)"}, + {ELF::NT_AMD_PAL_METADATA, "NT_AMD_PAL_METADATA (AMD PAL Metadata)"}, }; static const NoteType AMDGPUNoteTypes[] = { @@ -5141,8 +5368,7 @@ }; template <class ELFT> -const StringRef getNoteTypeName(const typename ELFT::Note &Note, - unsigned ELFType) { +StringRef getNoteTypeName(const typename ELFT::Note &Note, unsigned ELFType) { uint32_t Type = Note.getType(); auto FindNote = [&](ArrayRef<NoteType> V) -> StringRef { for (const NoteType &N : V) @@ -5154,8 +5380,17 @@ StringRef Name = Note.getName(); if (Name == "GNU") return FindNote(GNUNoteTypes); - if (Name == "FreeBSD") - return FindNote(FreeBSDNoteTypes); + if (Name == "FreeBSD") { + if (ELFType == ELF::ET_CORE) { + // FreeBSD also places the generic core notes in the FreeBSD namespace. + StringRef Result = FindNote(FreeBSDCoreNoteTypes); + if (!Result.empty()) + return Result; + return FindNote(CoreNoteTypes); + } else { + return FindNote(FreeBSDNoteTypes); + } + } if (Name == "AMD") return FindNote(AMDNoteTypes); if (Name == "AMDGPU") @@ -5172,12 +5407,13 @@ llvm::function_ref<void(Optional<StringRef>, typename ELFT::Off, typename ELFT::Addr)> StartNotesFn, - llvm::function_ref<Error(const typename ELFT::Note &)> ProcessNoteFn, + llvm::function_ref<Error(const typename ELFT::Note &, bool)> ProcessNoteFn, llvm::function_ref<void()> FinishNotesFn) { const ELFFile<ELFT> &Obj = Dumper.getElfObject().getELFFile(); + bool IsCoreFile = Obj.getHeader().e_type == ELF::ET_CORE; ArrayRef<typename ELFT::Shdr> Sections = cantFail(Obj.sections()); - if (Obj.getHeader().e_type != ELF::ET_CORE && !Sections.empty()) { + if (!IsCoreFile && !Sections.empty()) { for (const typename ELFT::Shdr &S : Sections) { if (S.sh_type != SHT_NOTE) continue; @@ -5186,7 +5422,7 @@ Error Err = Error::success(); size_t I = 0; for (const typename ELFT::Note Note : Obj.notes(S, Err)) { - if (Error E = ProcessNoteFn(Note)) + if (Error E = ProcessNoteFn(Note, IsCoreFile)) Dumper.reportUniqueWarning( "unable to read note with index " + Twine(I) + " from the " + describe(Obj, S) + ": " + toString(std::move(E))); @@ -5217,7 +5453,7 @@ Error Err = Error::success(); size_t Index = 0; for (const typename ELFT::Note Note : Obj.notes(P, Err)) { - if (Error E = ProcessNoteFn(Note)) + if (Error E = ProcessNoteFn(Note, IsCoreFile)) Dumper.reportUniqueWarning("unable to read note with index " + Twine(Index) + " from the PT_NOTE segment with index " + @@ -5233,9 +5469,17 @@ } template <class ELFT> void GNUELFDumper<ELFT>::printNotes() { + bool IsFirstHeader = true; auto PrintHeader = [&](Optional<StringRef> SecName, const typename ELFT::Off Offset, const typename ELFT::Addr Size) { + // Print a newline between notes sections to match GNU readelf. + if (!IsFirstHeader) { + OS << '\n'; + } else { + IsFirstHeader = false; + } + OS << "Displaying notes found "; if (SecName) @@ -5247,7 +5491,7 @@ OS << " Owner Data size \tDescription\n"; }; - auto ProcessNote = [&](const Elf_Note &Note) -> Error { + auto ProcessNote = [&](const Elf_Note &Note, bool IsCore) -> Error { StringRef Name = Note.getName(); ArrayRef<uint8_t> Descriptor = Note.getDesc(); Elf_Word Type = Note.getType(); @@ -5264,28 +5508,42 @@ OS << "Unknown note type: (" << format_hex(Type, 10) << ")\n"; // Print the description, or fallback to printing raw bytes for unknown - // owners. + // owners/if we fail to pretty-print the contents. if (Name == "GNU") { - printGNUNote<ELFT>(OS, Type, Descriptor); + if (printGNUNote<ELFT>(OS, Type, Descriptor)) + return Error::success(); + } else if (Name == "FreeBSD") { + if (Optional<FreeBSDNote> N = + getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) { + OS << " " << N->Type << ": " << N->Value << '\n'; + return Error::success(); + } } else if (Name == "AMD") { const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) + if (!N.Type.empty()) { OS << " " << N.Type << ":\n " << N.Value << '\n'; + return Error::success(); + } } else if (Name == "AMDGPU") { const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) + if (!N.Type.empty()) { OS << " " << N.Type << ":\n " << N.Value << '\n'; + return Error::success(); + } } else if (Name == "CORE") { if (Type == ELF::NT_FILE) { DataExtractor DescExtractor(Descriptor, ELFT::TargetEndianness == support::little, sizeof(Elf_Addr)); - if (Expected<CoreNote> NoteOrErr = readCoreNote(DescExtractor)) + if (Expected<CoreNote> NoteOrErr = readCoreNote(DescExtractor)) { printCoreNote<ELFT>(OS, *NoteOrErr); - else + return Error::success(); + } else { return NoteOrErr.takeError(); + } } - } else if (!Descriptor.empty()) { + } + if (!Descriptor.empty()) { OS << " description data:"; for (uint8_t B : Descriptor) OS << " " << format("%02x", B); @@ -5461,64 +5719,81 @@ } template <class ELFT> -bool ELFDumper<ELFT>::printFunctionStackSize( - uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec, - const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) { - uint32_t FuncSymIndex = 0; - if (this->DotSymtabSec) { - if (Expected<Elf_Sym_Range> SymsOrError = Obj.symbols(this->DotSymtabSec)) { - uint32_t Index = (uint32_t)-1; - for (const Elf_Sym &Sym : *SymsOrError) { - ++Index; +SmallVector<uint32_t> ELFDumper<ELFT>::getSymbolIndexesForFunctionAddress( + uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec) { + SmallVector<uint32_t> SymbolIndexes; + if (!this->AddressToIndexMap.hasValue()) { + // Populate the address to index map upon the first invocation of this + // function. + this->AddressToIndexMap.emplace(); + if (this->DotSymtabSec) { + if (Expected<Elf_Sym_Range> SymsOrError = + Obj.symbols(this->DotSymtabSec)) { + uint32_t Index = (uint32_t)-1; + for (const Elf_Sym &Sym : *SymsOrError) { + ++Index; - if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC) - continue; - - if (Expected<uint64_t> SymAddrOrErr = - ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress()) { - if (SymValue != *SymAddrOrErr) + if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC) continue; - } else { - std::string Name = this->getStaticSymbolName(Index); - reportUniqueWarning("unable to get address of symbol '" + Name + - "': " + toString(SymAddrOrErr.takeError())); - break; - } - // Check if the symbol is in the right section. FunctionSec == None - // means "any section". - if (FunctionSec) { - if (Expected<const Elf_Shdr *> SecOrErr = - Obj.getSection(Sym, this->DotSymtabSec, - this->getShndxTable(this->DotSymtabSec))) { - if (*FunctionSec != *SecOrErr) - continue; - } else { + Expected<uint64_t> SymAddrOrErr = + ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress(); + if (!SymAddrOrErr) { std::string Name = this->getStaticSymbolName(Index); - // Note: it is impossible to trigger this error currently, it is - // untested. - reportUniqueWarning("unable to get section of symbol '" + Name + - "': " + toString(SecOrErr.takeError())); - break; + reportUniqueWarning("unable to get address of symbol '" + Name + + "': " + toString(SymAddrOrErr.takeError())); + return SymbolIndexes; } - } - FuncSymIndex = Index; - break; + (*this->AddressToIndexMap)[*SymAddrOrErr].push_back(Index); + } + } else { + reportUniqueWarning("unable to read the symbol table: " + + toString(SymsOrError.takeError())); } - } else { - reportUniqueWarning("unable to read the symbol table: " + - toString(SymsOrError.takeError())); } } - std::string FuncName = "?"; - if (!FuncSymIndex) + auto Symbols = this->AddressToIndexMap->find(SymValue); + if (Symbols == this->AddressToIndexMap->end()) + return SymbolIndexes; + + for (uint32_t Index : Symbols->second) { + // Check if the symbol is in the right section. FunctionSec == None + // means "any section". + if (FunctionSec) { + const Elf_Sym &Sym = *cantFail(Obj.getSymbol(this->DotSymtabSec, Index)); + if (Expected<const Elf_Shdr *> SecOrErr = + Obj.getSection(Sym, this->DotSymtabSec, + this->getShndxTable(this->DotSymtabSec))) { + if (*FunctionSec != *SecOrErr) + continue; + } else { + std::string Name = this->getStaticSymbolName(Index); + // Note: it is impossible to trigger this error currently, it is + // untested. + reportUniqueWarning("unable to get section of symbol '" + Name + + "': " + toString(SecOrErr.takeError())); + return SymbolIndexes; + } + } + + SymbolIndexes.push_back(Index); + } + + return SymbolIndexes; +} + +template <class ELFT> +bool ELFDumper<ELFT>::printFunctionStackSize( + uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec, + const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) { + SmallVector<uint32_t> FuncSymIndexes = + this->getSymbolIndexesForFunctionAddress(SymValue, FunctionSec); + if (FuncSymIndexes.empty()) reportUniqueWarning( "could not identify function symbol for stack size entry in " + describe(StackSizeSec)); - else - FuncName = this->getStaticSymbolName(FuncSymIndex); // Extract the size. The expectation is that Offset is pointing to the right // place, i.e. past the function address. @@ -5530,17 +5805,27 @@ toString(std::move(Err))); return false; } - printStackSizeEntry(StackSize, FuncName); + + if (FuncSymIndexes.empty()) { + printStackSizeEntry(StackSize, {"?"}); + } else { + SmallVector<std::string> FuncSymNames; + for (uint32_t Index : FuncSymIndexes) + FuncSymNames.push_back(this->getStaticSymbolName(Index)); + printStackSizeEntry(StackSize, FuncSymNames); + } + return true; } template <class ELFT> void GNUELFDumper<ELFT>::printStackSizeEntry(uint64_t Size, - StringRef FuncName) { + ArrayRef<std::string> FuncNames) { OS.PadToColumn(2); OS << format_decimal(Size, 11); OS.PadToColumn(18); - OS << FuncName << "\n"; + + OS << join(FuncNames.begin(), FuncNames.end(), ", ") << "\n"; } template <class ELFT> @@ -5628,28 +5913,15 @@ } template <class ELFT> -void ELFDumper<ELFT>::printRelocatableStackSizes( - std::function<void()> PrintHeader) { - // Build a map between stack size sections and their corresponding relocation - // sections. - llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> StackSizeRelocMap; +void ELFDumper<ELFT>::getSectionAndRelocations( + std::function<bool(const Elf_Shdr &)> IsMatch, + llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> &SecToRelocMap) { for (const Elf_Shdr &Sec : cantFail(Obj.sections())) { - StringRef SectionName; - if (Expected<StringRef> NameOrErr = Obj.getSectionName(Sec)) - SectionName = *NameOrErr; - else - consumeError(NameOrErr.takeError()); - - // A stack size section that we haven't encountered yet is mapped to the - // null section until we find its corresponding relocation section. - if (SectionName == ".stack_sizes") - if (StackSizeRelocMap - .insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr)) + if (IsMatch(Sec)) + if (SecToRelocMap.insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr)) .second) continue; - // Check relocation sections if they are relocating contents of a - // stack sizes section. if (Sec.sh_type != ELF::SHT_RELA && Sec.sh_type != ELF::SHT_REL) continue; @@ -5660,14 +5932,28 @@ toString(RelSecOrErr.takeError())); continue; } - const Elf_Shdr *ContentsSec = *RelSecOrErr; - if (this->getPrintableSectionName(**RelSecOrErr) != ".stack_sizes") - continue; - - // Insert a mapping from the stack sizes section to its relocation section. - StackSizeRelocMap[ContentsSec] = &Sec; + if (IsMatch(*ContentsSec)) + SecToRelocMap[ContentsSec] = &Sec; } +} + +template <class ELFT> +void ELFDumper<ELFT>::printRelocatableStackSizes( + std::function<void()> PrintHeader) { + // Build a map between stack size sections and their corresponding relocation + // sections. + llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> StackSizeRelocMap; + auto IsMatch = [&](const Elf_Shdr &Sec) -> bool { + StringRef SectionName; + if (Expected<StringRef> NameOrErr = Obj.getSectionName(Sec)) + SectionName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + return SectionName == ".stack_sizes"; + }; + getSectionAndRelocations(IsMatch, StackSizeRelocMap); for (const auto &StackSizeMapEntry : StackSizeRelocMap) { PrintHeader(); @@ -5728,7 +6014,7 @@ OS.PadToColumn(9); OS << "Size"; OS.PadToColumn(18); - OS << "Function\n"; + OS << "Functions\n"; HeaderHasBeenPrinted = true; }; @@ -5977,11 +6263,32 @@ W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderMipsFlags), unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI), unsigned(ELF::EF_MIPS_MACH)); - else if (E.e_machine == EM_AMDGPU) - W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderAMDGPUFlags), - unsigned(ELF::EF_AMDGPU_MACH)); - else if (E.e_machine == EM_RISCV) + else if (E.e_machine == EM_AMDGPU) { + switch (E.e_ident[ELF::EI_ABIVERSION]) { + default: + W.printHex("Flags", E.e_flags); + break; + case 0: + // ELFOSABI_AMDGPU_PAL, ELFOSABI_AMDGPU_MESA3D support *_V3 flags. + LLVM_FALLTHROUGH; + case ELF::ELFABIVERSION_AMDGPU_HSA_V3: + W.printFlags("Flags", E.e_flags, + makeArrayRef(ElfHeaderAMDGPUFlagsABIVersion3), + unsigned(ELF::EF_AMDGPU_MACH)); + break; + case ELF::ELFABIVERSION_AMDGPU_HSA_V4: + W.printFlags("Flags", E.e_flags, + makeArrayRef(ElfHeaderAMDGPUFlagsABIVersion4), + unsigned(ELF::EF_AMDGPU_MACH), + unsigned(ELF::EF_AMDGPU_FEATURE_XNACK_V4), + unsigned(ELF::EF_AMDGPU_FEATURE_SRAMECC_V4)); + break; + } + } else if (E.e_machine == EM_RISCV) W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderRISCVFlags)); + else if (E.e_machine == EM_AVR) + W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderAVRFlags), + unsigned(ELF::EF_AVR_ARCH_MASK)); else W.printFlags("Flags", E.e_flags); W.printNumber("HeaderSize", E.e_ehsize); @@ -6407,28 +6714,142 @@ W.startLine() << "Hash Histogram not implemented!\n"; } -template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() { - ListScope L(W, "CGProfile"); - if (!this->DotCGProfileSec) - return; - - Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr = - this->Obj.template getSectionContentsAsArray<Elf_CGProfile>( - *this->DotCGProfileSec); - if (!CGProfileOrErr) { - this->reportUniqueWarning( - "unable to dump the SHT_LLVM_CALL_GRAPH_PROFILE section: " + - toString(CGProfileOrErr.takeError())); - return; +// Returns true if rel/rela section exists, and populates SymbolIndices. +// Otherwise returns false. +template <class ELFT> +static bool getSymbolIndices(const typename ELFT::Shdr *CGRelSection, + const ELFFile<ELFT> &Obj, + const LLVMELFDumper<ELFT> *Dumper, + SmallVector<uint32_t, 128> &SymbolIndices) { + if (!CGRelSection) { + Dumper->reportUniqueWarning( + "relocation section for a call graph section doesn't exist"); + return false; } - for (const Elf_CGProfile &CGPE : *CGProfileOrErr) { - DictScope D(W, "CGProfileEntry"); - W.printNumber("From", this->getStaticSymbolName(CGPE.cgp_from), - CGPE.cgp_from); - W.printNumber("To", this->getStaticSymbolName(CGPE.cgp_to), - CGPE.cgp_to); - W.printNumber("Weight", CGPE.cgp_weight); + if (CGRelSection->sh_type == SHT_REL) { + typename ELFT::RelRange CGProfileRel; + Expected<typename ELFT::RelRange> CGProfileRelOrError = + Obj.rels(*CGRelSection); + if (!CGProfileRelOrError) { + Dumper->reportUniqueWarning("unable to load relocations for " + "SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileRelOrError.takeError())); + return false; + } + + CGProfileRel = *CGProfileRelOrError; + for (const typename ELFT::Rel &Rel : CGProfileRel) + SymbolIndices.push_back(Rel.getSymbol(Obj.isMips64EL())); + } else { + // MC unconditionally produces SHT_REL, but GNU strip/objcopy may convert + // the format to SHT_RELA + // (https://sourceware.org/bugzilla/show_bug.cgi?id=28035) + typename ELFT::RelaRange CGProfileRela; + Expected<typename ELFT::RelaRange> CGProfileRelaOrError = + Obj.relas(*CGRelSection); + if (!CGProfileRelaOrError) { + Dumper->reportUniqueWarning("unable to load relocations for " + "SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileRelaOrError.takeError())); + return false; + } + + CGProfileRela = *CGProfileRelaOrError; + for (const typename ELFT::Rela &Rela : CGProfileRela) + SymbolIndices.push_back(Rela.getSymbol(Obj.isMips64EL())); + } + + return true; +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() { + llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> SecToRelocMap; + + auto IsMatch = [](const Elf_Shdr &Sec) -> bool { + return Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE; + }; + this->getSectionAndRelocations(IsMatch, SecToRelocMap); + + for (const auto &CGMapEntry : SecToRelocMap) { + const Elf_Shdr *CGSection = CGMapEntry.first; + const Elf_Shdr *CGRelSection = CGMapEntry.second; + + Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr = + this->Obj.template getSectionContentsAsArray<Elf_CGProfile>(*CGSection); + if (!CGProfileOrErr) { + this->reportUniqueWarning( + "unable to load the SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileOrErr.takeError())); + return; + } + + SmallVector<uint32_t, 128> SymbolIndices; + bool UseReloc = + getSymbolIndices<ELFT>(CGRelSection, this->Obj, this, SymbolIndices); + if (UseReloc && SymbolIndices.size() != CGProfileOrErr->size() * 2) { + this->reportUniqueWarning( + "number of from/to pairs does not match number of frequencies"); + UseReloc = false; + } + + ListScope L(W, "CGProfile"); + for (uint32_t I = 0, Size = CGProfileOrErr->size(); I != Size; ++I) { + const Elf_CGProfile &CGPE = (*CGProfileOrErr)[I]; + DictScope D(W, "CGProfileEntry"); + if (UseReloc) { + uint32_t From = SymbolIndices[I * 2]; + uint32_t To = SymbolIndices[I * 2 + 1]; + W.printNumber("From", this->getStaticSymbolName(From), From); + W.printNumber("To", this->getStaticSymbolName(To), To); + } + W.printNumber("Weight", CGPE.cgp_weight); + } + } +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printBBAddrMaps() { + bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL; + for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) { + if (Sec.sh_type != SHT_LLVM_BB_ADDR_MAP) + continue; + Optional<const Elf_Shdr *> FunctionSec = None; + if (IsRelocatable) + FunctionSec = + unwrapOrError(this->FileName, this->Obj.getSection(Sec.sh_link)); + ListScope L(W, "BBAddrMap"); + Expected<std::vector<Elf_BBAddrMap>> BBAddrMapOrErr = + this->Obj.decodeBBAddrMap(Sec); + if (!BBAddrMapOrErr) { + this->reportUniqueWarning("unable to dump " + this->describe(Sec) + ": " + + toString(BBAddrMapOrErr.takeError())); + continue; + } + for (const Elf_BBAddrMap &AM : *BBAddrMapOrErr) { + DictScope D(W, "Function"); + W.printHex("At", AM.Addr); + SmallVector<uint32_t> FuncSymIndex = + this->getSymbolIndexesForFunctionAddress(AM.Addr, FunctionSec); + std::string FuncName = "<?>"; + if (FuncSymIndex.empty()) + this->reportUniqueWarning( + "could not identify function symbol for address (0x" + + Twine::utohexstr(AM.Addr) + ") in " + this->describe(Sec)); + else + FuncName = this->getStaticSymbolName(FuncSymIndex.front()); + W.printString("Name", FuncName); + + ListScope L(W, "BB entries"); + for (const typename Elf_BBAddrMap::BBEntry &BBE : AM.BBEntries) { + DictScope L(W); + W.printHex("Offset", BBE.Offset); + W.printHex("Size", BBE.Size); + W.printBoolean("HasReturn", BBE.HasReturn); + W.printBoolean("HasTailCall", BBE.HasTailCall); + W.printBoolean("IsEHPad", BBE.IsEHPad); + W.printBoolean("CanFallThrough", BBE.CanFallThrough); + } + } } } @@ -6449,15 +6870,17 @@ } template <typename ELFT> -static void printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc, +static bool printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc, ScopedPrinter &W) { + // Return true if we were able to pretty-print the note, false otherwise. switch (NoteType) { default: - return; + return false; case ELF::NT_GNU_ABI_TAG: { const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc); if (!AbiTag.IsValid) { W.printString("ABI", "<corrupt GNU_ABI_TAG>"); + return false; } else { W.printString("OS", AbiTag.OSName); W.printString("ABI", AbiTag.ABI); @@ -6477,6 +6900,7 @@ W.printString(Property); break; } + return true; } static void printCoreNoteLLVMStyle(const CoreNote &Note, ScopedPrinter &W) { @@ -6505,7 +6929,7 @@ auto EndNotes = [&] { NoteScope.reset(); }; - auto ProcessNote = [&](const Elf_Note &Note) -> Error { + auto ProcessNote = [&](const Elf_Note &Note, bool IsCore) -> Error { DictScope D2(W, "Note"); StringRef Name = Note.getName(); ArrayRef<uint8_t> Descriptor = Note.getDesc(); @@ -6524,28 +6948,42 @@ "Unknown (" + to_string(format_hex(Type, 10)) + ")"); // Print the description, or fallback to printing raw bytes for unknown - // owners. + // owners/if we fail to pretty-print the contents. if (Name == "GNU") { - printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W); + if (printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W)) + return Error::success(); + } else if (Name == "FreeBSD") { + if (Optional<FreeBSDNote> N = + getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) { + W.printString(N->Type, N->Value); + return Error::success(); + } } else if (Name == "AMD") { const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) + if (!N.Type.empty()) { W.printString(N.Type, N.Value); + return Error::success(); + } } else if (Name == "AMDGPU") { const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) + if (!N.Type.empty()) { W.printString(N.Type, N.Value); + return Error::success(); + } } else if (Name == "CORE") { if (Type == ELF::NT_FILE) { DataExtractor DescExtractor(Descriptor, ELFT::TargetEndianness == support::little, sizeof(Elf_Addr)); - if (Expected<CoreNote> Note = readCoreNote(DescExtractor)) - printCoreNoteLLVMStyle(*Note, W); - else - return Note.takeError(); + if (Expected<CoreNote> N = readCoreNote(DescExtractor)) { + printCoreNoteLLVMStyle(*N, W); + return Error::success(); + } else { + return N.takeError(); + } } - } else if (!Descriptor.empty()) { + } + if (!Descriptor.empty()) { W.printBinaryBlock("Description data", Descriptor); } return Error::success(); @@ -6614,9 +7052,10 @@ } template <class ELFT> -void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) { +void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size, + ArrayRef<std::string> FuncNames) { DictScope D(W, "Entry"); - W.printString("Function", FuncName); + W.printList("Functions", FuncNames); W.printHex("Size", Size); }
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/MachODumper.cpp b/src/llvm-project/llvm/tools/llvm-readobj/MachODumper.cpp index c13b1f3..433ca93 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/MachODumper.cpp +++ b/src/llvm-project/llvm/tools/llvm-readobj/MachODumper.cpp
@@ -256,11 +256,14 @@ }; static const EnumEntry<unsigned> MachOSymbolFlags[] = { + { "ThumbDef", 0x8 }, { "ReferencedDynamically", 0x10 }, { "NoDeadStrip", 0x20 }, { "WeakRef", 0x40 }, { "WeakDef", 0x80 }, + { "SymbolResolver", 0x100 }, { "AltEntry", 0x200 }, + { "ColdFunc", 0x400 }, }; static const EnumEntry<unsigned> MachOSymbolTypes[] = { @@ -651,9 +654,9 @@ makeArrayRef(MachOSymbolTypes)); } W.printHex("Section", SectionName, MOSymbol.SectionIndex); - W.printEnum("RefType", static_cast<uint16_t>(MOSymbol.Flags & 0xF), + W.printEnum("RefType", static_cast<uint16_t>(MOSymbol.Flags & 0x7), makeArrayRef(MachOSymbolRefTypes)); - W.printFlags("Flags", static_cast<uint16_t>(MOSymbol.Flags & ~0xF), + W.printFlags("Flags", static_cast<uint16_t>(MOSymbol.Flags & ~0x7), makeArrayRef(MachOSymbolFlags)); W.printHex("Value", MOSymbol.Value); }
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/ObjDumper.cpp b/src/llvm-project/llvm/tools/llvm-readobj/ObjDumper.cpp index fc91d81..87c2293 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/ObjDumper.cpp +++ b/src/llvm-project/llvm/tools/llvm-readobj/ObjDumper.cpp
@@ -52,6 +52,25 @@ W << (isPrint(Start[i]) ? static_cast<char>(Start[i]) : '.'); } +void ObjDumper::printAsStringList(StringRef StringContent) { + const uint8_t *StrContent = StringContent.bytes_begin(); + const uint8_t *CurrentWord = StrContent; + const uint8_t *StrEnd = StringContent.bytes_end(); + + while (CurrentWord <= StrEnd) { + size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord), + StrEnd - CurrentWord); + if (!WordSize) { + CurrentWord++; + continue; + } + W.startLine() << format("[%6tx] ", CurrentWord - StrContent); + printAsPrintable(W.startLine(), CurrentWord, WordSize); + W.startLine() << '\n'; + CurrentWord += WordSize + 1; + } +} + static std::vector<object::SectionRef> getSectionRefsByNameOrIndex(const object::ObjectFile &Obj, ArrayRef<std::string> Sections) { @@ -109,23 +128,7 @@ StringRef SectionContent = unwrapOrError(Obj.getFileName(), Section.getContents()); - - const uint8_t *SecContent = SectionContent.bytes_begin(); - const uint8_t *CurrentWord = SecContent; - const uint8_t *SecEnd = SectionContent.bytes_end(); - - while (CurrentWord <= SecEnd) { - size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord), - SecEnd - CurrentWord); - if (!WordSize) { - CurrentWord++; - continue; - } - W.startLine() << format("[%6tx] ", CurrentWord - SecContent); - printAsPrintable(W.startLine(), CurrentWord, WordSize); - W.startLine() << '\n'; - CurrentWord += WordSize + 1; - } + printAsStringList(SectionContent); } }
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/ObjDumper.h b/src/llvm-project/llvm/tools/llvm-readobj/ObjDumper.h index d4e166b..7e1c0ca 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/ObjDumper.h +++ b/src/llvm-project/llvm/tools/llvm-readobj/ObjDumper.h
@@ -72,6 +72,7 @@ virtual void printGroupSections() {} virtual void printHashHistograms() {} virtual void printCGProfile() {} + virtual void printBBAddrMaps() {} virtual void printAddrsig() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} @@ -104,8 +105,13 @@ virtual void printMachOIndirectSymbols() { } virtual void printMachOLinkerOptions() { } + // Currently only implemented for XCOFF. + virtual void printStringTable() { } + virtual void printStackMap() const = 0; + void printAsStringList(StringRef StringContent); + void printSectionsAsString(const object::ObjectFile &Obj, ArrayRef<std::string> Sections); void printSectionsAsHex(const object::ObjectFile &Obj,
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/Opts.td b/src/llvm-project/llvm/tools/llvm-readobj/Opts.td new file mode 100644 index 0000000..493b937 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-readobj/Opts.td
@@ -0,0 +1,128 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass BB<string name, string help1, string help2> { + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; +} + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def addrsig : FF<"addrsig", "Display address-significance table">; +def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, --section-headers, " + "--symbols, --relocations, --dynamic-table, --notes, --version-info, --unwind, " + "--section-groups and --histogram">; +def arch_specific : FF<"arch-specific", "Display architecture-specific information">; +def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">; +def cg_profile : FF<"cg-profile", "Display call graph profile section">; +defm demangle : BB<"demangle", "Demangle symbol names", "Do not demangle symbol names (default)">; +def dependent_libraries : FF<"dependent-libraries", "Display the dependent libraries section">; +def dyn_relocations : FF<"dyn-relocations", "Display the dynamic relocation entries in the file">; +def dyn_syms : FF<"dyn-syms", "Display the dynamic symbol table">; +def expand_relocs : FF<"expand-relocs", "Expand each shown relocation to multiple lines">; +def file_header : FF<"file-header", "Display file header">; +def headers : FF<"headers", "Equivalent to setting: --file-header, --program-headers, --section-headers">; +defm hex_dump : Eq<"hex-dump", "Display the specified section(s) as hexadecimal bytes">, MetaVarName<"<name or index>">; +def relocs : FF<"relocs", "Display the relocation entries in the file">; +def section_data : FF<"section-data", "Display section data for each section shown">; +def section_details : FF<"section-details", "Display the section details">; +def section_headers : FF<"section-headers", "Display section headers">; +def section_mapping : FF<"section-mapping", "Display the section to segment mapping">; +def section_mapping_EQ_false : FF<"section-mapping=false", "Don't display the section to segment mapping">, Flags<[HelpHidden]>; +def section_relocations : FF<"section-relocations", "Display relocations for each section shown">; +def section_symbols : FF<"section-symbols", "Display symbols for each section shown">; +def stack_sizes : FF<"stack-sizes", "Display contents of all stack sizes sections">; +def stackmap : FF<"stackmap", "Display contents of stackmap section">; +defm string_dump : Eq<"string-dump", "Display the specified section(s) as a list of strings">, MetaVarName<"<name or index>">; +def string_table : FF<"string-table", "Display the string table (only for XCOFF now)">; +def symbols : FF<"symbols", "Display the symbol table. Also display the dynamic symbol table when using GNU output style for ELF">; +def unwind : FF<"unwind", "Display unwind information">; + +// ELF specific options. +def grp_elf : OptionGroup<"kind">, HelpText<"OPTIONS (ELF specific)">; +def dynamic_table : FF<"dynamic-table", "Display the dynamic section table">, Group<grp_elf>; +def elf_linker_options : FF<"elf-linker-options", "Display the .linker-options section">, Group<grp_elf>; +defm elf_output_style : Eq<"elf-output-style", "Specify ELF dump style">, Group<grp_elf>; +def histogram : FF<"histogram", "Display bucket list histogram for hash sections">, Group<grp_elf>; +def section_groups : FF<"section-groups", "Display section groups">, Group<grp_elf>; +def gnu_hash_table : FF<"gnu-hash-table", "Display .gnu.hash section">, Group<grp_elf>; +def hash_symbols : FF<"hash-symbols", "Display the dynamic symbols derived from the hash section">, Group<grp_elf>; +def hash_table : FF<"hash-table", "Display .hash section">, Group<grp_elf>; +def needed_libs : FF<"needed-libs", "Display the needed libraries">, Group<grp_elf>; +def notes : FF<"notes", "Display notes">, Group<grp_elf>; +def program_headers : FF<"program-headers", "Display program headers">, Group<grp_elf>; +def raw_relr : FF<"raw-relr", "Do not decode relocations in SHT_RELR section, display raw contents">, Group<grp_elf>; +def version_info : FF<"version-info", "Display version sections">, Group<grp_elf>; + +// Mach-O specific options. +def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">; +def macho_data_in_code : FF<"macho-data-in-code", "Display Data in Code command">, Group<grp_mach_o>; +def macho_dysymtab : FF<"macho-dysymtab", "Display Dysymtab command">, Group<grp_mach_o>; +def macho_indirect_symbols : FF<"macho-indirect-symbols", "Display indirect symbols">, Group<grp_mach_o>; +def macho_linker_options : FF<"macho-linker-options", "Display linker options">, Group<grp_mach_o>; +def macho_segment : FF<"macho-segment", "Display Segment command">, Group<grp_mach_o>; +def macho_version_min : FF<"macho-version-min", "Display version min command">, Group<grp_mach_o>; + +// PE/COFF specific options. +def grp_coff : OptionGroup<"kind">, HelpText<"OPTIONS (PE/COFF specific)">; +def codeview : FF<"codeview", "Display CodeView debug information">, Group<grp_coff>; +def codeview_ghash : FF<"codeview-ghash", "Enable global hashing for CodeView type stream de-duplication">, Group<grp_coff>; +def codeview_merged_types : FF<"codeview-merged-types", "Display the merged CodeView type stream">, Group<grp_coff>; +def codeview_subsection_bytes : FF<"codeview-subsection-bytes", "Dump raw contents of codeview debug sections and records">, Group<grp_coff>; +def coff_basereloc : FF<"coff-basereloc", "Display .reloc section">, Group<grp_coff>; +def coff_debug_directory : FF<"coff-debug-directory", "Display debug directory">, Group<grp_coff>; +def coff_directives : FF<"coff-directives", "Display .drectve section">, Group<grp_coff>; +def coff_exports : FF<"coff-exports", "Display export table">, Group<grp_coff>; +def coff_imports : FF<"coff-imports", "Display import table">, Group<grp_coff>; +def coff_load_config : FF<"coff-load-config", "Display load config">, Group<grp_coff>; +def coff_resources : FF<"coff-resources", "Display .rsrc section">, Group<grp_coff>; +def coff_tls_directory : FF<"coff-tls-directory", "Display TLS directory">, Group<grp_coff>; + +def help : FF<"help", "Display this help">; +def version : FF<"version", "Display the version">; + +// Ignored for GNU readelf compatibility. +def : F<"W", "Ignored for GNU readelf compatibility">; +def : FF<"wide", "Ignored for GNU readelf compatibility">; + +// Traditional llvm-readobj Aliases. +def : Flag<["--"], "dt">, Alias<dyn_syms>, HelpText<"Alias for --dyn-syms">; +def : Flag<["--"], "sd">, Alias<section_data>, HelpText<"Alias for --section-data">; +def : Flag<["--"], "st">, Alias<section_symbols>, HelpText<"Alias for --section-symbols">; +def : Flag<["--"], "sr">, Alias<section_relocations>, HelpText<"Alias for --section-relocations">; + +// Aliases. +def : FF<"dyn-symbols", "Alias for --dyn-syms">, Alias<dyn_syms>; +def : FF<"dynamic", "Alias for --dynamic-table">, Alias<dynamic_table>; +def : FF<"elf-cg-profile", "Alias for --cg-profile">, Alias<cg_profile>, Flags<[HelpHidden]>; +def : FF<"elf-hash-histogram", "Alias for --histogram">, Alias<histogram>, Flags<[HelpHidden]>; +def : FF<"elf-section-groups", "Alias for --section-groups">, Alias<section_groups>, Flags<[HelpHidden]>; +def : FF<"file-headers", "Alias for --file-header">, Alias<file_header>, Flags<[HelpHidden]>; +def : FF<"relocations", "Alias for --relocs">, Alias<relocs>; +def : FF<"sections", "Alias for --section-headers">, Alias<section_headers>; +def : FF<"segments", "Alias for --program-headers">, Alias<program_headers>, Group<grp_elf>; +def : FF<"syms", "Alias for --symbols">, Alias<symbols>; + +def : F<"A", "Alias for --arch-specific">, Alias<arch_specific>; +def : F<"a", "Alias for --all">, Alias<all>; +def : F<"C", "Alias for --demangle">, Alias<demangle>; +def : F<"d", "Alias for --dynamic-table">, Alias<dynamic_table>, Group<grp_elf>; +def : F<"e", "Alias for --headers">, Alias<headers>; +def : F<"g", "Alias for --section-groups">, Alias<section_groups>, Group<grp_elf>; +def : F<"h", "Alias for --file-header">, Alias<file_header>; +def : F<"I", "Alias for --histogram">, Alias<histogram>, Group<grp_elf>; +def : F<"l", "Alias for --program-headers">, Alias<program_headers>; +def : F<"n", "Alias for --notes">, Alias<notes>; +def : JoinedOrSeparate<["-"], "p">, Alias<string_dump_EQ>, HelpText<"Alias for --string-dump">, MetaVarName<"<name or index>">; +def : F<"r", "Alias for --relocs">, Alias<relocs>; +def : F<"S", "Alias for --section-headers">, Alias<section_headers>; +def : F<"s", "Alias for --symbols">, Alias<symbols>; +def : F<"t", "Alias for --section-details">, Alias<section_details>; +def : F<"u", "Alias for --unwind">, Alias<unwind>; +def : F<"V", "Alias for --version-info">, Alias<version_info>, Group<grp_elf>; +def : JoinedOrSeparate<["-"], "x">, Alias<hex_dump_EQ>, HelpText<"Alias for --hex-dump">, MetaVarName<"<name or index>">;
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/WasmDumper.cpp b/src/llvm-project/llvm/tools/llvm-readobj/WasmDumper.cpp index fb7134d..f7dcaa3 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/WasmDumper.cpp +++ b/src/llvm-project/llvm/tools/llvm-readobj/WasmDumper.cpp
@@ -23,8 +23,8 @@ static const EnumEntry<unsigned> WasmSymbolTypes[] = { #define ENUM_ENTRY(X) \ { #X, wasm::WASM_SYMBOL_TYPE_##X } - ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), - ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT), ENUM_ENTRY(TABLE), + ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), + ENUM_ENTRY(SECTION), ENUM_ENTRY(TAG), ENUM_ENTRY(TABLE), #undef ENUM_ENTRY }; @@ -33,7 +33,7 @@ { #X, wasm::WASM_SEC_##X } ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT), ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY), - ENUM_ENTRY(GLOBAL), ENUM_ENTRY(EVENT), ENUM_ENTRY(EXPORT), + ENUM_ENTRY(GLOBAL), ENUM_ENTRY(TAG), ENUM_ENTRY(EXPORT), ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE), ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT), #undef ENUM_ENTRY @@ -192,7 +192,7 @@ ListScope Group(W, "Memories"); for (const wasm::WasmLimits &Memory : Obj->memories()) { DictScope Group(W, "Memory"); - W.printNumber("InitialPages", Memory.Initial); + W.printNumber("MinPages", Memory.Minimum); if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) { W.printNumber("MaxPages", WasmSec.Offset); }
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/src/llvm-project/llvm/tools/llvm-readobj/XCOFFDumper.cpp index 8f0f18c..94ef96e 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/XCOFFDumper.cpp +++ b/src/llvm-project/llvm/tools/llvm-readobj/XCOFFDumper.cpp
@@ -34,13 +34,14 @@ void printUnwindInfo() override; void printStackMap() const override; void printNeededLibraries() override; + void printStringTable() override; private: template <typename T> void printSectionHeaders(ArrayRef<T> Sections); template <typename T> void printGenericSectionHeader(T &Sec) const; template <typename T> void printOverflowSectionHeader(T &Sec) const; void printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr); - void printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr); + void printCsectAuxEnt(XCOFFCsectAuxRef AuxEntRef); void printSectAuxEntForStat(const XCOFFSectAuxEntForStat *AuxEntPtr); void printSymbol(const SymbolRef &); void printRelocations(ArrayRef<XCOFFSectionHeader32> Sections); @@ -164,10 +165,17 @@ #undef ECase }; +static const EnumEntry<XCOFF::SymbolAuxType> SymAuxType[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(AUX_EXCEPT), ECase(AUX_FCN), ECase(AUX_SYM), ECase(AUX_FILE), + ECase(AUX_CSECT), ECase(AUX_SECT) +#undef ECase +}; + void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) { - if (Obj.is64Bit()) - report_fatal_error( - "Printing for File Auxiliary Entry in 64-bit is unimplemented."); + assert((!Obj.is64Bit() || AuxEntPtr->AuxType == XCOFF::AUX_FILE) && + "Mismatched auxiliary type!"); StringRef FileName = unwrapOrError(Obj.getFileName(), Obj.getCFileName(AuxEntPtr)); DictScope SymDs(W, "File Auxiliary Entry"); @@ -176,19 +184,22 @@ W.printString("Name", FileName); W.printEnum("Type", static_cast<uint8_t>(AuxEntPtr->Type), makeArrayRef(FileStringType)); + if (Obj.is64Bit()) { + W.printEnum("Auxiliary Type", static_cast<uint8_t>(AuxEntPtr->AuxType), + makeArrayRef(SymAuxType)); + } } static const EnumEntry<XCOFF::StorageMappingClass> CsectStorageMappingClass[] = { #define ECase(X) \ { #X, XCOFF::X } - ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB), - ECase(XMC_GL), ECase(XMC_XO), ECase(XMC_SV), - ECase(XMC_SV64), ECase(XMC_SV3264), ECase(XMC_TI), - ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0), - ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS), - ECase(XMC_UA), ECase(XMC_BS), ECase(XMC_UC), - ECase(XMC_TL), ECase(XMC_TE) + ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB), ECase(XMC_GL), + ECase(XMC_XO), ECase(XMC_SV), ECase(XMC_SV64), ECase(XMC_SV3264), + ECase(XMC_TI), ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0), + ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS), ECase(XMC_UA), + ECase(XMC_BS), ECase(XMC_UC), ECase(XMC_TL), ECase(XMC_UL), + ECase(XMC_TE) #undef ECase }; @@ -199,27 +210,32 @@ #undef ECase }; -void XCOFFDumper::printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr) { - assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file."); +void XCOFFDumper::printCsectAuxEnt(XCOFFCsectAuxRef AuxEntRef) { + assert((!Obj.is64Bit() || AuxEntRef.getAuxType64() == XCOFF::AUX_CSECT) && + "Mismatched auxiliary type!"); DictScope SymDs(W, "CSECT Auxiliary Entry"); - W.printNumber("Index", - Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr))); - if (AuxEntPtr->isLabel()) - W.printNumber("ContainingCsectSymbolIndex", AuxEntPtr->SectionOrLength); - else - W.printNumber("SectionLen", AuxEntPtr->SectionOrLength); - W.printHex("ParameterHashIndex", AuxEntPtr->ParameterHashIndex); - W.printHex("TypeChkSectNum", AuxEntPtr->TypeChkSectNum); + W.printNumber("Index", Obj.getSymbolIndex(AuxEntRef.getEntryAddress())); + W.printNumber(AuxEntRef.isLabel() ? "ContainingCsectSymbolIndex" + : "SectionLen", + AuxEntRef.getSectionOrLength()); + W.printHex("ParameterHashIndex", AuxEntRef.getParameterHashIndex()); + W.printHex("TypeChkSectNum", AuxEntRef.getTypeChkSectNum()); // Print out symbol alignment and type. - W.printNumber("SymbolAlignmentLog2", AuxEntPtr->getAlignmentLog2()); - W.printEnum("SymbolType", AuxEntPtr->getSymbolType(), + W.printNumber("SymbolAlignmentLog2", AuxEntRef.getAlignmentLog2()); + W.printEnum("SymbolType", AuxEntRef.getSymbolType(), makeArrayRef(CsectSymbolTypeClass)); W.printEnum("StorageMappingClass", - static_cast<uint8_t>(AuxEntPtr->StorageMappingClass), + static_cast<uint8_t>(AuxEntRef.getStorageMappingClass()), makeArrayRef(CsectStorageMappingClass)); - W.printHex("StabInfoIndex", AuxEntPtr->StabInfoIndex); - W.printHex("StabSectNum", AuxEntPtr->StabSectNum); + + if (Obj.is64Bit()) { + W.printEnum("Auxiliary Type", static_cast<uint8_t>(XCOFF::AUX_CSECT), + makeArrayRef(SymAuxType)); + } else { + W.printHex("StabInfoIndex", AuxEntRef.getStabInfoIndex32()); + W.printHex("StabSectNum", AuxEntRef.getStabSectNum32()); + } } void XCOFFDumper::printSectAuxEntForStat( @@ -301,53 +317,62 @@ }; void XCOFFDumper::printSymbol(const SymbolRef &S) { - if (Obj.is64Bit()) - report_fatal_error("64-bit support is unimplemented."); - DataRefImpl SymbolDRI = S.getRawDataRefImpl(); - const XCOFFSymbolEntry *SymbolEntPtr = Obj.toSymbolEntry(SymbolDRI); + XCOFFSymbolRef SymbolEntRef = Obj.toSymbolRef(SymbolDRI); - XCOFFSymbolRef XCOFFSymRef(SymbolDRI, &Obj); - uint8_t NumberOfAuxEntries = XCOFFSymRef.getNumberOfAuxEntries(); + uint8_t NumberOfAuxEntries = SymbolEntRef.getNumberOfAuxEntries(); DictScope SymDs(W, "Symbol"); StringRef SymbolName = - unwrapOrError(Obj.getFileName(), Obj.getSymbolName(SymbolDRI)); + unwrapOrError(Obj.getFileName(), SymbolEntRef.getName()); - W.printNumber("Index", - Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(SymbolEntPtr))); + W.printNumber("Index", Obj.getSymbolIndex(SymbolEntRef.getEntryAddress())); W.printString("Name", SymbolName); - W.printHex(GetSymbolValueName(SymbolEntPtr->StorageClass), - SymbolEntPtr->Value); + W.printHex(GetSymbolValueName(SymbolEntRef.getStorageClass()), + SymbolEntRef.getValue()); StringRef SectionName = - unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntPtr)); + unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntRef)); W.printString("Section", SectionName); - if (XCOFFSymRef.getStorageClass() == XCOFF::C_FILE) { - W.printEnum("Source Language ID", - SymbolEntPtr->CFileLanguageIdAndTypeId.LanguageId, + if (SymbolEntRef.getStorageClass() == XCOFF::C_FILE) { + W.printEnum("Source Language ID", SymbolEntRef.getLanguageIdForCFile(), makeArrayRef(CFileLangIdClass)); - W.printEnum("CPU Version ID", - SymbolEntPtr->CFileLanguageIdAndTypeId.CpuTypeId, + W.printEnum("CPU Version ID", SymbolEntRef.getCPUTypeIddForCFile(), makeArrayRef(CFileCpuIdClass)); } else - W.printHex("Type", SymbolEntPtr->SymbolType); + W.printHex("Type", SymbolEntRef.getSymbolType()); - W.printEnum("StorageClass", static_cast<uint8_t>(SymbolEntPtr->StorageClass), + W.printEnum("StorageClass", + static_cast<uint8_t>(SymbolEntRef.getStorageClass()), makeArrayRef(SymStorageClass)); - W.printNumber("NumberOfAuxEntries", SymbolEntPtr->NumberOfAuxEntries); + W.printNumber("NumberOfAuxEntries", NumberOfAuxEntries); if (NumberOfAuxEntries == 0) return; - switch (XCOFFSymRef.getStorageClass()) { + switch (SymbolEntRef.getStorageClass()) { case XCOFF::C_FILE: // If the symbol is C_FILE and has auxiliary entries... - for (int i = 1; i <= NumberOfAuxEntries; i++) { + for (int I = 1; I <= NumberOfAuxEntries; I++) { + uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress( + SymbolEntRef.getEntryAddress(), I); + + if (Obj.is64Bit() && + *Obj.getSymbolAuxType(AuxAddress) != XCOFF::SymbolAuxType::AUX_FILE) { + W.startLine() << "!Unexpected raw auxiliary entry data:\n"; + W.startLine() << format_bytes( + ArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(AuxAddress), + XCOFF::SymbolTableEntrySize), + 0, XCOFF::SymbolTableEntrySize) + << "\n"; + continue; + } + const XCOFFFileAuxEnt *FileAuxEntPtr = - reinterpret_cast<const XCOFFFileAuxEnt *>(SymbolEntPtr + i); + reinterpret_cast<const XCOFFFileAuxEnt *>(AuxAddress); #ifndef NDEBUG Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(FileAuxEntPtr)); #endif @@ -356,34 +381,52 @@ break; case XCOFF::C_EXT: case XCOFF::C_WEAKEXT: - case XCOFF::C_HIDEXT: + case XCOFF::C_HIDEXT: { // If the symbol is for a function, and it has more than 1 auxiliary entry, // then one of them must be function auxiliary entry which we do not // support yet. - if (XCOFFSymRef.isFunction() && NumberOfAuxEntries >= 2) + if (SymbolEntRef.isFunction() && NumberOfAuxEntries >= 2) report_fatal_error("Function auxiliary entry printing is unimplemented."); // If there is more than 1 auxiliary entry, instead of printing out - // error information, print out the raw Auxiliary entry from 1st till - // the last - 1. The last one must be a CSECT Auxiliary Entry. - for (int i = 1; i < NumberOfAuxEntries; i++) { + // error information, print out the raw Auxiliary entry. + // For 32-bit object, print from first to the last - 1. The last one must be + // a CSECT Auxiliary Entry. + // For 64-bit object, print from first to last and skips if SymbolAuxType is + // AUX_CSECT. + for (int I = 1; I <= NumberOfAuxEntries; I++) { + if (I == NumberOfAuxEntries && !Obj.is64Bit()) + break; + + uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress( + SymbolEntRef.getEntryAddress(), I); + if (Obj.is64Bit() && + *Obj.getSymbolAuxType(AuxAddress) == XCOFF::SymbolAuxType::AUX_CSECT) + continue; + W.startLine() << "!Unexpected raw auxiliary entry data:\n"; W.startLine() << format_bytes( - ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i), + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(AuxAddress), XCOFF::SymbolTableEntrySize)); } - // The symbol's last auxiliary entry is a CSECT Auxiliary Entry. - printCsectAuxEnt32(XCOFFSymRef.getXCOFFCsectAuxEnt32()); + auto ErrOrCsectAuxRef = SymbolEntRef.getXCOFFCsectAuxRef(); + if (!ErrOrCsectAuxRef) + reportUniqueWarning(ErrOrCsectAuxRef.takeError()); + else + printCsectAuxEnt(*ErrOrCsectAuxRef); + break; + } case XCOFF::C_STAT: if (NumberOfAuxEntries > 1) report_fatal_error( "C_STAT symbol should not have more than 1 auxiliary entry."); const XCOFFSectAuxEntForStat *StatAuxEntPtr; - StatAuxEntPtr = - reinterpret_cast<const XCOFFSectAuxEntForStat *>(SymbolEntPtr + 1); + StatAuxEntPtr = reinterpret_cast<const XCOFFSectAuxEntForStat *>( + XCOFFObjectFile::getAdvancedSymbolEntryAddress( + SymbolEntRef.getEntryAddress(), 1)); #ifndef NDEBUG Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(StatAuxEntPtr)); #endif @@ -399,7 +442,9 @@ for (int i = 1; i <= NumberOfAuxEntries; i++) { W.startLine() << "!Unexpected raw auxiliary entry data:\n"; W.startLine() << format_bytes( - ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i), + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>( + XCOFFObjectFile::getAdvancedSymbolEntryAddress( + SymbolEntRef.getEntryAddress(), i)), XCOFF::SymbolTableEntrySize)); } break; @@ -412,6 +457,12 @@ printSymbol(S); } +void XCOFFDumper::printStringTable() { + DictScope DS(W, "StringTable"); + StringRef StrTable = Obj.getStringTable(); + printAsStringList(StrTable); +} + void XCOFFDumper::printDynamicSymbols() { llvm_unreachable("Unimplemented functionality for XCOFFDumper"); }
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/llvm-readobj.cpp b/src/llvm-project/llvm/tools/llvm-readobj/llvm-readobj.cpp index 41cd441..0b49f03 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/src/llvm-project/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -31,6 +31,9 @@ #include "llvm/Object/Wasm.h" #include "llvm/Object/WindowsResource.h" #include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DataTypes.h" @@ -47,339 +50,107 @@ using namespace llvm; using namespace llvm::object; +namespace { +using namespace llvm::opt; // for HelpHidden in Opts.inc +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class ReadobjOptTable : public opt::OptTable { +public: + ReadobjOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; + +enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; +} // namespace + namespace opts { - cl::list<std::string> InputFilenames(cl::Positional, - cl::desc("<input object files>"), - cl::ZeroOrMore); +static bool Addrsig; +static bool All; +static bool ArchSpecificInfo; +static bool BBAddrMap; +bool ExpandRelocs; +static bool CGProfile; +bool Demangle; +static bool DependentLibraries; +static bool DynRelocs; +static bool DynamicSymbols; +static bool FileHeaders; +static bool Headers; +static std::vector<std::string> HexDump; +static bool PrintStackMap; +static bool PrintStackSizes; +static bool Relocations; +bool SectionData; +static bool SectionDetails; +static bool SectionHeaders; +bool SectionRelocations; +bool SectionSymbols; +static std::vector<std::string> StringDump; +static bool StringTable; +static bool Symbols; +static bool UnwindInfo; +static cl::boolOrDefault SectionMapping; - // --all, -a - cl::opt<bool> - All("all", - cl::desc("Equivalent to setting: --file-headers, --program-headers, " - "--section-headers, --symbols, --relocations, " - "--dynamic-table, --notes, --version-info, --unwind, " - "--section-groups and --elf-hash-histogram.")); - cl::alias AllShort("a", cl::desc("Alias for --all"), cl::aliasopt(All)); +// ELF specific options. +static bool DynamicTable; +static bool ELFLinkerOptions; +static bool GnuHashTable; +static bool HashSymbols; +static bool HashTable; +static bool HashHistogram; +static bool NeededLibraries; +static bool Notes; +static bool ProgramHeaders; +bool RawRelr; +static bool SectionGroups; +static bool VersionInfo; - // --dependent-libraries - cl::opt<bool> - DependentLibraries("dependent-libraries", - cl::desc("Display the dependent libraries section")); +// Mach-O specific options. +static bool MachODataInCode; +static bool MachODysymtab; +static bool MachOIndirectSymbols; +static bool MachOLinkerOptions; +static bool MachOSegment; +static bool MachOVersionMin; - // --headers, -e - cl::opt<bool> - Headers("headers", - cl::desc("Equivalent to setting: --file-headers, --program-headers, " - "--section-headers")); - cl::alias HeadersShort("e", cl::desc("Alias for --headers"), - cl::aliasopt(Headers)); +// PE/COFF specific options. +static bool CodeView; +static bool CodeViewEnableGHash; +static bool CodeViewMergedTypes; +bool CodeViewSubsectionBytes; +static bool COFFBaseRelocs; +static bool COFFDebugDirectory; +static bool COFFDirectives; +static bool COFFExports; +static bool COFFImports; +static bool COFFLoadConfig; +static bool COFFResources; +static bool COFFTLSDirectory; - // --wide, -W - cl::opt<bool> - WideOutput("wide", cl::desc("Ignored for compatibility with GNU readelf"), - cl::Hidden); - cl::alias WideOutputShort("W", - cl::desc("Alias for --wide"), - cl::aliasopt(WideOutput)); - - // --file-headers, --file-header, -h - cl::opt<bool> FileHeaders("file-headers", - cl::desc("Display file headers ")); - cl::alias FileHeadersShort("h", cl::desc("Alias for --file-headers"), - cl::aliasopt(FileHeaders), cl::NotHidden); - cl::alias FileHeadersSingular("file-header", - cl::desc("Alias for --file-headers"), - cl::aliasopt(FileHeaders)); - - // --section-headers, --sections, -S - // Also -s in llvm-readobj mode. - cl::opt<bool> SectionHeaders("section-headers", - cl::desc("Display all section headers.")); - cl::alias SectionsShortUpper("S", cl::desc("Alias for --section-headers"), - cl::aliasopt(SectionHeaders), cl::NotHidden); - cl::alias SectionHeadersAlias("sections", - cl::desc("Alias for --section-headers"), - cl::aliasopt(SectionHeaders), cl::NotHidden); - - // --section-relocations - // Also --sr in llvm-readobj mode. - cl::opt<bool> SectionRelocations("section-relocations", - cl::desc("Display relocations for each section shown.")); - - // --section-symbols - // Also --st in llvm-readobj mode. - cl::opt<bool> SectionSymbols("section-symbols", - cl::desc("Display symbols for each section shown.")); - - // --section-data - // Also --sd in llvm-readobj mode. - cl::opt<bool> SectionData("section-data", - cl::desc("Display section data for each section shown.")); - - // --section-mapping - cl::opt<cl::boolOrDefault> - SectionMapping("section-mapping", - cl::desc("Display the section to segment mapping.")); - - // --relocations, --relocs, -r - cl::opt<bool> Relocations("relocations", - cl::desc("Display the relocation entries in the file")); - cl::alias RelocationsShort("r", cl::desc("Alias for --relocations"), - cl::aliasopt(Relocations), cl::NotHidden); - cl::alias RelocationsGNU("relocs", cl::desc("Alias for --relocations"), - cl::aliasopt(Relocations)); - - // --notes, -n - cl::opt<bool> Notes("notes", cl::desc("Display the ELF notes in the file")); - cl::alias NotesShort("n", cl::desc("Alias for --notes"), cl::aliasopt(Notes)); - - // --dyn-relocations - cl::opt<bool> DynRelocs("dyn-relocations", - cl::desc("Display the dynamic relocation entries in the file")); - - // --section-details - // Also -t in llvm-readelf mode. - cl::opt<bool> SectionDetails("section-details", - cl::desc("Display the section details")); - - // --symbols - // Also -s in llvm-readelf mode, or -t in llvm-readobj mode. - cl::opt<bool> - Symbols("symbols", - cl::desc("Display the symbol table. Also display the dynamic " - "symbol table when using GNU output style for ELF")); - cl::alias SymbolsGNU("syms", cl::desc("Alias for --symbols"), - cl::aliasopt(Symbols)); - - // --dyn-symbols, --dyn-syms - // Also --dt in llvm-readobj mode. - cl::opt<bool> DynamicSymbols("dyn-symbols", - cl::desc("Display the dynamic symbol table")); - cl::alias DynSymsGNU("dyn-syms", cl::desc("Alias for --dyn-symbols"), - cl::aliasopt(DynamicSymbols)); - - // --hash-symbols - cl::opt<bool> HashSymbols( - "hash-symbols", - cl::desc("Display the dynamic symbols derived from the hash section")); - - // --unwind, -u - cl::opt<bool> UnwindInfo("unwind", - cl::desc("Display unwind information")); - cl::alias UnwindInfoShort("u", - cl::desc("Alias for --unwind"), - cl::aliasopt(UnwindInfo)); - - // --dynamic-table, --dynamic, -d - cl::opt<bool> DynamicTable("dynamic-table", - cl::desc("Display the ELF .dynamic section table")); - cl::alias DynamicTableShort("d", cl::desc("Alias for --dynamic-table"), - cl::aliasopt(DynamicTable), cl::NotHidden); - cl::alias DynamicTableAlias("dynamic", cl::desc("Alias for --dynamic-table"), - cl::aliasopt(DynamicTable)); - - // --needed-libs - cl::opt<bool> NeededLibraries("needed-libs", - cl::desc("Display the needed libraries")); - - // --program-headers, --segments, -l - cl::opt<bool> ProgramHeaders("program-headers", - cl::desc("Display ELF program headers")); - cl::alias ProgramHeadersShort("l", cl::desc("Alias for --program-headers"), - cl::aliasopt(ProgramHeaders), cl::NotHidden); - cl::alias SegmentsAlias("segments", cl::desc("Alias for --program-headers"), - cl::aliasopt(ProgramHeaders)); - - // --string-dump, -p - cl::list<std::string> StringDump( - "string-dump", cl::value_desc("number|name"), - cl::desc("Display the specified section(s) as a list of strings"), - cl::ZeroOrMore); - cl::alias StringDumpShort("p", cl::desc("Alias for --string-dump"), - cl::aliasopt(StringDump), cl::Prefix); - - // --hex-dump, -x - cl::list<std::string> - HexDump("hex-dump", cl::value_desc("number|name"), - cl::desc("Display the specified section(s) as hexadecimal bytes"), - cl::ZeroOrMore); - cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"), - cl::aliasopt(HexDump), cl::Prefix); - - // --demangle, -C - cl::opt<bool> Demangle("demangle", - cl::desc("Demangle symbol names in output")); - cl::alias DemangleShort("C", cl::desc("Alias for --demangle"), - cl::aliasopt(Demangle), cl::NotHidden); - - // --hash-table - cl::opt<bool> HashTable("hash-table", - cl::desc("Display ELF hash table")); - - // --gnu-hash-table - cl::opt<bool> GnuHashTable("gnu-hash-table", - cl::desc("Display ELF .gnu.hash section")); - - // --expand-relocs - cl::opt<bool> ExpandRelocs("expand-relocs", - cl::desc("Expand each shown relocation to multiple lines")); - - // --raw-relr - cl::opt<bool> RawRelr("raw-relr", - cl::desc("Do not decode relocations in SHT_RELR section, display raw contents")); - - // --codeview - cl::opt<bool> CodeView("codeview", - cl::desc("Display CodeView debug information")); - - // --codeview-merged-types - cl::opt<bool> - CodeViewMergedTypes("codeview-merged-types", - cl::desc("Display the merged CodeView type stream")); - - // --codeview-ghash - cl::opt<bool> CodeViewEnableGHash( - "codeview-ghash", - cl::desc( - "Enable global hashing for CodeView type stream de-duplication")); - - // --codeview-subsection-bytes - cl::opt<bool> CodeViewSubsectionBytes( - "codeview-subsection-bytes", - cl::desc("Dump raw contents of codeview debug sections and records")); - - // --arch-specific - cl::opt<bool> ArchSpecificInfo("arch-specific", - cl::desc("Displays architecture-specific information, if there is any.")); - cl::alias ArchSpecifcInfoShort("A", cl::desc("Alias for --arch-specific"), - cl::aliasopt(ArchSpecificInfo), cl::NotHidden); - - // --coff-imports - cl::opt<bool> - COFFImports("coff-imports", cl::desc("Display the PE/COFF import table")); - - // --coff-exports - cl::opt<bool> - COFFExports("coff-exports", cl::desc("Display the PE/COFF export table")); - - // --coff-directives - cl::opt<bool> - COFFDirectives("coff-directives", - cl::desc("Display the PE/COFF .drectve section")); - - // --coff-basereloc - cl::opt<bool> - COFFBaseRelocs("coff-basereloc", - cl::desc("Display the PE/COFF .reloc section")); - - // --coff-debug-directory - cl::opt<bool> - COFFDebugDirectory("coff-debug-directory", - cl::desc("Display the PE/COFF debug directory")); - - // --coff-tls-directory - cl::opt<bool> COFFTLSDirectory("coff-tls-directory", - cl::desc("Display the PE/COFF TLS directory")); - - // --coff-resources - cl::opt<bool> COFFResources("coff-resources", - cl::desc("Display the PE/COFF .rsrc section")); - - // --coff-load-config - cl::opt<bool> - COFFLoadConfig("coff-load-config", - cl::desc("Display the PE/COFF load config")); - - // --elf-linker-options - cl::opt<bool> - ELFLinkerOptions("elf-linker-options", - cl::desc("Display the ELF .linker-options section")); - - // --macho-data-in-code - cl::opt<bool> - MachODataInCode("macho-data-in-code", - cl::desc("Display MachO Data in Code command")); - - // --macho-indirect-symbols - cl::opt<bool> - MachOIndirectSymbols("macho-indirect-symbols", - cl::desc("Display MachO indirect symbols")); - - // --macho-linker-options - cl::opt<bool> - MachOLinkerOptions("macho-linker-options", - cl::desc("Display MachO linker options")); - - // --macho-segment - cl::opt<bool> - MachOSegment("macho-segment", - cl::desc("Display MachO Segment command")); - - // --macho-version-min - cl::opt<bool> - MachOVersionMin("macho-version-min", - cl::desc("Display MachO version min command")); - - // --macho-dysymtab - cl::opt<bool> - MachODysymtab("macho-dysymtab", - cl::desc("Display MachO Dysymtab command")); - - // --stackmap - cl::opt<bool> - PrintStackMap("stackmap", - cl::desc("Display contents of stackmap section")); - - // --stack-sizes - cl::opt<bool> - PrintStackSizes("stack-sizes", - cl::desc("Display contents of all stack sizes sections")); - - // --version-info, -V - cl::opt<bool> - VersionInfo("version-info", - cl::desc("Display ELF version sections (if present)")); - cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"), - cl::aliasopt(VersionInfo)); - - // --elf-section-groups, --section-groups, -g - cl::opt<bool> SectionGroups("elf-section-groups", - cl::desc("Display ELF section group contents")); - cl::alias SectionGroupsAlias("section-groups", - cl::desc("Alias for -elf-sections-groups"), - cl::aliasopt(SectionGroups)); - cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"), - cl::aliasopt(SectionGroups)); - - // --elf-hash-histogram, --histogram, -I - cl::opt<bool> HashHistogram( - "elf-hash-histogram", - cl::desc("Display bucket list histogram for hash sections")); - cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"), - cl::aliasopt(HashHistogram)); - cl::alias HistogramAlias("histogram", - cl::desc("Alias for --elf-hash-histogram"), - cl::aliasopt(HashHistogram)); - - // --cg-profile - cl::opt<bool> CGProfile("cg-profile", - cl::desc("Display callgraph profile section")); - cl::alias ELFCGProfile("elf-cg-profile", cl::desc("Alias for --cg-profile"), - cl::aliasopt(CGProfile)); - - // -addrsig - cl::opt<bool> Addrsig("addrsig", - cl::desc("Display address-significance table")); - - // -elf-output-style - cl::opt<OutputStyleTy> - Output("elf-output-style", cl::desc("Specify ELF dump style"), - cl::values(clEnumVal(LLVM, "LLVM default style"), - clEnumVal(GNU, "GNU readelf style")), - cl::init(LLVM)); - - cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); +OutputStyleTy Output = OutputStyleTy::LLVM; +static std::vector<std::string> InputFilenames; } // namespace opts static StringRef ToolName; @@ -419,6 +190,87 @@ } // namespace llvm +static void parseOptions(const opt::InputArgList &Args) { + opts::Addrsig = Args.hasArg(OPT_addrsig); + opts::All = Args.hasArg(OPT_all); + opts::ArchSpecificInfo = Args.hasArg(OPT_arch_specific); + opts::BBAddrMap = Args.hasArg(OPT_bb_addr_map); + opts::CGProfile = Args.hasArg(OPT_cg_profile); + opts::Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false); + opts::DependentLibraries = Args.hasArg(OPT_dependent_libraries); + opts::DynRelocs = Args.hasArg(OPT_dyn_relocations); + opts::DynamicSymbols = Args.hasArg(OPT_dyn_syms); + opts::ExpandRelocs = Args.hasArg(OPT_expand_relocs); + opts::FileHeaders = Args.hasArg(OPT_file_header); + opts::Headers = Args.hasArg(OPT_headers); + opts::HexDump = Args.getAllArgValues(OPT_hex_dump_EQ); + opts::Relocations = Args.hasArg(OPT_relocs); + opts::SectionData = Args.hasArg(OPT_section_data); + opts::SectionDetails = Args.hasArg(OPT_section_details); + opts::SectionHeaders = Args.hasArg(OPT_section_headers); + opts::SectionRelocations = Args.hasArg(OPT_section_relocations); + opts::SectionSymbols = Args.hasArg(OPT_section_symbols); + if (Args.hasArg(OPT_section_mapping)) + opts::SectionMapping = cl::BOU_TRUE; + else if (Args.hasArg(OPT_section_mapping_EQ_false)) + opts::SectionMapping = cl::BOU_FALSE; + else + opts::SectionMapping = cl::BOU_UNSET; + opts::PrintStackSizes = Args.hasArg(OPT_stack_sizes); + opts::PrintStackMap = Args.hasArg(OPT_stackmap); + opts::StringDump = Args.getAllArgValues(OPT_string_dump_EQ); + opts::StringTable = Args.hasArg(OPT_string_table); + opts::Symbols = Args.hasArg(OPT_symbols); + opts::UnwindInfo = Args.hasArg(OPT_unwind); + + // ELF specific options. + opts::DynamicTable = Args.hasArg(OPT_dynamic_table); + opts::ELFLinkerOptions = Args.hasArg(OPT_elf_linker_options); + if (Arg *A = Args.getLastArg(OPT_elf_output_style_EQ)) { + StringRef V(A->getValue()); + if (V == "LLVM") + opts::Output = opts::OutputStyleTy::LLVM; + else if (V == "GNU") + opts::Output = opts::OutputStyleTy::GNU; + else + error("--elf-output-style value should be either 'LLVM' or 'GNU'"); + } + opts::GnuHashTable = Args.hasArg(OPT_gnu_hash_table); + opts::HashSymbols = Args.hasArg(OPT_hash_symbols); + opts::HashTable = Args.hasArg(OPT_hash_table); + opts::HashHistogram = Args.hasArg(OPT_histogram); + opts::NeededLibraries = Args.hasArg(OPT_needed_libs); + opts::Notes = Args.hasArg(OPT_notes); + opts::ProgramHeaders = Args.hasArg(OPT_program_headers); + opts::RawRelr = Args.hasArg(OPT_raw_relr); + opts::SectionGroups = Args.hasArg(OPT_section_groups); + opts::VersionInfo = Args.hasArg(OPT_version_info); + + // Mach-O specific options. + opts::MachODataInCode = Args.hasArg(OPT_macho_data_in_code); + opts::MachODysymtab = Args.hasArg(OPT_macho_dysymtab); + opts::MachOIndirectSymbols = Args.hasArg(OPT_macho_indirect_symbols); + opts::MachOLinkerOptions = Args.hasArg(OPT_macho_linker_options); + opts::MachOSegment = Args.hasArg(OPT_macho_segment); + opts::MachOVersionMin = Args.hasArg(OPT_macho_version_min); + + // PE/COFF specific options. + opts::CodeView = Args.hasArg(OPT_codeview); + opts::CodeViewEnableGHash = Args.hasArg(OPT_codeview_ghash); + opts::CodeViewMergedTypes = Args.hasArg(OPT_codeview_merged_types); + opts::CodeViewSubsectionBytes = Args.hasArg(OPT_codeview_subsection_bytes); + opts::COFFBaseRelocs = Args.hasArg(OPT_coff_basereloc); + opts::COFFDebugDirectory = Args.hasArg(OPT_coff_debug_directory); + opts::COFFDirectives = Args.hasArg(OPT_coff_directives); + opts::COFFExports = Args.hasArg(OPT_coff_exports); + opts::COFFImports = Args.hasArg(OPT_coff_imports); + opts::COFFLoadConfig = Args.hasArg(OPT_coff_load_config); + opts::COFFResources = Args.hasArg(OPT_coff_resources); + opts::COFFTLSDirectory = Args.hasArg(OPT_coff_tls_directory); + + opts::InputFilenames = Args.getAllArgValues(OPT_INPUT); +} + namespace { struct ReadObjTypeTableBuilder { ReadObjTypeTableBuilder() @@ -529,6 +381,8 @@ Dumper->printGnuHashTable(); if (opts::VersionInfo) Dumper->printVersionInfo(); + if (opts::StringTable) + Dumper->printStringTable(); if (Obj.isELF()) { if (opts::DependentLibraries) Dumper->printDependentLibs(); @@ -542,6 +396,8 @@ Dumper->printHashHistograms(); if (opts::CGProfile) Dumper->printCGProfile(); + if (opts::BBAddrMap) + Dumper->printBBAddrMaps(); if (opts::Addrsig) Dumper->printAddrsig(); if (opts::Notes) @@ -646,93 +502,73 @@ /// Opens \a File and dumps it. static void dumpInput(StringRef File, ScopedPrinter &Writer) { - // Attempt to open the binary. - Expected<OwningBinary<Binary>> BinaryOrErr = - createBinary(File, /*Context=*/nullptr, /*InitContent=*/false); + ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = + MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false, + /*RequiresNullTerminator=*/false); + if (std::error_code EC = FileOrErr.getError()) + return reportError(errorCodeToError(EC), File); + + std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get(); + file_magic Type = identify_magic(Buffer->getBuffer()); + if (Type == file_magic::bitcode) { + reportWarning(createStringError(errc::invalid_argument, + "bitcode files are not supported"), + File); + return; + } + + Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary( + Buffer->getMemBufferRef(), /*Context=*/nullptr, /*InitContent=*/false); if (!BinaryOrErr) reportError(BinaryOrErr.takeError(), File); - Binary &Binary = *BinaryOrErr.get().getBinary(); - if (Archive *Arc = dyn_cast<Archive>(&Binary)) + std::unique_ptr<Binary> Bin = std::move(*BinaryOrErr); + if (Archive *Arc = dyn_cast<Archive>(Bin.get())) dumpArchive(Arc, Writer); else if (MachOUniversalBinary *UBinary = - dyn_cast<MachOUniversalBinary>(&Binary)) + dyn_cast<MachOUniversalBinary>(Bin.get())) dumpMachOUniversalBinary(UBinary, Writer); - else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) + else if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin.get())) dumpObject(*Obj, Writer); - else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(&Binary)) + else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(Bin.get())) dumpCOFFImportFile(Import, Writer); - else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(&Binary)) + else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(Bin.get())) dumpWindowsResourceFile(WinRes, Writer); else llvm_unreachable("unrecognized file type"); - CVTypes.Binaries.push_back(std::move(*BinaryOrErr)); + CVTypes.Binaries.push_back( + OwningBinary<Binary>(std::move(Bin), std::move(Buffer))); } -/// Registers aliases that should only be allowed by readobj. -static void registerReadobjAliases() { - // -s has meant --sections for a very long time in llvm-readobj despite - // meaning --symbols in readelf. - static cl::alias SectionsShort("s", cl::desc("Alias for --section-headers"), - cl::aliasopt(opts::SectionHeaders), - cl::NotHidden); - - // llvm-readelf reserves it for --section-details. - static cl::alias SymbolsShort("t", cl::desc("Alias for --symbols"), - cl::aliasopt(opts::Symbols), cl::NotHidden); - - // The following two-letter aliases are only provided for readobj, as readelf - // allows single-letter args to be grouped together. - static cl::alias SectionRelocationsShort( - "sr", cl::desc("Alias for --section-relocations"), - cl::aliasopt(opts::SectionRelocations)); - static cl::alias SectionDataShort("sd", cl::desc("Alias for --section-data"), - cl::aliasopt(opts::SectionData)); - static cl::alias SectionSymbolsShort("st", - cl::desc("Alias for --section-symbols"), - cl::aliasopt(opts::SectionSymbols)); - static cl::alias DynamicSymbolsShort("dt", - cl::desc("Alias for --dyn-symbols"), - cl::aliasopt(opts::DynamicSymbols)); -} - -/// Registers aliases that should only be allowed by readelf. -static void registerReadelfAliases() { - // -s is here because for readobj it means --sections. - static cl::alias SymbolsShort("s", cl::desc("Alias for --symbols"), - cl::aliasopt(opts::Symbols), cl::NotHidden, - cl::Grouping); - - // -t is here because for readobj it is an alias for --symbols. - static cl::alias SectionDetailsShort( - "t", cl::desc("Alias for --section-details"), - cl::aliasopt(opts::SectionDetails), cl::NotHidden); - - // Allow all single letter flags to be grouped together. - for (auto &OptEntry : cl::getRegisteredOptions()) { - StringRef ArgName = OptEntry.getKey(); - cl::Option *Option = OptEntry.getValue(); - if (ArgName.size() == 1) - apply(Option, cl::Grouping); - } -} - -int main(int argc, const char *argv[]) { +int main(int argc, char *argv[]) { InitLLVM X(argc, argv); + BumpPtrAllocator A; + StringSaver Saver(A); + ReadobjOptTable Tbl; ToolName = argv[0]; - - // Register the target printer for --version. - cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); - - if (sys::path::stem(argv[0]).contains("readelf")) { - opts::Output = opts::GNU; - registerReadelfAliases(); - } else { - registerReadobjAliases(); + opt::InputArgList Args = + Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { + error(Msg); + exit(1); + }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp( + outs(), + (Twine(ToolName) + " [options] <input object files>").str().c_str(), + "LLVM Object Reader"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + cl::PrintVersionMessage(); + return 0; } - cl::ParseCommandLineOptions(argc, argv, "LLVM Object Reader\n"); + if (sys::path::stem(argv[0]).contains("readelf")) + opts::Output = opts::GNU; + parseOptions(Args); // Default to print error if no filename is specified. if (opts::InputFilenames.empty()) {
diff --git a/src/llvm-project/llvm/tools/llvm-readobj/llvm-readobj.h b/src/llvm-project/llvm/tools/llvm-readobj/llvm-readobj.h index d9813f5..43d19b4 100644 --- a/src/llvm-project/llvm/tools/llvm-readobj/llvm-readobj.h +++ b/src/llvm-project/llvm/tools/llvm-readobj/llvm-readobj.h
@@ -32,15 +32,15 @@ } // namespace llvm namespace opts { - extern llvm::cl::opt<bool> SectionRelocations; - extern llvm::cl::opt<bool> SectionSymbols; - extern llvm::cl::opt<bool> SectionData; - extern llvm::cl::opt<bool> ExpandRelocs; - extern llvm::cl::opt<bool> RawRelr; - extern llvm::cl::opt<bool> CodeViewSubsectionBytes; - extern llvm::cl::opt<bool> Demangle; - enum OutputStyleTy { LLVM, GNU }; - extern llvm::cl::opt<OutputStyleTy> Output; +extern bool SectionRelocations; +extern bool SectionSymbols; +extern bool SectionData; +extern bool ExpandRelocs; +extern bool RawRelr; +extern bool CodeViewSubsectionBytes; +extern bool Demangle; +enum OutputStyleTy { LLVM, GNU }; +extern OutputStyleTy Output; } // namespace opts #define LLVM_READOBJ_ENUM_ENT(ns, enum) \
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-reduce/CMakeLists.txt index b6db920..44818ed 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-reduce/CMakeLists.txt
@@ -11,6 +11,7 @@ ) add_llvm_tool(llvm-reduce + DeltaManager.cpp TestRunner.cpp deltas/Delta.cpp deltas/ReduceAliases.cpp @@ -19,10 +20,12 @@ deltas/ReduceBasicBlocks.cpp deltas/ReduceFunctionBodies.cpp deltas/ReduceFunctions.cpp + deltas/ReduceGlobalValues.cpp deltas/ReduceGlobalVarInitializers.cpp deltas/ReduceGlobalVars.cpp deltas/ReduceInstructions.cpp deltas/ReduceMetadata.cpp + deltas/ReduceModuleInlineAsm.cpp deltas/ReduceOperandBundles.cpp deltas/ReduceSpecialGlobals.cpp llvm-reduce.cpp
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/DeltaManager.cpp b/src/llvm-project/llvm/tools/llvm-reduce/DeltaManager.cpp new file mode 100644 index 0000000..19e9913 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-reduce/DeltaManager.cpp
@@ -0,0 +1,92 @@ +//===- DeltaManager.cpp - Runs Delta Passes to reduce Input ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file calls each specialized Delta pass in order to reduce the input IR +// file. +// +//===----------------------------------------------------------------------===// + +#include "DeltaManager.h" +#include "TestRunner.h" +#include "deltas/Delta.h" +#include "deltas/ReduceAliases.h" +#include "deltas/ReduceArguments.h" +#include "deltas/ReduceAttributes.h" +#include "deltas/ReduceBasicBlocks.h" +#include "deltas/ReduceFunctionBodies.h" +#include "deltas/ReduceFunctions.h" +#include "deltas/ReduceGlobalValues.h" +#include "deltas/ReduceGlobalVarInitializers.h" +#include "deltas/ReduceGlobalVars.h" +#include "deltas/ReduceInstructions.h" +#include "deltas/ReduceMetadata.h" +#include "deltas/ReduceModuleInlineAsm.h" +#include "deltas/ReduceOperandBundles.h" +#include "deltas/ReduceSpecialGlobals.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; + +static cl::opt<std::string> + DeltaPasses("delta-passes", + cl::desc("Delta passes to run, separated by commas. By " + "default, run all delta passes.")); + +#define DELTA_PASSES \ + DELTA_PASS("special-globals", reduceSpecialGlobalsDeltaPass) \ + DELTA_PASS("aliases", reduceAliasesDeltaPass) \ + DELTA_PASS("function-bodies", reduceFunctionBodiesDeltaPass) \ + DELTA_PASS("functions", reduceFunctionsDeltaPass) \ + DELTA_PASS("basic-blocks", reduceBasicBlocksDeltaPass) \ + DELTA_PASS("global-values", reduceGlobalValuesDeltaPass) \ + DELTA_PASS("global-initializers", reduceGlobalsInitializersDeltaPass) \ + DELTA_PASS("global-variables", reduceGlobalsDeltaPass) \ + DELTA_PASS("metadata", reduceMetadataDeltaPass) \ + DELTA_PASS("arguments", reduceArgumentsDeltaPass) \ + DELTA_PASS("instructions", reduceInstructionsDeltaPass) \ + DELTA_PASS("operand-bundles", reduceOperandBundesDeltaPass) \ + DELTA_PASS("attributes", reduceAttributesDeltaPass) \ + DELTA_PASS("module-inline-asm", reduceModuleInlineAsmDeltaPass) + +static void runAllDeltaPasses(TestRunner &Tester) { +#define DELTA_PASS(NAME, FUNC) FUNC(Tester); + DELTA_PASSES +#undef DELTA_PASS +} + +static void runDeltaPassName(TestRunner &Tester, StringRef PassName) { +#define DELTA_PASS(NAME, FUNC) \ + if (PassName == NAME) { \ + FUNC(Tester); \ + return; \ + } + DELTA_PASSES +#undef DELTA_PASS + errs() << "unknown pass \"" << PassName << "\""; + exit(1); +} + +void llvm::printDeltaPasses(raw_ostream &OS) { + OS << "Delta passes (pass to `--delta-passes=` as a comma separated list):\n"; +#define DELTA_PASS(NAME, FUNC) OS << " " << NAME << "\n"; + DELTA_PASSES +#undef DELTA_PASS +} + +void llvm::runDeltaPasses(TestRunner &Tester) { + if (DeltaPasses.empty()) { + runAllDeltaPasses(Tester); + } else { + StringRef Passes = DeltaPasses; + while (!Passes.empty()) { + auto Split = Passes.split(","); + runDeltaPassName(Tester, Split.first); + Passes = Split.second; + } + } +}
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/DeltaManager.h b/src/llvm-project/llvm/tools/llvm-reduce/DeltaManager.h index 18a6b0d..8fb69be 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/DeltaManager.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/DeltaManager.h
@@ -11,38 +11,15 @@ // //===----------------------------------------------------------------------===// -#include "TestRunner.h" -#include "deltas/Delta.h" -#include "deltas/ReduceAliases.h" -#include "deltas/ReduceArguments.h" -#include "deltas/ReduceAttributes.h" -#include "deltas/ReduceBasicBlocks.h" -#include "deltas/ReduceFunctionBodies.h" -#include "deltas/ReduceFunctions.h" -#include "deltas/ReduceGlobalVarInitializers.h" -#include "deltas/ReduceGlobalVars.h" -#include "deltas/ReduceInstructions.h" -#include "deltas/ReduceMetadata.h" -#include "deltas/ReduceOperandBundles.h" -#include "deltas/ReduceSpecialGlobals.h" +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAMANAGER_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAMANAGER_H namespace llvm { +class raw_ostream; +class TestRunner; -// TODO: Add CLI option to run only specified Passes (for unit tests) -inline void runDeltaPasses(TestRunner &Tester) { - reduceSpecialGlobalsDeltaPass(Tester); - reduceAliasesDeltaPass(Tester); - reduceFunctionBodiesDeltaPass(Tester); - reduceFunctionsDeltaPass(Tester); - reduceBasicBlocksDeltaPass(Tester); - reduceGlobalsInitializersDeltaPass(Tester); - reduceGlobalsDeltaPass(Tester); - reduceMetadataDeltaPass(Tester); - reduceArgumentsDeltaPass(Tester); - reduceInstructionsDeltaPass(Tester); - reduceOperandBundesDeltaPass(Tester); - reduceAttributesDeltaPass(Tester); - // TODO: Implement the remaining Delta Passes -} - +void printDeltaPasses(raw_ostream &OS); +void runDeltaPasses(TestRunner &Tester); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/TestRunner.h b/src/llvm-project/llvm/tools/llvm-reduce/TestRunner.h index 2270d6b..b7fb44e 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/TestRunner.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/TestRunner.h
@@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_LLVMREDUCE_TESTRUNNER_H -#define LLVM_TOOLS_LLVMREDUCE_TESTRUNNER_H +#ifndef LLVM_TOOLS_LLVM_REDUCE_TESTRUNNER_H +#define LLVM_TOOLS_LLVM_REDUCE_TESTRUNNER_H #include "llvm/ADT/SmallString.h" #include "llvm/IR/Module.h"
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/Delta.cpp b/src/llvm-project/llvm/tools/llvm-reduce/deltas/Delta.cpp index 9b0969e..1059764 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/Delta.cpp +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/Delta.cpp
@@ -15,6 +15,7 @@ #include "Delta.h" #include "llvm/ADT/STLExtras.h" #include "llvm/IR/Verifier.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Transforms/Utils/Cloning.h" #include <fstream> @@ -22,9 +23,13 @@ using namespace llvm; +static cl::opt<bool> AbortOnInvalidReduction( + "abort-on-invalid-reduction", + cl::desc("Abort if any reduction results in invalid IR")); + void writeOutput(llvm::Module *M, llvm::StringRef Message); -bool IsReduced(Module &M, TestRunner &Test, SmallString<128> &CurrentFilepath) { +bool isReduced(Module &M, TestRunner &Test, SmallString<128> &CurrentFilepath) { // Write Module to tmp file int FD; std::error_code EC = @@ -66,12 +71,12 @@ bool SplitOne = false; for (auto &C : Chunks) { - if (C.end - C.begin == 0) + if (C.End - C.Begin == 0) NewChunks.push_back(C); else { - int Half = (C.begin + C.end) / 2; - NewChunks.push_back({C.begin, Half}); - NewChunks.push_back({Half + 1, C.end}); + int Half = (C.Begin + C.End) / 2; + NewChunks.push_back({C.Begin, Half}); + NewChunks.push_back({Half + 1, C.End}); SplitOne = true; } } @@ -102,7 +107,7 @@ if (Module *Program = Test.getProgram()) { SmallString<128> CurrentFilepath; - if (!IsReduced(*Program, Test, CurrentFilepath)) { + if (!isReduced(*Program, Test, CurrentFilepath)) { errs() << "\nInput isn't interesting! Verify interesting-ness test\n"; exit(1); } @@ -141,6 +146,10 @@ // Some reductions may result in invalid IR. Skip such reductions. if (verifyModule(*Clone.get(), &errs())) { + if (AbortOnInvalidReduction) { + errs() << "Invalid reduction\n"; + exit(1); + } errs() << " **** WARNING | reduction resulted in invalid module, " "skipping\n"; continue; @@ -152,7 +161,7 @@ C.print(); SmallString<128> CurrentFilepath; - if (!IsReduced(*Clone, Test, CurrentFilepath)) { + if (!isReduced(*Clone, Test, CurrentFilepath)) { // Program became non-reduced, so this chunk appears to be interesting. errs() << "\n"; continue;
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/Delta.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/Delta.h index 7da3c79..69bd4d2 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/Delta.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/Delta.h
@@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_LLVMREDUCE_LLVMREDUCE_DELTA_H -#define LLVM_TOOLS_LLVMREDUCE_LLVMREDUCE_DELTA_H +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_DELTA_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_DELTA_H #include "TestRunner.h" #include "llvm/ADT/ScopeExit.h" @@ -24,27 +24,27 @@ namespace llvm { struct Chunk { - int begin; - int end; + int Begin; + int End; /// Helper function to verify if a given Target-index is inside the Chunk - bool contains(int Index) const { return Index >= begin && Index <= end; } + bool contains(int Index) const { return Index >= Begin && Index <= End; } void print() const { - errs() << "[" << begin; - if (end - begin != 0) - errs() << "," << end; + errs() << "[" << Begin; + if (End - Begin != 0) + errs() << "," << End; errs() << "]"; } /// Operator when populating CurrentChunks in Generic Delta Pass friend bool operator!=(const Chunk &C1, const Chunk &C2) { - return C1.begin != C2.begin || C1.end != C2.end; + return C1.Begin != C2.Begin || C1.End != C2.End; } /// Operator used for sets friend bool operator<(const Chunk &C1, const Chunk &C2) { - return std::tie(C1.begin, C1.end) < std::tie(C2.begin, C2.end); + return std::tie(C1.Begin, C1.End) < std::tie(C2.Begin, C2.End); } }; @@ -60,8 +60,7 @@ ArrayRef<Chunk> ChunksToKeep; public: - explicit Oracle(ArrayRef<Chunk> ChunksToKeep_) - : ChunksToKeep(ChunksToKeep_) {} + explicit Oracle(ArrayRef<Chunk> ChunksToKeep) : ChunksToKeep(ChunksToKeep) {} /// Should be called for each feature on which we are operating. /// Name is self-explanatory - if returns true, then it should be preserved. @@ -74,7 +73,7 @@ auto _ = make_scope_exit([&]() { ++Index; }); // Next time - next feature. // Is this the last feature in the chunk? - if (ChunksToKeep.front().end == Index) + if (ChunksToKeep.front().End == Index) ChunksToKeep = ChunksToKeep.drop_front(); // Onto next chunk. return ShouldKeep;
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAliases.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAliases.h index 0c2886e..0660efe 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAliases.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAliases.h
@@ -11,8 +11,13 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEALIASES_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEALIASES_H + #include "Delta.h" namespace llvm { void reduceAliasesDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceArguments.cpp b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceArguments.cpp index c3c7dee..a88bba4 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceArguments.cpp +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceArguments.cpp
@@ -7,13 +7,14 @@ //===----------------------------------------------------------------------===// // // This file implements a function which calls the Generic Delta pass in order -// to reduce uninteresting Arguments from defined functions. +// to reduce uninteresting Arguments from declared and defined functions. // //===----------------------------------------------------------------------===// #include "ReduceArguments.h" #include "Delta.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Intrinsics.h" #include <set> #include <vector> @@ -38,6 +39,14 @@ } } +/// Returns whether or not this function should be considered a candidate for +/// argument removal. Currently, functions with no arguments and intrinsics are +/// not considered. Intrinsics aren't considered because their signatures are +/// fixed. +static bool shouldRemoveArguments(const Function &F) { + return !F.arg_empty() && !F.isIntrinsic(); +} + /// Removes out-of-chunk arguments from functions, and modifies their calls /// accordingly. It also removes allocations of out-of-chunk arguments. static void extractArgumentsFromModule(std::vector<Chunk> ChunksToKeep, @@ -48,7 +57,7 @@ std::vector<Function *> Funcs; // Get inside-chunk arguments, as well as their parent function for (auto &F : *Program) - if (!F.arg_empty()) { + if (shouldRemoveArguments(F)) { Funcs.push_back(&F); for (auto &A : F.args()) if (O.shouldKeep()) @@ -100,15 +109,15 @@ } } -/// Counts the amount of arguments in non-declaration functions and prints their -/// respective name, index, and parent function name +/// Counts the amount of arguments in functions and prints their respective +/// name, index, and parent function name static int countArguments(Module *Program) { // TODO: Silence index with --quiet flag outs() << "----------------------------\n"; outs() << "Param Index Reference:\n"; int ArgsCount = 0; for (auto &F : *Program) - if (!F.arg_empty()) { + if (shouldRemoveArguments(F)) { outs() << " " << F.getName() << "\n"; for (auto &A : F.args()) outs() << "\t" << ++ArgsCount << ": " << A.getName() << "\n";
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceArguments.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceArguments.h index d9682b4..228409d 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceArguments.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceArguments.h
@@ -11,6 +11,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEARGUMENTS_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEARGUMENTS_H + #include "Delta.h" #include "llvm/IR/Argument.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" @@ -19,3 +22,5 @@ namespace llvm { void reduceArgumentsDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp index cbaf5d5..223866b 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp
@@ -1,4 +1,4 @@ -//===- ReduceAttributes.cpp - Specialized Delta Pass -------------------===// +//===- ReduceAttributes.cpp - Specialized Delta Pass ----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -84,7 +84,8 @@ AttrPtrVecVecTy &AttributeSetsToPreserve) { assert(AttributeSetsToPreserve.empty() && "Should not be sharing vectors."); AttributeSetsToPreserve.reserve(AL.getNumAttrSets()); - for (unsigned SetIdx : seq(AL.index_begin(), AL.index_end())) { + for (unsigned SetIdx = AL.index_begin(), SetEndIdx = AL.index_end(); + SetIdx != SetEndIdx; ++SetIdx) { AttrPtrIdxVecVecTy AttributesToPreserve; AttributesToPreserve.first = SetIdx; visitAttributeSet(AL.getAttributes(AttributesToPreserve.first),
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAttributes.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAttributes.h index f8deb04..375e764 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAttributes.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceAttributes.h
@@ -11,10 +11,12 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEATTRIBUTES_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEATTRIBUTES_H + namespace llvm { - class TestRunner; - void reduceAttributesDeltaPass(TestRunner &Test); - } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp index 8c0832d..5a1905d 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp
@@ -26,9 +26,9 @@ /// Replaces BB Terminator with one that only contains Chunk BBs static void replaceBranchTerminator(BasicBlock &BB, std::set<BasicBlock *> BBsToKeep) { - auto Term = BB.getTerminator(); + auto *Term = BB.getTerminator(); std::vector<BasicBlock *> ChunkSucessors; - for (auto Succ : successors(&BB)) + for (auto *Succ : successors(&BB)) if (BBsToKeep.count(Succ)) ChunkSucessors.push_back(Succ); @@ -38,7 +38,7 @@ bool IsBranch = isa<BranchInst>(Term) || isa<InvokeInst>(Term); Value *Address = nullptr; - if (auto IndBI = dyn_cast<IndirectBrInst>(Term)) + if (auto *IndBI = dyn_cast<IndirectBrInst>(Term)) Address = IndBI->getAddress(); Term->replaceAllUsesWith(UndefValue::get(Term->getType())); @@ -56,9 +56,9 @@ BranchInst::Create(ChunkSucessors[0], &BB); if (Address) { - auto NewIndBI = + auto *NewIndBI = IndirectBrInst::Create(Address, ChunkSucessors.size(), &BB); - for (auto Dest : ChunkSucessors) + for (auto *Dest : ChunkSucessors) NewIndBI->addDestination(Dest); } }
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.h index cf76a0a..4938552 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.h
@@ -10,6 +10,8 @@ // to reduce uninteresting Arguments from defined functions. // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEBASICBLOCKS_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEBASICBLOCKS_H #include "Delta.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" @@ -18,3 +20,5 @@ namespace llvm { void reduceBasicBlocksDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceFunctionBodies.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceFunctionBodies.h index 8c06c2e..bfe701b 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceFunctionBodies.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceFunctionBodies.h
@@ -11,8 +11,13 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEFUNCTIONBODIES_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEFUNCTIONBODIES_H + #include "Delta.h" namespace llvm { void reduceFunctionBodiesDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceFunctions.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceFunctions.h index 7c2cd3f..f5bc83b 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceFunctions.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceFunctions.h
@@ -11,6 +11,8 @@ // Module. // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEFUNCTIONS_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEFUNCTIONS_H #include "Delta.h" #include "llvm/Transforms/Utils/Cloning.h" @@ -18,3 +20,5 @@ namespace llvm { void reduceFunctionsDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalValues.cpp b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalValues.cpp new file mode 100644 index 0000000..4d918aa --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalValues.cpp
@@ -0,0 +1,54 @@ +//===- ReduceGlobalValues.cpp - Specialized Delta Pass --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass to reduce +// global value attributes/specifiers. +// +//===----------------------------------------------------------------------===// + +#include "ReduceGlobalValues.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalValue.h" + +using namespace llvm; + +static bool isValidDSOLocalReductionGV(GlobalValue &GV) { + return GV.isDSOLocal() && !GV.isImplicitDSOLocal(); +} + +/// Sets dso_local to false for all global values. +static void extractGVsFromModule(std::vector<Chunk> ChunksToKeep, + Module *Program) { + Oracle O(ChunksToKeep); + + // remove dso_local from global values + for (auto &GV : Program->global_values()) + if (isValidDSOLocalReductionGV(GV) && !O.shouldKeep()) { + GV.setDSOLocal(false); + } +} + +/// Counts the amount of global values with dso_local and displays their +/// respective name & index +static int countGVs(Module *Program) { + // TODO: Silence index with --quiet flag + outs() << "----------------------------\n"; + outs() << "GlobalValue Index Reference:\n"; + int GVCount = 0; + for (auto &GV : Program->global_values()) + if (isValidDSOLocalReductionGV(GV)) + outs() << "\t" << ++GVCount << ": " << GV.getName() << "\n"; + outs() << "----------------------------\n"; + return GVCount; +} + +void llvm::reduceGlobalValuesDeltaPass(TestRunner &Test) { + outs() << "*** Reducing GlobalValues...\n"; + int GVCount = countGVs(Test.getProgram()); + runDeltaPass(Test, GVCount, extractGVsFromModule); +}
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalValues.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalValues.h new file mode 100644 index 0000000..ea32a6c --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalValues.h
@@ -0,0 +1,23 @@ +//===- ReduceGlobalValues.h - Specialized Delta Pass ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass to reduce +// global value attributes/specifiers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVALUES_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVALUES_H + +#include "Delta.h" + +namespace llvm { +void reduceGlobalValuesDeltaPass(TestRunner &Test); +} // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.h index 39288ad..0f5f22a 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.h
@@ -1,5 +1,4 @@ -//===- reduceGlobalsInitializersDeltaPass.h - Specialized Delta Pass -//-------===// +//===- reduceGlobalsInitializersDeltaPass.h - Specialized Delta Pass ------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,6 +11,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVARINITIALIZERS_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVARINITIALIZERS_H + #include "Delta.h" #include "llvm/IR/Value.h" #include "llvm/Transforms/Utils/Cloning.h" @@ -19,3 +21,5 @@ namespace llvm { void reduceGlobalsInitializersDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp index 4b184e5..525eff9 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp
@@ -33,7 +33,7 @@ std::vector<WeakVH> InstToRemove; for (auto &GV : Program->globals()) if (!GVsToKeep.count(&GV)) { - for (auto U : GV.users()) + for (auto *U : GV.users()) if (auto *Inst = dyn_cast<Instruction>(U)) InstToRemove.push_back(Inst);
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.h index c8ba7ea..fe7813c 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.h
@@ -11,6 +11,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVARS_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVARS_H + #include "Delta.h" #include "llvm/IR/Value.h" #include "llvm/Transforms/Utils/Cloning.h" @@ -18,3 +21,5 @@ namespace llvm { void reduceGlobalsDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceInstructions.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceInstructions.h index a9266ac..be568f1 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceInstructions.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceInstructions.h
@@ -11,6 +11,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEINSTRUCTIONS_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEINSTRUCTIONS_H + #include "Delta.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" @@ -18,3 +21,5 @@ namespace llvm { void reduceInstructionsDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceMetadata.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceMetadata.h index 275b44c..6efc3f5 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceMetadata.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceMetadata.h
@@ -1,4 +1,4 @@ -//===- ReduceMetadata.h - Specialized Delta Pass ------------------------===// +//===- ReduceMetadata.h - Specialized Delta Pass --------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,8 +11,13 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEMETADATA_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEMETADATA_H + #include "TestRunner.h" namespace llvm { void reduceMetadataDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceModuleInlineAsm.cpp b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceModuleInlineAsm.cpp new file mode 100644 index 0000000..b74bd0a --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceModuleInlineAsm.cpp
@@ -0,0 +1,32 @@ +//===- ReduceModuleInlineAsm.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass to reduce +// module inline asm. +// +//===----------------------------------------------------------------------===// + +#include "ReduceModuleInlineAsm.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalValue.h" + +using namespace llvm; + +static void clearModuleInlineAsm(std::vector<Chunk> ChunksToKeep, + Module *Program) { + Oracle O(ChunksToKeep); + + // TODO: clear line by line rather than all at once + if (!O.shouldKeep()) + Program->setModuleInlineAsm(""); +} + +void llvm::reduceModuleInlineAsmDeltaPass(TestRunner &Test) { + outs() << "*** Reducing Module Inline Asm...\n"; + runDeltaPass(Test, 1, clearModuleInlineAsm); +}
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceModuleInlineAsm.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceModuleInlineAsm.h new file mode 100644 index 0000000..6d31c8f --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceModuleInlineAsm.h
@@ -0,0 +1,18 @@ +//===- ReduceModuleInlineAsm.h --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCE_MODULEINLINEASM_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCE_MODULEINLINEASM_H + +#include "Delta.h" + +namespace llvm { +void reduceModuleInlineAsmDeltaPass(TestRunner &Test); +} // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.h index 382c5cb..d07e021 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.h
@@ -11,10 +11,12 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDBUNDLES_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDBUNDLES_H + namespace llvm { - class TestRunner; - void reduceOperandBundesDeltaPass(TestRunner &Test); - } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceSpecialGlobals.h b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceSpecialGlobals.h index 52ecaed..c0f3f9e 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceSpecialGlobals.h +++ b/src/llvm-project/llvm/tools/llvm-reduce/deltas/ReduceSpecialGlobals.h
@@ -14,8 +14,13 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCESPECIALGLOBALS_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCESPECIALGLOBALS_H + #include "Delta.h" namespace llvm { void reduceSpecialGlobalsDeltaPass(TestRunner &Test); } // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-reduce/llvm-reduce.cpp b/src/llvm-project/llvm/tools/llvm-reduce/llvm-reduce.cpp index 376826b..43dd15f 100644 --- a/src/llvm-project/llvm/tools/llvm-reduce/llvm-reduce.cpp +++ b/src/llvm-project/llvm/tools/llvm-reduce/llvm-reduce.cpp
@@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "DeltaManager.h" +#include "TestRunner.h" #include "llvm/ADT/SmallString.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Verifier.h" @@ -35,6 +36,11 @@ static cl::opt<bool> Version("v", cl::desc("Alias for -version"), cl::Hidden, cl::cat(Options)); +static cl::opt<bool> + PrintDeltaPasses("print-delta-passes", + cl::desc("Print list of delta passes, passable to " + "--delta-passes as a comma separated list")); + static cl::opt<std::string> InputFilename(cl::Positional, cl::Required, cl::desc("<input llvm ll/bc file>"), cl::cat(Options)); @@ -96,15 +102,25 @@ errs() << Message << OutputFilename << "\n"; } -int main(int argc, char **argv) { - InitLLVM X(argc, argv); +int main(int Argc, char **Argv) { + InitLLVM X(Argc, Argv); - cl::ParseCommandLineOptions(argc, argv, "LLVM automatic testcase reducer.\n"); + cl::HideUnrelatedOptions({&Options, &getColorCategory()}); + cl::ParseCommandLineOptions(Argc, Argv, "LLVM automatic testcase reducer.\n"); + + if (PrintDeltaPasses) { + printDeltaPasses(errs()); + return 0; + } LLVMContext Context; std::unique_ptr<Module> OriginalProgram = parseInputFile(InputFilename, Context); + if (!OriginalProgram) { + return 1; + } + // Initialize test environment TestRunner Tester(TestFilename, TestArguments); Tester.setProgram(std::move(OriginalProgram));
diff --git a/src/llvm-project/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/src/llvm-project/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp index 9199431..f02d898 100644 --- a/src/llvm-project/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/src/llvm-project/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -27,6 +27,7 @@ #include "llvm/Object/SymbolSize.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MSVCErrorWorkarounds.h" #include "llvm/Support/Memory.h" @@ -43,9 +44,11 @@ using namespace llvm; using namespace llvm::object; -static cl::list<std::string> -InputFileList(cl::Positional, cl::ZeroOrMore, - cl::desc("<input files>")); +static cl::OptionCategory RTDyldCategory("RTDyld Options"); + +static cl::list<std::string> InputFileList(cl::Positional, cl::ZeroOrMore, + cl::desc("<input files>"), + cl::cat(RTDyldCategory)); enum ActionType { AC_Execute, @@ -55,94 +58,93 @@ AC_Verify }; -static cl::opt<ActionType> -Action(cl::desc("Action to perform:"), - cl::init(AC_Execute), - cl::values(clEnumValN(AC_Execute, "execute", - "Load, link, and execute the inputs."), - clEnumValN(AC_PrintLineInfo, "printline", - "Load, link, and print line information for each function."), - clEnumValN(AC_PrintDebugLineInfo, "printdebugline", - "Load, link, and print line information for each function using the debug object"), - clEnumValN(AC_PrintObjectLineInfo, "printobjline", - "Like -printlineinfo but does not load the object first"), - clEnumValN(AC_Verify, "verify", - "Load, link and verify the resulting memory image."))); +static cl::opt<ActionType> Action( + cl::desc("Action to perform:"), cl::init(AC_Execute), + cl::values( + clEnumValN(AC_Execute, "execute", + "Load, link, and execute the inputs."), + clEnumValN(AC_PrintLineInfo, "printline", + "Load, link, and print line information for each function."), + clEnumValN(AC_PrintDebugLineInfo, "printdebugline", + "Load, link, and print line information for each function " + "using the debug object"), + clEnumValN(AC_PrintObjectLineInfo, "printobjline", + "Like -printlineinfo but does not load the object first"), + clEnumValN(AC_Verify, "verify", + "Load, link and verify the resulting memory image.")), + cl::cat(RTDyldCategory)); static cl::opt<std::string> -EntryPoint("entry", - cl::desc("Function to call as entry point."), - cl::init("_main")); + EntryPoint("entry", cl::desc("Function to call as entry point."), + cl::init("_main"), cl::cat(RTDyldCategory)); -static cl::list<std::string> -Dylibs("dylib", - cl::desc("Add library."), - cl::ZeroOrMore); +static cl::list<std::string> Dylibs("dylib", cl::desc("Add library."), + cl::ZeroOrMore, cl::cat(RTDyldCategory)); static cl::list<std::string> InputArgv("args", cl::Positional, cl::desc("<program arguments>..."), - cl::ZeroOrMore, cl::PositionalEatsArgs); + cl::ZeroOrMore, cl::PositionalEatsArgs, + cl::cat(RTDyldCategory)); static cl::opt<std::string> -TripleName("triple", cl::desc("Target triple for disassembler")); + TripleName("triple", cl::desc("Target triple for disassembler"), + cl::cat(RTDyldCategory)); static cl::opt<std::string> -MCPU("mcpu", - cl::desc("Target a specific cpu type (-mcpu=help for details)"), - cl::value_desc("cpu-name"), - cl::init("")); + MCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), cl::init(""), cl::cat(RTDyldCategory)); static cl::list<std::string> -CheckFiles("check", - cl::desc("File containing RuntimeDyld verifier checks."), - cl::ZeroOrMore); + CheckFiles("check", + cl::desc("File containing RuntimeDyld verifier checks."), + cl::ZeroOrMore, cl::cat(RTDyldCategory)); static cl::opt<uint64_t> PreallocMemory("preallocate", cl::desc("Allocate memory upfront rather than on-demand"), - cl::init(0)); + cl::init(0), cl::cat(RTDyldCategory)); static cl::opt<uint64_t> TargetAddrStart( "target-addr-start", cl::desc("For -verify only: start of phony target address " "range."), cl::init(4096), // Start at "page 1" - no allocating at "null". - cl::Hidden); + cl::Hidden, cl::cat(RTDyldCategory)); static cl::opt<uint64_t> TargetAddrEnd( "target-addr-end", cl::desc("For -verify only: end of phony target address range."), - cl::init(~0ULL), cl::Hidden); + cl::init(~0ULL), cl::Hidden, cl::cat(RTDyldCategory)); static cl::opt<uint64_t> TargetSectionSep( "target-section-sep", cl::desc("For -verify only: Separation between sections in " "phony target address space."), - cl::init(0), cl::Hidden); + cl::init(0), cl::Hidden, cl::cat(RTDyldCategory)); static cl::list<std::string> -SpecificSectionMappings("map-section", - cl::desc("For -verify only: Map a section to a " - "specific address."), - cl::ZeroOrMore, - cl::Hidden); + SpecificSectionMappings("map-section", + cl::desc("For -verify only: Map a section to a " + "specific address."), + cl::ZeroOrMore, cl::Hidden, + cl::cat(RTDyldCategory)); -static cl::list<std::string> -DummySymbolMappings("dummy-extern", - cl::desc("For -verify only: Inject a symbol into the extern " - "symbol table."), - cl::ZeroOrMore, - cl::Hidden); +static cl::list<std::string> DummySymbolMappings( + "dummy-extern", + cl::desc("For -verify only: Inject a symbol into the extern " + "symbol table."), + cl::ZeroOrMore, cl::Hidden, cl::cat(RTDyldCategory)); -static cl::opt<bool> -PrintAllocationRequests("print-alloc-requests", - cl::desc("Print allocation requests made to the memory " - "manager by RuntimeDyld"), - cl::Hidden); +static cl::opt<bool> PrintAllocationRequests( + "print-alloc-requests", + cl::desc("Print allocation requests made to the memory " + "manager by RuntimeDyld"), + cl::Hidden, cl::cat(RTDyldCategory)); static cl::opt<bool> ShowTimes("show-times", cl::desc("Show times for llvm-rtdyld phases"), - cl::init(false)); + cl::init(false), cl::cat(RTDyldCategory)); ExitOnError ExitOnErr; @@ -756,7 +758,7 @@ if (!MAI) ErrorAndExit("Unable to create target asm info!"); - MCContext Ctx(MAI.get(), MRI.get(), nullptr); + MCContext Ctx(Triple(TripleName), MAI.get(), MRI.get(), STI.get()); std::unique_ptr<MCDisassembler> Disassembler( TheTarget->createMCDisassembler(*STI, Ctx)); @@ -840,7 +842,7 @@ char *CSymAddr = static_cast<char *>(SymAddr); StringRef SecContent = Dyld.getSectionContent(SectionID); uint64_t SymSize = SecContent.size() - (CSymAddr - SecContent.data()); - SymInfo.setContent(StringRef(CSymAddr, SymSize)); + SymInfo.setContent(ArrayRef<char>(CSymAddr, SymSize)); } } return SymInfo; @@ -867,7 +869,8 @@ return SectionID.takeError(); RuntimeDyldChecker::MemoryRegionInfo SecInfo; SecInfo.setTargetAddress(Dyld.getSectionLoadAddress(*SectionID)); - SecInfo.setContent(Dyld.getSectionContent(*SectionID)); + StringRef SecContent = Dyld.getSectionContent(*SectionID); + SecInfo.setContent(ArrayRef<char>(SecContent.data(), SecContent.size())); return SecInfo; }; @@ -886,8 +889,10 @@ RuntimeDyldChecker::MemoryRegionInfo StubMemInfo; StubMemInfo.setTargetAddress(Dyld.getSectionLoadAddress(SI.SectionID) + SI.Offset); + StringRef SecContent = + Dyld.getSectionContent(SI.SectionID).substr(SI.Offset); StubMemInfo.setContent( - Dyld.getSectionContent(SI.SectionID).substr(SI.Offset)); + ArrayRef<char>(SecContent.data(), SecContent.size())); return StubMemInfo; }; @@ -962,6 +967,7 @@ llvm::InitializeAllTargetMCs(); llvm::InitializeAllDisassemblers(); + cl::HideUnrelatedOptions({&RTDyldCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm MC-JIT tool\n"); ExitOnErr.setBanner(std::string(argv[0]) + ": ");
diff --git a/src/llvm-project/llvm/tools/llvm-rust-demangle-fuzzer/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-rust-demangle-fuzzer/CMakeLists.txt new file mode 100644 index 0000000..eeb37fc --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-rust-demangle-fuzzer/CMakeLists.txt
@@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Demangle + FuzzMutate + Support +) + +add_llvm_fuzzer(llvm-rust-demangle-fuzzer + llvm-rust-demangle-fuzzer.cpp + DUMMY_MAIN DummyDemanglerFuzzer.cpp + )
diff --git a/src/llvm-project/llvm/tools/llvm-rust-demangle-fuzzer/DummyDemanglerFuzzer.cpp b/src/llvm-project/llvm/tools/llvm-rust-demangle-fuzzer/DummyDemanglerFuzzer.cpp new file mode 100644 index 0000000..201fdd7 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-rust-demangle-fuzzer/DummyDemanglerFuzzer.cpp
@@ -0,0 +1,18 @@ +//===-- DummyDemanglerFuzzer.cpp - Entry point to sanity check the fuzzer -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implementation of main so we can build and test without linking libFuzzer. +// +//===----------------------------------------------------------------------===// + +#include "llvm/FuzzMutate/FuzzerCLI.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +int main(int argc, char *argv[]) { + return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput); +}
diff --git a/src/llvm-project/llvm/tools/llvm-rust-demangle-fuzzer/llvm-rust-demangle-fuzzer.cpp b/src/llvm-project/llvm/tools/llvm-rust-demangle-fuzzer/llvm-rust-demangle-fuzzer.cpp new file mode 100644 index 0000000..c2891e5 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-rust-demangle-fuzzer/llvm-rust-demangle-fuzzer.cpp
@@ -0,0 +1,21 @@ +//===--- llvm-demangle-fuzzer.cpp - Fuzzer for the Rust Demangler ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Demangle/Demangle.h" +#include <cstdint> +#include <cstdlib> +#include <string> + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + std::string NullTerminatedString((const char *)Data, Size); + int Status = 0; + char *Demangled = llvm::rustDemangle(NullTerminatedString.c_str(), nullptr, + nullptr, &Status); + std::free(Demangled); + return 0; +}
diff --git a/src/llvm-project/llvm/tools/llvm-shlib/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-shlib/CMakeLists.txt index b0ee190..76b9a25 100644 --- a/src/llvm-project/llvm/tools/llvm-shlib/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-shlib/CMakeLists.txt
@@ -50,6 +50,13 @@ # Solaris ld does not accept global: *; so there is no way to version *all* global symbols set(LIB_NAMES -Wl,--version-script,${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map ${LIB_NAMES}) endif() + if (NOT MINGW) + # Optimize function calls for default visibility definitions to avoid PLT and + # reduce dynamic relocations. + # Note: for -fno-pic default, the address of a function may be different from + # inside and outside libLLVM.so. + target_link_options(LLVM PRIVATE LINKER:-Bsymbolic-functions) + endif() elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") set(LIB_NAMES -Wl,-all_load ${LIB_NAMES}) endif()
diff --git a/src/llvm-project/llvm/tools/llvm-sim/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-sim/CMakeLists.txt new file mode 100644 index 0000000..7629905 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-sim/CMakeLists.txt
@@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + Analysis + IRReader) + +add_llvm_tool(llvm-sim + llvm-sim.cpp +)
diff --git a/src/llvm-project/llvm/tools/llvm-sim/llvm-sim.cpp b/src/llvm-project/llvm/tools/llvm-sim/llvm-sim.cpp new file mode 100644 index 0000000..26e370f --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-sim/llvm-sim.cpp
@@ -0,0 +1,149 @@ +//===-- llvm-sim.cpp - Find similar sections of programs -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This program finds similar sections of a Module, and exports them as a JSON +// file. +// +// To find similarities contained across multiple modules, please use llvm-link +// first to merge the modules. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/IRSimilarityIdentifier.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/ToolOutputFile.h" + +using namespace llvm; +using namespace IRSimilarity; + +static cl::opt<std::string> OutputFilename("o", cl::desc("Output Filename"), + cl::init("-"), + cl::value_desc("filename")); + +static cl::opt<std::string> InputSourceFile(cl::Positional, + cl::desc("<Source file>"), + cl::init("-"), + cl::value_desc("filename")); + +/// Retrieve the unique number \p I was mapped to in parseBitcodeFile. +/// +/// \param I - The Instruction to find the instruction number for. +/// \param LLVMInstNum - The mapping of Instructions to their location in the +/// module represented by an unsigned integer. +/// \returns The instruction number for \p I if it exists. +Optional<unsigned> +getPositionInModule(const Instruction *I, + const DenseMap<Instruction *, unsigned> &LLVMInstNum) { + assert(I && "Instruction is nullptr!"); + DenseMap<Instruction *, unsigned>::const_iterator It = LLVMInstNum.find(I); + if (It == LLVMInstNum.end()) + return None; + return It->second; +} + +/// Exports the given SimilarityGroups to a JSON file at \p FilePath. +/// +/// \param FilePath - The path to the output location. +/// \param SimSections - The similarity groups to process. +/// \param LLVMInstNum - The mapping of Instructions to their location in the +/// module represented by an unsigned integer. +/// \returns A nonzero error code if there was a failure creating the file. +std::error_code +exportToFile(const StringRef FilePath, + const SimilarityGroupList &SimSections, + const DenseMap<Instruction *, unsigned> &LLVMInstNum) { + std::error_code EC; + std::unique_ptr<ToolOutputFile> Out( + new ToolOutputFile(FilePath, EC, sys::fs::OF_None)); + if (EC) + return EC; + + json::OStream J(Out->os(), 1); + J.objectBegin(); + + unsigned SimOption = 1; + // Process each list of SimilarityGroups organized by the Module. + for (const SimilarityGroup &G : SimSections) { + std::string SimOptionStr = std::to_string(SimOption); + J.attributeBegin(SimOptionStr); + J.arrayBegin(); + // For each file there is a list of the range where the similarity + // exists. + for (const IRSimilarityCandidate &C : G) { + Optional<unsigned> Start = + getPositionInModule((*C.front()).Inst, LLVMInstNum); + Optional<unsigned> End = + getPositionInModule((*C.back()).Inst, LLVMInstNum); + + assert(Start.hasValue() && + "Could not find instruction number for first instruction"); + assert(End.hasValue() && + "Could not find instruction number for last instruction"); + + J.object([&] { + J.attribute("start", Start.getValue()); + J.attribute("end", End.getValue()); + }); + } + J.arrayEnd(); + J.attributeEnd(); + SimOption++; + } + J.objectEnd(); + + Out->keep(); + + return EC; +} + +int main(int argc, const char *argv[]) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "LLVM IR Similarity Visualizer\n"); + + LLVMContext CurrContext; + SMDiagnostic Err; + std::unique_ptr<Module> ModuleToAnalyze = + parseIRFile(InputSourceFile, Err, CurrContext); + + if (!ModuleToAnalyze) { + Err.print(argv[0], errs()); + return 1; + } + + // Mapping from an Instruction pointer to its occurrence in a sequential + // list of all the Instructions in a Module. + DenseMap<Instruction *, unsigned> LLVMInstNum; + + // We give each instruction a number, which gives us a start and end value + // for the beginning and end of each IRSimilarityCandidate. + unsigned InstructionNumber = 1; + for (Function &F : *ModuleToAnalyze) + for (BasicBlock &BB : F) + for (Instruction &I : BB.instructionsWithoutDebug()) + LLVMInstNum[&I]= InstructionNumber++; + + // The similarity identifier we will use to find the similar sections. + IRSimilarityIdentifier SimIdent; + SimilarityGroupList SimilaritySections = + SimIdent.findSimilarity(*ModuleToAnalyze); + + std::error_code E = + exportToFile(OutputFilename, SimilaritySections, LLVMInstNum); + if (E) { + errs() << argv[0] << ": " << E.message() << '\n'; + return 2; + } + + return 0; +}
diff --git a/src/llvm-project/llvm/tools/llvm-size/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-size/CMakeLists.txt index 7ef4f17..0d1f660 100644 --- a/src/llvm-project/llvm/tools/llvm-size/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-size/CMakeLists.txt
@@ -1,10 +1,17 @@ set(LLVM_LINK_COMPONENTS Object + Option Support ) +set(LLVM_TARGET_DEFINITIONS Opts.td) +tablegen(LLVM Opts.inc -gen-opt-parser-defs) +add_public_tablegen_target(SizeOptsTableGen) + add_llvm_tool(llvm-size llvm-size.cpp + DEPENDS + SizeOptsTableGen ) if(LLVM_INSTALL_BINUTILS_SYMLINKS)
diff --git a/src/llvm-project/llvm/tools/llvm-size/Opts.td b/src/llvm-project/llvm/tools/llvm-size/Opts.td new file mode 100644 index 0000000..edae43f --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-size/Opts.td
@@ -0,0 +1,32 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, + HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def common : FF<"common", "Print common symbols in the ELF file. When using Berkeley format, this is added to bss">; +defm format : Eq<"format", "Specify output format">; +def help : FF<"help", "Display this help">; +defm radix : Eq<"radix", "Print size in radix">; +def totals : FF<"totals", "Print totals of all objects - Berkeley format only">; +def version : FF<"version", "Display the version">; + +// Mach-O specific options. +def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">; +def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>; +def : Separate<["--", "-"], "arch">, Alias<arch_EQ>; +def l : F<"l", "When format is darwin, use long format to include addresses and offsets">, Group<grp_mach_o>; + +def : F<"A", "Alias for --format">, Alias<format_EQ>, AliasArgs<["sysv"]>; +def : F<"B", "Alias for --format">, Alias<format_EQ>, AliasArgs<["berkeley"]>; +def : F<"d", "Alias for --radix=10">, Alias<radix_EQ>, AliasArgs<["10"]>; +def : F<"h", "Alias for --help">, Alias<help>; +def : F<"m", "Alias for --format">, Alias<format_EQ>, AliasArgs<["darwin"]>; +def : F<"o", "Alias for --radix=8">, Alias<radix_EQ>, AliasArgs<["8"]>; +def : F<"t", "Alias for --totals">, Alias<totals>; +def : F<"x", "Alias for --radix=16">, Alias<radix_EQ>, AliasArgs<["16"]>;
diff --git a/src/llvm-project/llvm/tools/llvm-size/llvm-size.cpp b/src/llvm-project/llvm/tools/llvm-size/llvm-size.cpp index 4f98a4c..ec9a4cd 100644 --- a/src/llvm-project/llvm/tools/llvm-size/llvm-size.cpp +++ b/src/llvm-project/llvm/tools/llvm-size/llvm-size.cpp
@@ -18,6 +18,9 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -33,23 +36,56 @@ using namespace llvm; using namespace object; -cl::OptionCategory SizeCat("llvm-size Options"); +namespace { +using namespace llvm::opt; // for HelpHidden in Opts.inc +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class SizeOptTable : public opt::OptTable { +public: + SizeOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; enum OutputFormatTy { berkeley, sysv, darwin }; -static cl::opt<OutputFormatTy> - OutputFormat("format", cl::desc("Specify output format"), - cl::values(clEnumVal(sysv, "System V format"), - clEnumVal(berkeley, "Berkeley format"), - clEnumVal(darwin, "Darwin -m format")), - cl::init(berkeley), cl::cat(SizeCat)); +enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 }; +} // namespace -static cl::opt<OutputFormatTy> - OutputFormatShort(cl::desc("Specify output format"), - cl::values(clEnumValN(sysv, "A", "System V format"), - clEnumValN(berkeley, "B", "Berkeley format"), - clEnumValN(darwin, "m", "Darwin -m format")), - cl::init(berkeley), cl::cat(SizeCat)); +static bool ArchAll = false; +static std::vector<StringRef> ArchFlags; +static bool ELFCommons; +static OutputFormatTy OutputFormat; +static bool DarwinLongFormat; +static RadixTy Radix; +static bool TotalSizes; +static std::vector<std::string> InputFilenames; + +static std::string ToolName; + +// States +static bool HadError = false; static bool BerkeleyHeaderPrinted = false; static bool MoreThanOneFile = false; static uint64_t TotalObjectText = 0; @@ -57,59 +93,13 @@ static uint64_t TotalObjectBss = 0; static uint64_t TotalObjectTotal = 0; -cl::opt<bool> - DarwinLongFormat("l", - cl::desc("When format is darwin, use long format " - "to include addresses and offsets."), - cl::cat(SizeCat)); - -cl::opt<bool> - ELFCommons("common", - cl::desc("Print common symbols in the ELF file. When using " - "Berkeley format, this is added to bss."), - cl::init(false), cl::cat(SizeCat)); - -static cl::list<std::string> - ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), - cl::ZeroOrMore, cl::cat(SizeCat)); -static bool ArchAll = false; - -enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 }; -static cl::opt<RadixTy> Radix( - "radix", cl::desc("Print size in radix"), cl::init(decimal), - cl::values(clEnumValN(octal, "8", "Print size in octal"), - clEnumValN(decimal, "10", "Print size in decimal"), - clEnumValN(hexadecimal, "16", "Print size in hexadecimal")), - cl::cat(SizeCat)); - -static cl::opt<RadixTy> RadixShort( - cl::desc("Print size in radix:"), - cl::values(clEnumValN(octal, "o", "Print size in octal"), - clEnumValN(decimal, "d", "Print size in decimal"), - clEnumValN(hexadecimal, "x", "Print size in hexadecimal")), - cl::init(decimal), cl::cat(SizeCat)); - -static cl::opt<bool> - TotalSizes("totals", - cl::desc("Print totals of all objects - Berkeley format only"), - cl::init(false), cl::cat(SizeCat)); - -static cl::alias TotalSizesShort("t", cl::desc("Short for --totals"), - cl::aliasopt(TotalSizes)); - -static cl::list<std::string> - InputFilenames(cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore); - -static cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); - -static bool HadError = false; - -static std::string ToolName; - -static void error(const Twine &Message, StringRef File) { +static void error(const Twine &Message, StringRef File = "") { HadError = true; - WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n"; + if (File.empty()) + WithColor::error(errs(), ToolName) << Message << '\n'; + else + WithColor::error(errs(), ToolName) + << "'" << File << "': " << Message << '\n'; } // This version of error() prints the archive name and member name, for example: @@ -874,27 +864,66 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); - cl::HideUnrelatedOptions(SizeCat); - cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n"); - + BumpPtrAllocator A; + StringSaver Saver(A); + SizeOptTable Tbl; ToolName = argv[0]; - if (OutputFormatShort.getNumOccurrences()) - OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort); - if (RadixShort.getNumOccurrences()) - Radix = RadixShort.getValue(); + opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, + [&](StringRef Msg) { error(Msg); }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp( + outs(), + (Twine(ToolName) + " [options] <input object files>").str().c_str(), + "LLVM object size dumper"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + outs() << ToolName << '\n'; + cl::PrintVersionMessage(); + return 0; + } - for (StringRef Arch : ArchFlags) { - if (Arch == "all") { - ArchAll = true; - } else { - if (!MachOObjectFile::isValidArch(Arch)) { + ELFCommons = Args.hasArg(OPT_common); + DarwinLongFormat = Args.hasArg(OPT_l); + TotalSizes = Args.hasArg(OPT_totals); + StringRef V = Args.getLastArgValue(OPT_format_EQ, "berkeley"); + if (V == "berkeley") + OutputFormat = berkeley; + else if (V == "darwin") + OutputFormat = darwin; + else if (V == "sysv") + OutputFormat = sysv; + else + error("--format value should be one of: 'berkeley', 'darwin', 'sysv'"); + V = Args.getLastArgValue(OPT_radix_EQ, "10"); + if (V == "8") + Radix = RadixTy::octal; + else if (V == "10") + Radix = RadixTy::decimal; + else if (V == "16") + Radix = RadixTy::hexadecimal; + else + error("--radix value should be one of: 8, 10, 16 "); + + for (const auto *A : Args.filtered(OPT_arch_EQ)) { + SmallVector<StringRef, 2> Values; + llvm::SplitString(A->getValue(), Values, ","); + for (StringRef V : Values) { + if (V == "all") + ArchAll = true; + else if (MachOObjectFile::isValidArch(V)) + ArchFlags.push_back(V); + else { outs() << ToolName << ": for the -arch option: Unknown architecture " - << "named '" << Arch << "'"; + << "named '" << V << "'"; return 1; } } } + InputFilenames = Args.getAllArgValues(OPT_INPUT); if (InputFilenames.empty()) InputFilenames.push_back("a.out");
diff --git a/src/llvm-project/llvm/tools/llvm-split/llvm-split.cpp b/src/llvm-project/llvm/tools/llvm-split/llvm-split.cpp index be020c4..6de28dc 100644 --- a/src/llvm-project/llvm/tools/llvm-split/llvm-split.cpp +++ b/src/llvm-project/llvm/tools/llvm-split/llvm-split.cpp
@@ -24,24 +24,32 @@ using namespace llvm; -static cl::opt<std::string> -InputFilename(cl::Positional, cl::desc("<input bitcode file>"), - cl::init("-"), cl::value_desc("filename")); +static cl::OptionCategory SplitCategory("Split Options"); -static cl::opt<std::string> -OutputFilename("o", cl::desc("Override output filename"), - cl::value_desc("filename")); +static cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input bitcode file>"), + cl::init("-"), + cl::value_desc("filename"), + cl::cat(SplitCategory)); + +static cl::opt<std::string> OutputFilename("o", + cl::desc("Override output filename"), + cl::value_desc("filename"), + cl::cat(SplitCategory)); static cl::opt<unsigned> NumOutputs("j", cl::Prefix, cl::init(2), - cl::desc("Number of output files")); + cl::desc("Number of output files"), + cl::cat(SplitCategory)); static cl::opt<bool> PreserveLocals("preserve-locals", cl::Prefix, cl::init(false), - cl::desc("Split without externalizing locals")); + cl::desc("Split without externalizing locals"), + cl::cat(SplitCategory)); int main(int argc, char **argv) { LLVMContext Context; SMDiagnostic Err; + cl::HideUnrelatedOptions({&SplitCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n"); std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context); @@ -52,25 +60,28 @@ } unsigned I = 0; - SplitModule(std::move(M), NumOutputs, [&](std::unique_ptr<Module> MPart) { - std::error_code EC; - std::unique_ptr<ToolOutputFile> Out( - new ToolOutputFile(OutputFilename + utostr(I++), EC, sys::fs::OF_None)); - if (EC) { - errs() << EC.message() << '\n'; - exit(1); - } + SplitModule( + *M, NumOutputs, + [&](std::unique_ptr<Module> MPart) { + std::error_code EC; + std::unique_ptr<ToolOutputFile> Out(new ToolOutputFile( + OutputFilename + utostr(I++), EC, sys::fs::OF_None)); + if (EC) { + errs() << EC.message() << '\n'; + exit(1); + } - if (verifyModule(*MPart, &errs())) { - errs() << "Broken module!\n"; - exit(1); - } + if (verifyModule(*MPart, &errs())) { + errs() << "Broken module!\n"; + exit(1); + } - WriteBitcodeToFile(*MPart, Out->os()); + WriteBitcodeToFile(*MPart, Out->os()); - // Declare success. - Out->keep(); - }, PreserveLocals); + // Declare success. + Out->keep(); + }, + PreserveLocals); return 0; }
diff --git a/src/llvm-project/llvm/tools/llvm-stress/llvm-stress.cpp b/src/llvm-project/llvm/tools/llvm-stress/llvm-stress.cpp index 538240d..ece3229 100644 --- a/src/llvm-project/llvm/tools/llvm-stress/llvm-stress.cpp +++ b/src/llvm-project/llvm/tools/llvm-stress/llvm-stress.cpp
@@ -52,16 +52,20 @@ namespace llvm { -static cl::opt<unsigned> SeedCL("seed", - cl::desc("Seed used for randomness"), cl::init(0)); +static cl::OptionCategory StressCategory("Stress Options"); -static cl::opt<unsigned> SizeCL("size", - cl::desc("The estimated size of the generated function (# of instrs)"), - cl::init(100)); +static cl::opt<unsigned> SeedCL("seed", cl::desc("Seed used for randomness"), + cl::init(0), cl::cat(StressCategory)); -static cl::opt<std::string> -OutputFilename("o", cl::desc("Override output filename"), - cl::value_desc("filename")); +static cl::opt<unsigned> SizeCL( + "size", + cl::desc("The estimated size of the generated function (# of instrs)"), + cl::init(100), cl::cat(StressCategory)); + +static cl::opt<std::string> OutputFilename("o", + cl::desc("Override output filename"), + cl::value_desc("filename"), + cl::cat(StressCategory)); static LLVMContext Context; @@ -632,7 +636,7 @@ // If the value type is a vector, and we allow vector select, then in 50% // of the cases generate a vector select. - if (isa<FixedVectorType>(Val0->getType()) && (getRandom() % 1)) { + if (isa<FixedVectorType>(Val0->getType()) && (getRandom() & 1)) { unsigned NumElem = cast<FixedVectorType>(Val0->getType())->getNumElements(); CondTy = FixedVectorType::get(CondTy, NumElem); @@ -718,7 +722,7 @@ BoolInst.push_back(&Instr); } - std::shuffle(BoolInst.begin(), BoolInst.end(), R); + llvm::shuffle(BoolInst.begin(), BoolInst.end(), R); for (auto *Instr : BoolInst) { BasicBlock *Curr = Instr->getParent(); @@ -738,6 +742,7 @@ using namespace llvm; InitLLVM X(argc, argv); + cl::HideUnrelatedOptions({&StressCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm codegen stress-tester\n"); auto M = std::make_unique<Module>("/tmp/autogen.bc", Context);
diff --git a/src/llvm-project/llvm/tools/llvm-strings/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-strings/CMakeLists.txt index 390f117..95dd69c 100644 --- a/src/llvm-project/llvm/tools/llvm-strings/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/llvm-strings/CMakeLists.txt
@@ -1,11 +1,18 @@ set(LLVM_LINK_COMPONENTS Core Object + Option Support ) +set(LLVM_TARGET_DEFINITIONS Opts.td) +tablegen(LLVM Opts.inc -gen-opt-parser-defs) +add_public_tablegen_target(StringsOptsTableGen) + add_llvm_tool(llvm-strings llvm-strings.cpp + DEPENDS + StringsOptsTableGen ) if(LLVM_INSTALL_BINUTILS_SYMLINKS)
diff --git a/src/llvm-project/llvm/tools/llvm-strings/Opts.td b/src/llvm-project/llvm/tools/llvm-strings/Opts.td new file mode 100644 index 0000000..2ad77fa --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-strings/Opts.td
@@ -0,0 +1,23 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, + HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def all : FF<"all", "Silently ignored. Present for GNU strings compatibility">; +defm bytes : Eq<"bytes", "Print sequences of the specified length">; +def help : FF<"help", "Display this help">; +def print_file_name : Flag<["--"], "print-file-name">, HelpText<"Print the name of the file before each string">; +defm radix : Eq<"radix", "Print the offset within the file with the specified radix: o (octal), d (decimal), x (hexadecimal)">, MetaVarName<"<radix>">; +def version : FF<"version", "Display the version">; + +def : F<"a", "Alias for --all">, Alias<all>; +def : F<"f", "Alias for --print-file-name">, Alias<print_file_name>; +def : F<"h", "Alias for --help">, Alias<help>; +def : JoinedOrSeparate<["-"], "n">, Alias<bytes_EQ>, HelpText<"Alias for --bytes">; +def : JoinedOrSeparate<["-"], "t">, Alias<radix_EQ>, HelpText<"Alias for --radix">, MetaVarName<"<radix>">;
diff --git a/src/llvm-project/llvm/tools/llvm-strings/llvm-strings.cpp b/src/llvm-project/llvm/tools/llvm-strings/llvm-strings.cpp index 51313d7..0b06874 100644 --- a/src/llvm-project/llvm/tools/llvm-strings/llvm-strings.cpp +++ b/src/llvm-project/llvm/tools/llvm-strings/llvm-strings.cpp
@@ -11,51 +11,81 @@ // //===----------------------------------------------------------------------===// +#include "Opts.inc" #include "llvm/Object/Binary.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" +#include "llvm/Support/WithColor.h" #include <cctype> #include <string> using namespace llvm; using namespace llvm::object; +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class StringsOptTable : public opt::OptTable { +public: + StringsOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; +} // namespace + +const char ToolName[] = "llvm-strings"; + static cl::list<std::string> InputFileNames(cl::Positional, cl::desc("<input object files>"), cl::ZeroOrMore); -static cl::opt<bool> - PrintFileName("print-file-name", - cl::desc("Print the name of the file before each string")); -static cl::alias PrintFileNameShort("f", cl::desc(""), - cl::aliasopt(PrintFileName)); - -static cl::opt<int> - MinLength("bytes", cl::desc("Print sequences of the specified length"), - cl::init(4)); -static cl::alias MinLengthShort("n", cl::desc(""), cl::aliasopt(MinLength)); - -static cl::opt<bool> - AllSections("all", - cl::desc("Check all sections, not just the data section")); -static cl::alias AllSectionsShort("a", cl::desc(""), - cl::aliasopt(AllSections)); +static int MinLength = 4; +static bool PrintFileName; enum radix { none, octal, hexadecimal, decimal }; -static cl::opt<radix> - Radix("radix", cl::desc("print the offset within the file"), - cl::values(clEnumValN(octal, "o", "octal"), - clEnumValN(hexadecimal, "x", "hexadecimal"), - clEnumValN(decimal, "d", "decimal")), - cl::init(none)); -static cl::alias RadixShort("t", cl::desc(""), cl::aliasopt(Radix)); +static radix Radix; -static cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); +LLVM_ATTRIBUTE_NORETURN static void reportCmdLineError(const Twine &Message) { + WithColor::error(errs(), ToolName) << Message << "\n"; + exit(1); +} + +template <typename T> +static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) { + if (const opt::Arg *A = Args.getLastArg(ID)) { + StringRef V(A->getValue()); + if (!llvm::to_integer(V, Value, 0) || Value <= 0) + reportCmdLineError("expected a positive integer, but got '" + V + "'"); + } +} static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) { auto print = [&OS, FileName](unsigned Offset, StringRef L) { @@ -96,13 +126,48 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); + BumpPtrAllocator A; + StringSaver Saver(A); + StringsOptTable Tbl; + opt::InputArgList Args = + Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, + [&](StringRef Msg) { reportCmdLineError(Msg); }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp( + outs(), + (Twine(ToolName) + " [options] <input object files>").str().c_str(), + "llvm string dumper"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + outs() << ToolName << '\n'; + cl::PrintVersionMessage(); + return 0; + } - cl::ParseCommandLineOptions(argc, argv, "llvm string dumper\n"); + parseIntArg(Args, OPT_bytes_EQ, MinLength); + PrintFileName = Args.hasArg(OPT_print_file_name); + StringRef R = Args.getLastArgValue(OPT_radix_EQ); + if (R.empty()) + Radix = none; + else if (R == "o") + Radix = octal; + else if (R == "d") + Radix = decimal; + else if (R == "x") + Radix = hexadecimal; + else + reportCmdLineError("--radix value should be one of: '' (no offset), 'o' " + "(octal), 'd' (decimal), 'x' (hexadecimal)"); + if (MinLength == 0) { errs() << "invalid minimum string length 0\n"; return EXIT_FAILURE; } + std::vector<std::string> InputFileNames = Args.getAllArgValues(OPT_INPUT); if (InputFileNames.empty()) InputFileNames.push_back("-");
diff --git a/src/llvm-project/llvm/tools/llvm-symbolizer/Opts.td b/src/llvm-project/llvm/tools/llvm-symbolizer/Opts.td index ac23639..6026e24 100644 --- a/src/llvm-project/llvm/tools/llvm-symbolizer/Opts.td +++ b/src/llvm-project/llvm/tools/llvm-symbolizer/Opts.td
@@ -1,17 +1,20 @@ include "llvm/Option/OptParser.td" multiclass B<string name, string help1, string help2> { - def NAME: Flag<["--", "-"], name>, HelpText<help1>; - def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>; + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; } multiclass Eq<string name, string help> { - def NAME #_EQ : Joined<["--", "-"], name #"=">, + def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>; - def : Separate<["--", "-"], name>, Alias<!cast<Joined>(NAME #_EQ)>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; } -class F<string name, string help>: Flag<["--", "-"], name>, HelpText<help>; +class F<string name, string help>: Flag<["--"], name>, HelpText<help>; + +def grp_mach_o : OptionGroup<"kind">, + HelpText<"llvm-symbolizer Mach-O Specific Options">; def addresses : F<"addresses", "Show address before line information">; defm adjust_vma @@ -19,13 +22,19 @@ MetaVarName<"<offset>">; def basenames : Flag<["--"], "basenames">, HelpText<"Strip directory names from paths">; defm debug_file_directory : Eq<"debug-file-directory", "Path to directory where to look for debug files">, MetaVarName<"<dir>">; -defm default_arch : Eq<"default-arch", "Default architecture (for multi-arch objects)">; +defm default_arch + : Eq<"default-arch", "Default architecture (for multi-arch objects)">, + Group<grp_mach_o>; defm demangle : B<"demangle", "Demangle function names", "Don't demangle function names">; def functions : F<"functions", "Print function name for a given address">; def functions_EQ : Joined<["--"], "functions=">, HelpText<"Print function name for a given address">, Values<"none,short,linkage">; def help : F<"help", "Display this help">; defm dwp : Eq<"dwp", "Path to DWP file to be use for any split CUs">, MetaVarName<"<file>">; -defm dsym_hint : Eq<"dsym-hint", "Path to .dSYM bundles to search for debug info for the object files">, MetaVarName<"<dir>">; +defm dsym_hint + : Eq<"dsym-hint", + "Path to .dSYM bundles to search for debug info for the object files">, + MetaVarName<"<dir>">, + Group<grp_mach_o>; defm fallback_debug_path : Eq<"fallback-debug-path", "Fallback path for debug binaries">, MetaVarName<"<dir>">; defm inlines : B<"inlines", "Print all inlined frames for a given address", "Do not print inlined frames">; @@ -33,9 +42,9 @@ : Eq<"obj", "Path to object file to be symbolized (if not provided, " "object file should be specified for each input line)">, MetaVarName<"<file>">; defm output_style - : Eq<"output-style", "Specify print style. Supported styles: LLVM, GNU">, + : Eq<"output-style", "Specify print style. Supported styles: LLVM, GNU, JSON">, MetaVarName<"style">, - Values<"LLVM,GNU">; + Values<"LLVM,GNU,JSON">; def pretty_print : F<"pretty-print", "Make the output more human friendly">; defm print_source_context_lines : Eq<"print-source-context-lines", "Print N lines of source file context">; def relative_address : F<"relative-address", "Interpret addresses as addresses relative to the image base">;
diff --git a/src/llvm-project/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/src/llvm-project/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp index 8734c2d..227ce12 100644 --- a/src/llvm-project/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/src/llvm-project/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -66,23 +66,35 @@ class SymbolizerOptTable : public opt::OptTable { public: - SymbolizerOptTable() : OptTable(InfoTable, true) {} + SymbolizerOptTable() : OptTable(InfoTable) { + setGroupedShortOptions(true); + } }; } // namespace -static cl::list<std::string> ClInputAddresses(cl::Positional, - cl::desc("<input addresses>..."), - cl::ZeroOrMore); +template <typename T> +static void print(const Request &Request, Expected<T> &ResOrErr, + DIPrinter &Printer) { + if (ResOrErr) { + // No error, print the result. + Printer.print(Request, *ResOrErr); + return; + } -template<typename T> -static bool error(Expected<T> &ResOrErr) { - if (ResOrErr) - return false; - logAllUnhandledErrors(ResOrErr.takeError(), errs(), - "LLVMSymbolizer: error reading file: "); - return true; + // Handle the error. + bool PrintEmpty = true; + handleAllErrors(std::move(ResOrErr.takeError()), + [&](const ErrorInfoBase &EI) { + PrintEmpty = Printer.printError( + Request, EI, "LLVMSymbolizer: error reading file: "); + }); + + if (PrintEmpty) + Printer.print(Request, T()); } +enum class OutputStyle { LLVM, GNU, JSON }; + enum class Command { Code, Data, @@ -136,7 +148,7 @@ } static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA, - bool IsAddr2Line, DIPrinter::OutputStyle OutputStyle, + bool IsAddr2Line, OutputStyle Style, StringRef InputString, LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { Command Cmd; @@ -144,62 +156,49 @@ uint64_t Offset = 0; if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, StringRef(InputString), Cmd, ModuleName, Offset)) { - outs() << InputString << "\n"; + Printer.printInvalidCommand({ModuleName, None}, InputString); return; } - if (Args.hasArg(OPT_addresses)) { - outs() << "0x"; - outs().write_hex(Offset); - StringRef Delimiter = Args.hasArg(OPT_pretty_print) ? ": " : "\n"; - outs() << Delimiter; - } - Offset -= AdjustVMA; + uint64_t AdjustedOffset = Offset - AdjustVMA; if (Cmd == Command::Data) { - auto ResOrErr = Symbolizer.symbolizeData( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get()); + Expected<DIGlobal> ResOrErr = Symbolizer.symbolizeData( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print({ModuleName, Offset}, ResOrErr, Printer); } else if (Cmd == Command::Frame) { - auto ResOrErr = Symbolizer.symbolizeFrame( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - if (!error(ResOrErr)) { - for (DILocal Local : *ResOrErr) - Printer << Local; - if (ResOrErr->empty()) - outs() << "??\n"; - } + Expected<std::vector<DILocal>> ResOrErr = Symbolizer.symbolizeFrame( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print({ModuleName, Offset}, ResOrErr, Printer); } else if (Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line)) { - auto ResOrErr = Symbolizer.symbolizeInlinedCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get()); - } else if (OutputStyle == DIPrinter::OutputStyle::GNU) { + Expected<DIInliningInfo> ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print({ModuleName, Offset}, ResOrErr, Printer); + } else if (Style == OutputStyle::GNU) { // With PrintFunctions == FunctionNameKind::LinkageName (default) // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode() // may override the name of an inlined function with the name of the topmost // caller function in the inlining chain. This contradicts the existing // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only // the topmost function, which suits our needs better. - auto ResOrErr = Symbolizer.symbolizeInlinedCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - if (!ResOrErr || ResOrErr->getNumberOfFrames() == 0) { - error(ResOrErr); - Printer << DILineInfo(); - } else { - Printer << ResOrErr->getFrame(0); - } + Expected<DIInliningInfo> ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + Expected<DILineInfo> Res0OrErr = + !ResOrErr + ? Expected<DILineInfo>(ResOrErr.takeError()) + : ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo() + : ResOrErr->getFrame(0)); + print({ModuleName, Offset}, Res0OrErr, Printer); } else { - auto ResOrErr = Symbolizer.symbolizeCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); + Expected<DILineInfo> ResOrErr = Symbolizer.symbolizeCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print({ModuleName, Offset}, ResOrErr, Printer); } - if (OutputStyle == DIPrinter::OutputStyle::LLVM) - outs() << "\n"; } static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl, raw_ostream &OS) { const char HelpText[] = " [options] addresses..."; - Tbl.PrintHelp(OS, (ToolName + HelpText).str().c_str(), + Tbl.printHelp(OS, (ToolName + HelpText).str().c_str(), ToolName.str().c_str()); // TODO Replace this with OptTable API once it adds extrahelp support. OS << "\nPass @FILE as argument to read options from FILE.\n"; @@ -209,7 +208,6 @@ StringSaver &Saver, SymbolizerOptTable &Tbl) { StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer"; - Tbl.setGroupedShortOptions(true); // The environment variable specifies initial options which can be overridden // by commnad line options. Tbl.setInitialOptionsFromEnvironment(IsAddr2Line ? "LLVM_ADDR2LINE_OPTS" @@ -273,7 +271,7 @@ LLVMSymbolizer::Options Opts; uint64_t AdjustVMA; - unsigned SourceContextLines; + PrinterConfig Config; parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA); if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) { Opts.PathStyle = @@ -290,7 +288,8 @@ Opts.FallbackDebugPath = Args.getLastArgValue(OPT_fallback_debug_path_EQ).str(); Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line); - parseIntArg(Args, OPT_print_source_context_lines_EQ, SourceContextLines); + parseIntArg(Args, OPT_print_source_context_lines_EQ, + Config.SourceContextLines); Opts.RelativeAddresses = Args.hasArg(OPT_relative_address); Opts.UntagAddresses = Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line); @@ -302,6 +301,10 @@ } #endif Opts.UseSymbolTable = true; + Config.PrintAddress = Args.hasArg(OPT_addresses); + Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None; + Config.Pretty = Args.hasArg(OPT_pretty_print); + Config.Verbose = Args.hasArg(OPT_verbose); for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) { StringRef Hint(A->getValue()); @@ -313,18 +316,24 @@ } } - auto OutputStyle = - IsAddr2Line ? DIPrinter::OutputStyle::GNU : DIPrinter::OutputStyle::LLVM; + auto Style = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM; if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) { - OutputStyle = strcmp(A->getValue(), "GNU") == 0 - ? DIPrinter::OutputStyle::GNU - : DIPrinter::OutputStyle::LLVM; + if (strcmp(A->getValue(), "GNU") == 0) + Style = OutputStyle::GNU; + else if (strcmp(A->getValue(), "JSON") == 0) + Style = OutputStyle::JSON; + else + Style = OutputStyle::LLVM; } LLVMSymbolizer Symbolizer(Opts); - DIPrinter Printer(outs(), Opts.PrintFunctions != FunctionNameKind::None, - Args.hasArg(OPT_pretty_print), SourceContextLines, - Args.hasArg(OPT_verbose), OutputStyle); + std::unique_ptr<DIPrinter> Printer; + if (Style == OutputStyle::GNU) + Printer = std::make_unique<GNUPrinter>(outs(), errs(), Config); + else if (Style == OutputStyle::JSON) + Printer = std::make_unique<JSONPrinter>(outs(), Config); + else + Printer = std::make_unique<LLVMPrinter>(outs(), errs(), Config); std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT); if (InputAddresses.empty()) { @@ -336,14 +345,16 @@ std::string StrippedInputString(InputString); llvm::erase_if(StrippedInputString, [](char c) { return c == '\r' || c == '\n'; }); - symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, - StrippedInputString, Symbolizer, Printer); + symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, StrippedInputString, + Symbolizer, *Printer); outs().flush(); } } else { + Printer->listBegin(); for (StringRef Address : InputAddresses) - symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, Address, - Symbolizer, Printer); + symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, Address, Symbolizer, + *Printer); + Printer->listEnd(); } return 0;
diff --git a/src/llvm-project/llvm/tools/llvm-tapi-diff/CMakeLists.txt b/src/llvm-project/llvm/tools/llvm-tapi-diff/CMakeLists.txt new file mode 100644 index 0000000..2870221 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-tapi-diff/CMakeLists.txt
@@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Object + Support + TextAPI + ) + +add_llvm_tool(llvm-tapi-diff + llvm-tapi-diff.cpp + DiffEngine.cpp + )
diff --git a/src/llvm-project/llvm/tools/llvm-tapi-diff/DiffEngine.cpp b/src/llvm-project/llvm/tools/llvm-tapi-diff/DiffEngine.cpp new file mode 100644 index 0000000..ff60d52 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-tapi-diff/DiffEngine.cpp
@@ -0,0 +1,556 @@ +//===-- DiffEngine.cpp - Structural file comparison -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the implementation of the llvm-tapi difference +// engine, which structurally compares two tbd files. +// +//===----------------------------------------------------------------------===/ +#include "DiffEngine.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/Symbol.h" +#include "llvm/TextAPI/Target.h" + +using namespace llvm; +using namespace MachO; +using namespace object; + +StringRef setOrderIndicator(InterfaceInputOrder Order) { + return ((Order == lhs) ? "< " : "> "); +} + +// The following template specialization implementations +// need to be explicitly placed into the llvm namespace +// to work around a GCC 4.8 bug. +namespace llvm { + +template <typename T, DiffAttrKind U> +inline void DiffScalarVal<T, U>::print(raw_ostream &OS, std::string Indent) { + OS << Indent << "\t" << setOrderIndicator(Order) << Val << "\n"; +} + +template <> +inline void +DiffScalarVal<StringRef, AD_Diff_Scalar_Str>::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t\t" << setOrderIndicator(Order) << Val << "\n"; +} + +template <> +inline void +DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t" << setOrderIndicator(Order) << std::to_string(Val) + << "\n"; +} + +template <> +inline void +DiffScalarVal<bool, AD_Diff_Scalar_Bool>::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t" << setOrderIndicator(Order) + << ((Val == true) ? "true" : "false") << "\n"; +} + +} // end namespace llvm + +StringLiteral SymScalar::getSymbolNamePrefix(MachO::SymbolKind Kind) { + switch (Kind) { + case MachO::SymbolKind::GlobalSymbol: + return StringLiteral(""); + case MachO::SymbolKind::ObjectiveCClass: + return ObjC2MetaClassNamePrefix; + case MachO::SymbolKind ::ObjectiveCClassEHType: + return ObjC2EHTypePrefix; + case MachO::SymbolKind ::ObjectiveCInstanceVariable: + return ObjC2IVarPrefix; + } + llvm_unreachable("Unknown llvm::MachO::SymbolKind enum"); +} + +std::string SymScalar::stringifySymbolFlag(MachO::SymbolFlags Flag) { + switch (Flag) { + case MachO::SymbolFlags::None: + return ""; + case MachO::SymbolFlags::ThreadLocalValue: + return "Thread-Local"; + case MachO::SymbolFlags::WeakDefined: + return "Weak-Defined"; + case MachO::SymbolFlags::WeakReferenced: + return "Weak-Referenced"; + case MachO::SymbolFlags::Undefined: + return "Undefined"; + case MachO::SymbolFlags::Rexported: + return "Reexported"; + } + llvm_unreachable("Unknown llvm::MachO::SymbolFlags enum"); +} + +void SymScalar::print(raw_ostream &OS, std::string Indent, MachO::Target Targ) { + if (Val->getKind() == MachO::SymbolKind::ObjectiveCClass) { + if (Targ.Arch == MachO::AK_i386 && + Targ.Platform == MachO::PlatformKind::macOS) { + OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") + << ObjC1ClassNamePrefix << Val->getName() + << getFlagString(Val->getFlags()) << "\n"; + return; + } + OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") + << ObjC2ClassNamePrefix << Val->getName() + << getFlagString(Val->getFlags()) << "\n"; + } + OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") + << getSymbolNamePrefix(Val->getKind()) << Val->getName() + << getFlagString(Val->getFlags()) << "\n"; +} + +bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS, + llvm::MachO::InterfaceFile::const_symbol_range RHS) { + return std::equal(LHS.begin(), LHS.end(), RHS.begin(), + [&](auto LHS, auto RHS) { return *LHS == *RHS; }); +} + +template <typename TargetVecT, typename ValTypeT, typename V> +void addDiffForTargSlice(V Val, Target Targ, DiffOutput &Diff, + InterfaceInputOrder Order) { + auto TargetVector = llvm::find_if( + Diff.Values, [&](const std::unique_ptr<AttributeDiff> &RawTVec) { + if (TargetVecT *TVec = dyn_cast<TargetVecT>(RawTVec.get())) + return TVec->Targ == Targ; + return false; + }); + if (TargetVector != Diff.Values.end()) { + ValTypeT NewVal(Order, Val); + cast<TargetVecT>(TargetVector->get())->TargValues.push_back(NewVal); + } else { + auto NewTargetVec = std::make_unique<TargetVecT>(Targ); + ValTypeT NewVal(Order, Val); + NewTargetVec->TargValues.push_back(NewVal); + Diff.Values.push_back(std::move(NewTargetVec)); + } +} + +DiffOutput getSingleAttrDiff(const std::vector<InterfaceFileRef> &IRefVec, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Str_Vec; + for (const auto &IRef : IRefVec) + for (auto Targ : IRef.targets()) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + IRef.getInstallName(), Targ, Diff, Order); + return Diff; +} + +DiffOutput +getSingleAttrDiff(const std::vector<std::pair<Target, std::string>> &PairVec, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Str_Vec; + for (const auto &Pair : PairVec) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + StringRef(Pair.second), Pair.first, Diff, Order); + return Diff; +} + +DiffOutput getSingleAttrDiff(InterfaceFile::const_symbol_range SymRange, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Sym_Vec; + for (const auto *Sym : SymRange) + for (auto Targ : Sym->targets()) + addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Diff, Order); + return Diff; +} + +template <typename T> +DiffOutput getSingleAttrDiff(T SingleAttr, std::string Attribute) { + DiffOutput Diff(Attribute); + Diff.Kind = SingleAttr.getKind(); + Diff.Values.push_back(std::make_unique<T>(SingleAttr)); + return Diff; +} + +template <typename T, DiffAttrKind U> +void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, + DiffScalarVal<T, U> Attr) { + Output.push_back(getSingleAttrDiff(Attr, Name)); +} + +template <typename T> +void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, + const T &Val, InterfaceInputOrder Order) { + Output.push_back(getSingleAttrDiff(Val, Name, Order)); +} + +std::vector<DiffOutput> getSingleIF(InterfaceFile *Interface, + InterfaceInputOrder Order) { + std::vector<DiffOutput> Output; + diffAttribute("Install Name", Output, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>( + Order, Interface->getInstallName())); + diffAttribute("Current Version", Output, + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + Order, Interface->getCurrentVersion())); + diffAttribute("Compatibility Version", Output, + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + Order, Interface->getCompatibilityVersion())); + diffAttribute("Swift ABI Version", Output, + DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( + Order, Interface->getSwiftABIVersion())); + diffAttribute("InstallAPI", Output, + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + Order, Interface->isInstallAPI())); + diffAttribute("Two Level Namespace", Output, + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + Order, Interface->isTwoLevelNamespace())); + diffAttribute("Application Extension Safe", Output, + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + Order, Interface->isApplicationExtensionSafe())); + diffAttribute("Reexported Libraries", Output, + Interface->reexportedLibraries(), Order); + diffAttribute("Allowable Clients", Output, Interface->allowableClients(), + Order); + diffAttribute("Parent Umbrellas", Output, Interface->umbrellas(), Order); + diffAttribute("Symbols", Output, Interface->symbols(), Order); + for (auto Doc : Interface->documents()) { + DiffOutput Documents("Inlined Reexported Frameworks/Libraries"); + Documents.Kind = AD_Inline_Doc; + Documents.Values.push_back(std::make_unique<InlineDoc>( + InlineDoc(Doc->getInstallName(), getSingleIF(Doc.get(), Order)))); + Output.push_back(std::move(Documents)); + } + return Output; +} + +void findAndAddDiff(const std::vector<InterfaceFileRef> &CollectedIRefVec, + const std::vector<InterfaceFileRef> &LookupIRefVec, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Str_Vec; + for (const auto &IRef : CollectedIRefVec) + for (auto Targ : IRef.targets()) { + auto FoundIRef = llvm::find_if(LookupIRefVec, [&](const auto LIRef) { + auto FoundTarg = llvm::find(LIRef.targets(), Targ); + return (FoundTarg != LIRef.targets().end() && + IRef.getInstallName() == LIRef.getInstallName()); + }); + if (FoundIRef == LookupIRefVec.end()) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + IRef.getInstallName(), Targ, Result, Order); + } +} + +void findAndAddDiff( + const std::vector<std::pair<Target, std::string>> &CollectedPairs, + const std::vector<std::pair<Target, std::string>> &LookupPairs, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Str_Vec; + for (const auto &Pair : CollectedPairs) { + auto FoundPair = llvm::find(LookupPairs, Pair); + if (FoundPair == LookupPairs.end()) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + StringRef(Pair.second), Pair.first, Result, Order); + } +} + +void findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms, + InterfaceFile::const_symbol_range LookupSyms, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Sym_Vec; + for (const auto *Sym : CollectedSyms) + for (const auto Targ : Sym->targets()) { + auto FoundSym = llvm::find_if(LookupSyms, [&](const auto LSym) { + auto FoundTarg = llvm::find(LSym->targets(), Targ); + return (Sym->getName() == LSym->getName() && + Sym->getKind() == LSym->getKind() && + Sym->getFlags() == LSym->getFlags() && + FoundTarg != LSym->targets().end()); + }); + if (FoundSym == LookupSyms.end()) + addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Result, Order); + } +} + +template <typename T> +DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) { + DiffOutput Diff(Attr); + if (LHS.getKind() == RHS.getKind()) { + Diff.Kind = LHS.getKind(); + Diff.Values.push_back(std::make_unique<T>(LHS)); + Diff.Values.push_back(std::make_unique<T>(RHS)); + } + return Diff; +} + +template <typename T> +DiffOutput recordDifferences(const std::vector<T> &LHS, + const std::vector<T> &RHS, std::string Attr) { + DiffOutput Diff(Attr); + Diff.Kind = AD_Str_Vec; + findAndAddDiff(LHS, RHS, Diff, lhs); + findAndAddDiff(RHS, LHS, Diff, rhs); + return Diff; +} + +DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS, + llvm::MachO::InterfaceFile::const_symbol_range RHS, + std::string Attr) { + DiffOutput Diff(Attr); + Diff.Kind = AD_Sym_Vec; + findAndAddDiff(LHS, RHS, Diff, lhs); + findAndAddDiff(RHS, LHS, Diff, rhs); + return Diff; +} + +std::vector<DiffOutput> +DiffEngine::findDifferences(const InterfaceFile *IFLHS, + const InterfaceFile *IFRHS) { + std::vector<DiffOutput> Output; + if (IFLHS->getInstallName() != IFRHS->getInstallName()) + Output.push_back(recordDifferences( + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs, + IFLHS->getInstallName()), + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs, + IFRHS->getInstallName()), + "Install Name")); + + if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion()) + Output.push_back(recordDifferences( + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + lhs, IFLHS->getCurrentVersion()), + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + rhs, IFRHS->getCurrentVersion()), + "Current Version")); + if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion()) + Output.push_back(recordDifferences( + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + lhs, IFLHS->getCompatibilityVersion()), + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + rhs, IFRHS->getCompatibilityVersion()), + "Compatibility Version")); + if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion()) + Output.push_back( + recordDifferences(DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( + lhs, IFLHS->getSwiftABIVersion()), + DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( + rhs, IFRHS->getSwiftABIVersion()), + "Swift ABI Version")); + if (IFLHS->isInstallAPI() != IFRHS->isInstallAPI()) + Output.push_back(recordDifferences( + DiffScalarVal<bool, AD_Diff_Scalar_Bool>(lhs, IFLHS->isInstallAPI()), + DiffScalarVal<bool, AD_Diff_Scalar_Bool>(rhs, IFRHS->isInstallAPI()), + "InstallAPI")); + + if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace()) + Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + lhs, IFLHS->isTwoLevelNamespace()), + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + rhs, IFRHS->isTwoLevelNamespace()), + "Two Level Namespace")); + + if (IFLHS->isApplicationExtensionSafe() != + IFRHS->isApplicationExtensionSafe()) + Output.push_back( + recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + lhs, IFLHS->isApplicationExtensionSafe()), + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + rhs, IFRHS->isApplicationExtensionSafe()), + "Application Extension Safe")); + + if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries()) + Output.push_back(recordDifferences(IFLHS->reexportedLibraries(), + IFRHS->reexportedLibraries(), + "Reexported Libraries")); + + if (IFLHS->allowableClients() != IFRHS->allowableClients()) + Output.push_back(recordDifferences(IFLHS->allowableClients(), + IFRHS->allowableClients(), + "Allowable Clients")); + + if (IFLHS->umbrellas() != IFRHS->umbrellas()) + Output.push_back(recordDifferences(IFLHS->umbrellas(), IFRHS->umbrellas(), + "Parent Umbrellas")); + + if (!checkSymbolEquality(IFLHS->symbols(), IFRHS->symbols())) + Output.push_back( + recordDifferences(IFLHS->symbols(), IFRHS->symbols(), "Symbols")); + + if (IFLHS->documents() != IFRHS->documents()) { + DiffOutput Docs("Inlined Reexported Frameworks/Libraries"); + Docs.Kind = AD_Inline_Doc; + std::vector<StringRef> DocsInserted; + // Iterate through inline frameworks/libraries from interface file and find + // match based on install name. + for (auto DocLHS : IFLHS->documents()) { + auto Pair = llvm::find_if(IFRHS->documents(), [&](const auto &DocRHS) { + return (DocLHS->getInstallName() == DocRHS->getInstallName()); + }); + // If a match found, recursively get differences between the pair. + if (Pair != IFRHS->documents().end()) { + InlineDoc PairDiff = + InlineDoc(DocLHS->getInstallName(), + findDifferences(DocLHS.get(), Pair->get())); + if (!PairDiff.DocValues.empty()) + Docs.Values.push_back( + std::make_unique<InlineDoc>(std::move(PairDiff))); + } + // If a match is not found, get attributes from single item. + else + Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc( + DocLHS->getInstallName(), getSingleIF(DocLHS.get(), lhs)))); + DocsInserted.push_back(DocLHS->getInstallName()); + } + for (auto DocRHS : IFRHS->documents()) { + auto WasGathered = + llvm::find_if(DocsInserted, [&](const auto &GatheredDoc) { + return (GatheredDoc == DocRHS->getInstallName()); + }); + if (WasGathered == DocsInserted.end()) + Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc( + DocRHS->getInstallName(), getSingleIF(DocRHS.get(), rhs)))); + } + if (!Docs.Values.empty()) + Output.push_back(std::move(Docs)); + } + return Output; +} + +template <typename T> +void printSingleVal(std::string Indent, const DiffOutput &Attr, + raw_ostream &OS) { + if (Attr.Values.empty()) + return; + OS << Indent << Attr.Name << "\n"; + for (auto &RawItem : Attr.Values) + if (T *Item = dyn_cast<T>(RawItem.get())) + Item->print(OS, Indent); +} + +template <typename T> +T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) { + T *CastAttr = cast<T>(RawAttr.get()); + return CastAttr; +} + +template <typename T> void sortTargetValues(std::vector<T> &TargValues) { + llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) { + return ValA.getOrder() < ValB.getOrder(); + }); + llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) { + return ValA.getOrder() == ValB.getOrder() && ValA.getVal() < ValB.getVal(); + }); +} + +template <typename T> +void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) { + if (Attr.Values.empty()) + return; + + OS << Indent << Attr.Name << "\n"; + + std::vector<T *> SortedAttrs; + + llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), castValues<T>); + + llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) { + return ValA->Targ < ValB->Targ; + }); + + for (auto *Vec : SortedAttrs) { + sortTargetValues<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + Vec->TargValues); + OS << Indent << "\t" << getTargetTripleName(Vec->Targ) << "\n"; + for (auto &Item : Vec->TargValues) + Item.print(OS, Indent); + } +} + +template <> +void printVecVal<DiffSymVec>(std::string Indent, const DiffOutput &Attr, + raw_ostream &OS) { + if (Attr.Values.empty()) + return; + + OS << Indent << Attr.Name << "\n"; + + std::vector<DiffSymVec *> SortedAttrs; + + llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), + castValues<DiffSymVec>); + + llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) { + return ValA->Targ < ValB->Targ; + }); + for (auto *SymVec : SortedAttrs) { + sortTargetValues<SymScalar>(SymVec->TargValues); + OS << Indent << "\t" << getTargetTripleName(SymVec->Targ) << "\n"; + for (auto &Item : SymVec->TargValues) + Item.print(OS, Indent, SymVec->Targ); + } +} + +void DiffEngine::printDifferences(raw_ostream &OS, + const std::vector<DiffOutput> &Diffs, + int IndentCounter) { + std::string Indent = std::string(IndentCounter, '\t'); + for (auto &Attr : Diffs) { + switch (Attr.Kind) { + case AD_Diff_Scalar_Str: + if (IndentCounter == 0) + printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_PackedVersion: + printSingleVal< + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_Unsigned: + printSingleVal<DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_Bool: + printSingleVal<DiffScalarVal<bool, AD_Diff_Scalar_Bool>>(Indent, Attr, + OS); + break; + case AD_Str_Vec: + printVecVal<DiffStrVec>(Indent, Attr, OS); + break; + case AD_Sym_Vec: + printVecVal<DiffSymVec>(Indent, Attr, OS); + break; + case AD_Inline_Doc: + if (!Attr.Values.empty()) { + OS << Indent << Attr.Name << "\n"; + for (auto &Item : Attr.Values) + if (InlineDoc *Doc = dyn_cast<InlineDoc>(Item.get())) + if (!Doc->DocValues.empty()) { + OS << Indent << "\t" << Doc->InstallName << "\n"; + printDifferences(OS, std::move(Doc->DocValues), 2); + } + } + break; + } + } +} + +bool DiffEngine::compareFiles(raw_ostream &OS) { + const auto *IFLHS = &(FileLHS->getInterfaceFile()); + const auto *IFRHS = &(FileRHS->getInterfaceFile()); + if (*IFLHS == *IFRHS) + return false; + OS << "< " << std::string(IFLHS->getPath().data()) << "\n> " + << std::string(IFRHS->getPath().data()) << "\n\n"; + std::vector<DiffOutput> Diffs = findDifferences(IFLHS, IFRHS); + printDifferences(OS, Diffs, 0); + return true; +}
diff --git a/src/llvm-project/llvm/tools/llvm-tapi-diff/DiffEngine.h b/src/llvm-project/llvm/tools/llvm-tapi-diff/DiffEngine.h new file mode 100644 index 0000000..252fbd8 --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-tapi-diff/DiffEngine.h
@@ -0,0 +1,169 @@ +//===-- DiffEngine.h - File comparator --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header defines the interface to the llvm-tapi difference engine, +// which structurally compares two tbd files. +// +//===----------------------------------------------------------------------===/ +#ifndef LLVM_TOOLS_LLVM_TAPI_DIFF_DIFFENGINE_H +#define LLVM_TOOLS_LLVM_TAPI_DIFF_DIFFENGINE_H + +#include "llvm/ADT/Optional.h" +#include "llvm/Object/TapiUniversal.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TextAPI/Symbol.h" +#include "llvm/TextAPI/Target.h" + +namespace llvm { + +/// InterfaceInputOrder determines from which file the diff attribute belongs +/// to. +enum InterfaceInputOrder { lhs, rhs }; + +/// DiffAttrKind is the enum that holds the concrete bases for RTTI. +enum DiffAttrKind { + AD_Diff_Scalar_PackedVersion, + AD_Diff_Scalar_Unsigned, + AD_Diff_Scalar_Bool, + AD_Diff_Scalar_Str, + AD_Str_Vec, + AD_Sym_Vec, + AD_Inline_Doc, +}; + +/// AttributeDiff is the abstract class for RTTI. +class AttributeDiff { +public: + AttributeDiff(DiffAttrKind Kind) : Kind(Kind){}; + virtual ~AttributeDiff(){}; + DiffAttrKind getKind() const { return Kind; } + +private: + DiffAttrKind Kind; +}; + +/// DiffOutput is the representation of a diff for a single attribute. +struct DiffOutput { + /// The name of the attribute. + std::string Name; + /// The kind for RTTI + DiffAttrKind Kind; + /// Different values for the attribute + /// from each file where a diff is present. + std::vector<std::unique_ptr<AttributeDiff>> Values; + DiffOutput(std::string Name) : Name(Name){}; +}; + +/// DiffScalarVal is a template class for the different types of scalar values. +template <class T, DiffAttrKind U> class DiffScalarVal : public AttributeDiff { +public: + DiffScalarVal(InterfaceInputOrder Order, T Val) + : AttributeDiff(U), Order(Order), Val(Val){}; + + static bool classof(const AttributeDiff *A) { return A->getKind() == U; } + + void print(raw_ostream &, std::string); + + T getVal() const { return Val; } + InterfaceInputOrder getOrder() const { return Order; } + +private: + /// The order is the file from which the diff is found. + InterfaceInputOrder Order; + T Val; +}; + +/// SymScalar is the diff symbol and the order. +class SymScalar { +public: + SymScalar(InterfaceInputOrder Order, const MachO::Symbol *Sym) + : Order(Order), Val(Sym){}; + + std::string getFlagString(MachO::SymbolFlags Flags) { + return Flags != MachO::SymbolFlags::None + ? " - " + stringifySymbolFlag(Flags) + : stringifySymbolFlag(Flags); + } + + void print(raw_ostream &OS, std::string Indent, MachO::Target Targ); + + const MachO::Symbol *getVal() const { return Val; } + InterfaceInputOrder getOrder() const { return Order; } + +private: + /// The order is the file from which the diff is found. + InterfaceInputOrder Order; + const MachO::Symbol *Val; + StringLiteral getSymbolNamePrefix(MachO::SymbolKind Kind); + std::string stringifySymbolFlag(MachO::SymbolFlags Flag); +}; + +class DiffStrVec : public AttributeDiff { +public: + MachO::Target Targ; + /// Values is a vector of StringRef values associated with the target. + std::vector<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>> TargValues; + DiffStrVec(MachO::Target Targ) : AttributeDiff(AD_Str_Vec), Targ(Targ){}; + + static bool classof(const AttributeDiff *A) { + return A->getKind() == AD_Str_Vec; + } +}; + +class DiffSymVec : public AttributeDiff { +public: + MachO::Target Targ; + /// Values is a vector of symbol values associated with the target. + std::vector<SymScalar> TargValues; + DiffSymVec(MachO::Target Targ) : AttributeDiff(AD_Sym_Vec), Targ(Targ){}; + + static bool classof(const AttributeDiff *A) { + return A->getKind() == AD_Sym_Vec; + } +}; + +/// InlineDoc represents an inlined framework/library in a TBD File. +class InlineDoc : public AttributeDiff { +public: + /// Install name of the framework/library. + std::string InstallName; + /// Differences found from each file. + std::vector<DiffOutput> DocValues; + InlineDoc(StringRef InstName, std::vector<DiffOutput> Diff) + : AttributeDiff(AD_Inline_Doc), InstallName(InstName), + DocValues(std::move(Diff)){}; + + static bool classof(const AttributeDiff *A) { + return A->getKind() == AD_Inline_Doc; + } +}; + +/// DiffEngine contains the methods to compare the input files and print the +/// output of the differences found in the files. +class DiffEngine { +public: + DiffEngine(object::TapiUniversal *InputFileNameLHS, + object::TapiUniversal *InputFileNameRHS) + : FileLHS(InputFileNameLHS), FileRHS(InputFileNameRHS){}; + bool compareFiles(raw_ostream &); + +private: + object::TapiUniversal *FileLHS; + object::TapiUniversal *FileRHS; + + /// Function that prints the differences found in the files. + void printDifferences(raw_ostream &, const std::vector<DiffOutput> &, int); + /// Function that does the comparison of the TBD files and returns the + /// differences. + std::vector<DiffOutput> findDifferences(const MachO::InterfaceFile *, + const MachO::InterfaceFile *); +}; + +} // namespace llvm + +#endif
diff --git a/src/llvm-project/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp b/src/llvm-project/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp new file mode 100644 index 0000000..40f1eec --- /dev/null +++ b/src/llvm-project/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp
@@ -0,0 +1,89 @@ +//===-- llvm-tapi-diff.cpp - tbd comparator command-line driver ---*- +// C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the command-line driver for the llvm-tapi difference +// engine. +// +//===----------------------------------------------------------------------===// +#include "DiffEngine.h" +#include "llvm/Object/TapiUniversal.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdlib> + +using namespace llvm; +using namespace MachO; +using namespace object; + +namespace { +cl::OptionCategory NMCat("llvm-tapi-diff Options"); +cl::opt<std::string> InputFileNameLHS(cl::Positional, cl::desc("<first file>"), + cl::cat(NMCat)); +cl::opt<std::string> InputFileNameRHS(cl::Positional, cl::desc("<second file>"), + cl::cat(NMCat)); + +std::string ToolName; +} // anonymous namespace + +ExitOnError ExitOnErr; + +void setErrorBanner(ExitOnError &ExitOnErr, std::string InputFile) { + ExitOnErr.setBanner(ToolName + ": error: " + InputFile + ": "); +} + +Expected<std::unique_ptr<Binary>> convertFileToBinary(std::string &Filename) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + if (BufferOrErr.getError()) + return errorCodeToError(BufferOrErr.getError()); + return createBinary(BufferOrErr.get()->getMemBufferRef()); +} + +int main(int Argc, char **Argv) { + InitLLVM X(Argc, Argv); + cl::HideUnrelatedOptions(NMCat); + cl::ParseCommandLineOptions( + Argc, Argv, + "This tool will compare two tbd files and return the " + "differences in those files."); + if (InputFileNameLHS.empty() || InputFileNameRHS.empty()) { + cl::PrintHelpMessage(); + return EXIT_FAILURE; + } + + ToolName = Argv[0]; + + setErrorBanner(ExitOnErr, InputFileNameLHS); + auto BinLHS = ExitOnErr(convertFileToBinary(InputFileNameLHS)); + + TapiUniversal *FileLHS = dyn_cast<TapiUniversal>(BinLHS.get()); + if (!FileLHS) { + ExitOnErr( + createStringError(std::errc::executable_format_error, + "Error when parsing file, unsupported file format")); + } + + setErrorBanner(ExitOnErr, InputFileNameRHS); + auto BinRHS = ExitOnErr(convertFileToBinary(InputFileNameRHS)); + + TapiUniversal *FileRHS = dyn_cast<TapiUniversal>(BinRHS.get()); + if (!FileRHS) { + ExitOnErr( + createStringError(std::errc::executable_format_error, + "Error when parsing file, unsupported file format")); + } + + raw_ostream &OS = outs(); + + return DiffEngine(FileLHS, FileRHS).compareFiles(OS); +}
diff --git a/src/llvm-project/llvm/tools/llvm-undname/llvm-undname.cpp b/src/llvm-project/llvm/tools/llvm-undname/llvm-undname.cpp index f9f9e05..c6714cf 100644 --- a/src/llvm-project/llvm/tools/llvm-undname/llvm-undname.cpp +++ b/src/llvm-project/llvm/tools/llvm-undname/llvm-undname.cpp
@@ -28,28 +28,32 @@ using namespace llvm; +cl::OptionCategory UndNameCategory("UndName Options"); + cl::opt<bool> DumpBackReferences("backrefs", cl::Optional, cl::desc("dump backreferences"), cl::Hidden, - cl::init(false)); + cl::init(false), cl::cat(UndNameCategory)); cl::opt<bool> NoAccessSpecifier("no-access-specifier", cl::Optional, cl::desc("skip access specifiers"), cl::Hidden, - cl::init(false)); + cl::init(false), cl::cat(UndNameCategory)); cl::opt<bool> NoCallingConvention("no-calling-convention", cl::Optional, cl::desc("skip calling convention"), - cl::Hidden, cl::init(false)); + cl::Hidden, cl::init(false), + cl::cat(UndNameCategory)); cl::opt<bool> NoReturnType("no-return-type", cl::Optional, cl::desc("skip return types"), cl::Hidden, - cl::init(false)); + cl::init(false), cl::cat(UndNameCategory)); cl::opt<bool> NoMemberType("no-member-type", cl::Optional, cl::desc("skip member types"), cl::Hidden, - cl::init(false)); + cl::init(false), cl::cat(UndNameCategory)); cl::opt<std::string> RawFile("raw-file", cl::Optional, - cl::desc("for fuzzer data"), cl::Hidden); + cl::desc("for fuzzer data"), cl::Hidden, + cl::cat(UndNameCategory)); cl::opt<bool> WarnTrailing("warn-trailing", cl::Optional, cl::desc("warn on trailing characters"), cl::Hidden, - cl::init(false)); + cl::init(false), cl::cat(UndNameCategory)); cl::list<std::string> Symbols(cl::Positional, cl::desc("<input symbols>"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(UndNameCategory)); static bool msDemangle(const std::string &S) { int Status; @@ -84,6 +88,7 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); + cl::HideUnrelatedOptions({&UndNameCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm-undname\n"); if (!RawFile.empty()) {
diff --git a/src/llvm-project/llvm/tools/llvm-xray/xray-account.cpp b/src/llvm-project/llvm/tools/llvm-xray/xray-account.cpp index bde028a..1117046 100644 --- a/src/llvm-project/llvm/tools/llvm-xray/xray-account.cpp +++ b/src/llvm-project/llvm/tools/llvm-xray/xray-account.cpp
@@ -459,7 +459,7 @@ } std::error_code EC; - raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::OF_Text); + raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + AccountOutput + "' for writing.", EC);
diff --git a/src/llvm-project/llvm/tools/llvm-xray/xray-color-helper.cpp b/src/llvm-project/llvm/tools/llvm-xray/xray-color-helper.cpp index ea7ff35..e2cae21 100644 --- a/src/llvm-project/llvm/tools/llvm-xray/xray-color-helper.cpp +++ b/src/llvm-project/llvm/tools/llvm-xray/xray-color-helper.cpp
@@ -13,6 +13,7 @@ #include "xray-color-helper.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" +#include <cmath> using namespace llvm; using namespace xray;
diff --git a/src/llvm-project/llvm/tools/llvm-xray/xray-color-helper.h b/src/llvm-project/llvm/tools/llvm-xray/xray-color-helper.h index 0940fc2..3141e90 100644 --- a/src/llvm-project/llvm/tools/llvm-xray/xray-color-helper.h +++ b/src/llvm-project/llvm/tools/llvm-xray/xray-color-helper.h
@@ -13,9 +13,8 @@ #ifndef XRAY_COLOR_HELPER_H #define XRAY_COLOR_HELPER_H -#include <tuple> - #include "llvm/ADT/ArrayRef.h" +#include <tuple> namespace llvm { namespace xray {
diff --git a/src/llvm-project/llvm/tools/llvm-xray/xray-converter.cpp b/src/llvm-project/llvm/tools/llvm-xray/xray-converter.cpp index c1a623f..47cb645 100644 --- a/src/llvm-project/llvm/tools/llvm-xray/xray-converter.cpp +++ b/src/llvm-project/llvm/tools/llvm-xray/xray-converter.cpp
@@ -269,19 +269,14 @@ auto CycleFreq = FH.CycleFrequency; unsigned id_counter = 0; + int NumOutputRecords = 0; - OS << "{\n \"traceEvents\": ["; + OS << "{\n \"traceEvents\": [\n"; DenseMap<uint32_t, StackTrieNode *> StackCursorByThreadId{}; DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> StackRootsByThreadId{}; DenseMap<unsigned, StackTrieNode *> StacksByStackId{}; std::forward_list<StackTrieNode> NodeStore{}; - int loop_count = 0; for (const auto &R : Records) { - if (loop_count++ == 0) - OS << "\n"; - else - OS << ",\n"; - // Chrome trace event format always wants data in micros. // CyclesPerMicro = CycleHertz / 10^6 // TSC / CyclesPerMicro == TSC * 10^6 / CycleHertz == MicroTimestamp @@ -306,6 +301,9 @@ // type of B for begin or E for end, thread id, process id, // timestamp in microseconds, and a stack frame id. The ids are logged // in an id dictionary after the events. + if (NumOutputRecords++ > 0) { + OS << ",\n"; + } writeTraceViewerRecord(Version, OS, R.FuncId, R.TId, R.PId, Symbolize, FuncIdHelper, EventTimestampUs, *StackCursor, "B"); break; @@ -318,7 +316,7 @@ // (And/Or in loop termination below) StackTrieNode *PreviousCursor = nullptr; do { - if (PreviousCursor != nullptr) { + if (NumOutputRecords++ > 0) { OS << ",\n"; } writeTraceViewerRecord(Version, OS, StackCursor->FuncId, R.TId, R.PId, @@ -383,7 +381,7 @@ raw_fd_ostream OS(ConvertOutput, EC, ConvertOutputFormat == ConvertFormats::BINARY ? sys::fs::OpenFlags::OF_None - : sys::fs::OpenFlags::OF_Text); + : sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC);
diff --git a/src/llvm-project/llvm/tools/llvm-xray/xray-extract.cpp b/src/llvm-project/llvm/tools/llvm-xray/xray-extract.cpp index 8304d2d..a6ffacc 100644 --- a/src/llvm-project/llvm/tools/llvm-xray/xray-extract.cpp +++ b/src/llvm-project/llvm/tools/llvm-xray/xray-extract.cpp
@@ -83,7 +83,7 @@ InstrumentationMapOrError.takeError()); std::error_code EC; - raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::OF_Text); + raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
diff --git a/src/llvm-project/llvm/tools/llvm-xray/xray-graph-diff.cpp b/src/llvm-project/llvm/tools/llvm-xray/xray-graph-diff.cpp index 11210e2..f22ea06 100644 --- a/src/llvm-project/llvm/tools/llvm-xray/xray-graph-diff.cpp +++ b/src/llvm-project/llvm/tools/llvm-xray/xray-graph-diff.cpp
@@ -294,10 +294,7 @@ } template <typename T> static bool containsNullptr(const T &Collection) { - for (const auto &E : Collection) - if (E == nullptr) - return true; - return false; + return llvm::is_contained(Collection, nullptr); } static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E, @@ -459,7 +456,7 @@ auto &GDR = *GDROrErr; std::error_code EC; - raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_Text); + raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC);
diff --git a/src/llvm-project/llvm/tools/llvm-xray/xray-graph.cpp b/src/llvm-project/llvm/tools/llvm-xray/xray-graph.cpp index 00a1807..39d2c5c 100644 --- a/src/llvm-project/llvm/tools/llvm-xray/xray-graph.cpp +++ b/src/llvm-project/llvm/tools/llvm-xray/xray-graph.cpp
@@ -523,7 +523,7 @@ auto &GR = *GROrError; std::error_code EC; - raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::OF_Text); + raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + GraphOutput + "' for writing.", EC);
diff --git a/src/llvm-project/llvm/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp b/src/llvm-project/llvm/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp index 7ffc75e..c61b509 100644 --- a/src/llvm-project/llvm/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp +++ b/src/llvm-project/llvm/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp
@@ -9,15 +9,14 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Regex.h" #include "llvm/Support/YAMLTraits.h" -#include <cassert> #include <string> -llvm::Regex Infinity("^[-+]?(\\.inf|\\.Inf|\\.INF)$"); -llvm::Regex Base8("^0o[0-7]+$"); -llvm::Regex Base16("^0x[0-9a-fA-F]+$"); -llvm::Regex Float("^[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$"); - inline bool isNumericRegex(llvm::StringRef S) { + static llvm::Regex Infinity("^[-+]?(\\.inf|\\.Inf|\\.INF)$"); + static llvm::Regex Base8("^0o[0-7]+$"); + static llvm::Regex Base16("^0x[0-9a-fA-F]+$"); + static llvm::Regex Float( + "^[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$"); if (S.equals(".nan") || S.equals(".NaN") || S.equals(".NAN")) return true;
diff --git a/src/llvm-project/llvm/tools/lto/CMakeLists.txt b/src/llvm-project/llvm/tools/lto/CMakeLists.txt index 2963f97..0af29ad 100644 --- a/src/llvm-project/llvm/tools/lto/CMakeLists.txt +++ b/src/llvm-project/llvm/tools/lto/CMakeLists.txt
@@ -21,8 +21,16 @@ set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/lto.exports) -add_llvm_library(LTO SHARED INSTALL_WITH_TOOLCHAIN ${SOURCES} DEPENDS - intrinsics_gen) +if(CMAKE_SYSTEM_NAME STREQUAL AIX) + set(LTO_LIBRARY_TYPE MODULE) + set(LTO_LIBRARY_NAME libLTO) + else() + set(LTO_LIBRARY_TYPE SHARED) + set(LTO_LIBRARY_NAME LTO) +endif() + +add_llvm_library(${LTO_LIBRARY_NAME} ${LTO_LIBRARY_TYPE} INSTALL_WITH_TOOLCHAIN + ${SOURCES} DEPENDS intrinsics_gen) install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/lto.h DESTINATION include/llvm-c
diff --git a/src/llvm-project/llvm/tools/lto/lto.cpp b/src/llvm-project/llvm/tools/lto/lto.cpp index 688c7f2..2a560a0 100644 --- a/src/llvm-project/llvm/tools/lto/lto.cpp +++ b/src/llvm-project/llvm/tools/lto/lto.cpp
@@ -63,8 +63,12 @@ // *** Not thread safe *** static bool initialized = false; -// Holds the command-line option parsing state of the LTO module. -static bool parsedOptions = false; +// Represent the state of parsing command line debug options. +static enum class OptParsingState { + NotParsed, // Initial state. + Early, // After lto_set_debug_options is called. + Done // After maybeParseOptions is called. +} optionParsingState = OptParsingState::NotParsed; static LLVMContext *LTOContext = nullptr; @@ -418,10 +422,11 @@ } static void maybeParseOptions(lto_code_gen_t cg) { - if (!parsedOptions) { + if (optionParsingState != OptParsingState::Done) { + // Parse options if any were set by the lto_codegen_debug_options* function. unwrap(cg)->parseCodeGenDebugOptions(); lto_add_attrs(cg); - parsedOptions = true; + optionParsingState = OptParsingState::Done; } } @@ -460,7 +465,22 @@ return !unwrap(cg)->compile_to_file(name); } +void lto_set_debug_options(const char *const *options, int number) { + assert(optionParsingState == OptParsingState::NotParsed && + "option processing already happened"); + // Need to put each suboption in a null-terminated string before passing to + // parseCommandLineOptions(). + std::vector<std::string> Options; + for (int i = 0; i < number; ++i) + Options.push_back(options[i]); + + llvm::parseCommandLineOptions(Options); + optionParsingState = OptParsingState::Early; +} + void lto_codegen_debug_options(lto_code_gen_t cg, const char *opt) { + assert(optionParsingState != OptParsingState::Early && + "early option processing already happened"); SmallVector<StringRef, 4> Options; for (std::pair<StringRef, StringRef> o = getToken(opt); !o.first.empty(); o = getToken(o.second)) @@ -471,6 +491,8 @@ void lto_codegen_debug_options_array(lto_code_gen_t cg, const char *const *options, int number) { + assert(optionParsingState != OptParsingState::Early && + "early option processing already happened"); SmallVector<StringRef, 4> Options; for (int i = 0; i < number; ++i) Options.push_back(options[i]); @@ -564,8 +586,7 @@ // if options were requested, set them if (number && options) { std::vector<const char *> CodegenArgv(1, "libLTO"); - for (auto Arg : ArrayRef<const char *>(options, number)) - CodegenArgv.push_back(Arg); + append_range(CodegenArgv, ArrayRef<const char *>(options, number)); cl::ParseCommandLineOptions(CodegenArgv.size(), CodegenArgv.data()); } }
diff --git a/src/llvm-project/llvm/tools/lto/lto.exports b/src/llvm-project/llvm/tools/lto/lto.exports index 1f0a6b2..1948bba 100644 --- a/src/llvm-project/llvm/tools/lto/lto.exports +++ b/src/llvm-project/llvm/tools/lto/lto.exports
@@ -43,6 +43,7 @@ lto_codegen_compile_optimized lto_codegen_set_should_internalize lto_codegen_set_should_embed_uselists +lto_set_debug_options LLVMCreateDisasm LLVMCreateDisasmCPU LLVMDisasmDispose
diff --git a/src/llvm-project/llvm/tools/obj2yaml/elf2yaml.cpp b/src/llvm-project/llvm/tools/obj2yaml/elf2yaml.cpp index c85e265..028758d 100644 --- a/src/llvm-project/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/src/llvm-project/llvm/tools/obj2yaml/elf2yaml.cpp
@@ -31,6 +31,7 @@ DenseMap<StringRef, uint32_t> UsedSectionNames; std::vector<std::string> SectionNames; + Optional<uint32_t> ShStrTabIndex; DenseMap<StringRef, uint32_t> UsedSymbolNames; std::vector<std::string> SymbolNames; @@ -289,6 +290,13 @@ Sections = *SectionsOrErr; SectionNames.resize(Sections.size()); + if (Sections.size() > 0) { + ShStrTabIndex = Obj.getHeader().e_shstrndx; + if (*ShStrTabIndex == ELF::SHN_XINDEX) + ShStrTabIndex = Sections[0].sh_link; + // TODO: Set EShStrndx if the value doesn't represent a real section. + } + // Normally an object that does not have sections has e_shnum == 0. // Also, e_shnum might be 0, when the the number of entries in the section // header table is larger than or equal to SHN_LORESERVE (0xff00). In this @@ -398,6 +406,29 @@ return !shouldPrintSection(S, Sections[S.OriginalSecNdx], Y->DWARF); }); + // The section header string table by default is assumed to be called + // ".shstrtab" and be in its own unique section. However, it's possible for it + // to be called something else and shared with another section. If the name + // isn't the default, provide this in the YAML. + if (ShStrTabIndex && *ShStrTabIndex != ELF::SHN_UNDEF && + *ShStrTabIndex < Sections.size()) { + StringRef ShStrtabName; + if (SymTab && SymTab->sh_link == *ShStrTabIndex) { + // Section header string table is shared with the symbol table. Use that + // section's name (usually .strtab). + ShStrtabName = cantFail(Obj.getSectionName(Sections[SymTab->sh_link])); + } else if (DynSymTab && DynSymTab->sh_link == *ShStrTabIndex) { + // Section header string table is shared with the dynamic symbol table. + // Use that section's name (usually .dynstr). + ShStrtabName = cantFail(Obj.getSectionName(Sections[DynSymTab->sh_link])); + } else { + // Otherwise, the section name potentially needs uniquifying. + ShStrtabName = cantFail(getUniquedSectionName(Sections[*ShStrTabIndex])); + } + if (ShStrtabName != ".shstrtab") + Y->Header.SectionHeaderStringTable = ShStrtabName; + } + Y->Chunks = std::move(Chunks); return Y.release(); } @@ -486,6 +517,12 @@ if (ELFYAML::RawContentSection *RawSec = dyn_cast<ELFYAML::RawContentSection>(C.get())) { + // FIXME: The dumpDebug* functions should take the content as stored in + // RawSec. Currently, they just use the last section with the matching + // name, which defeats this attempt to skip reading a section header + // string table with the same name as a DWARF section. + if (ShStrTabIndex && RawSec->OriginalSecNdx == *ShStrTabIndex) + continue; Error Err = Error::success(); cantFail(std::move(Err)); @@ -852,16 +889,16 @@ DataExtractor::Cursor Cur(0); while (Cur && Cur.tell() < Content.size()) { uint64_t Address = Data.getAddress(Cur); - uint32_t NumBlocks = Data.getULEB128(Cur); + uint64_t NumBlocks = Data.getULEB128(Cur); std::vector<ELFYAML::BBAddrMapEntry::BBEntry> BBEntries; // Read the specified number of BB entries, or until decoding fails. - for (uint32_t BlockID = 0; Cur && BlockID < NumBlocks; ++BlockID) { - uint32_t Offset = Data.getULEB128(Cur); - uint32_t Size = Data.getULEB128(Cur); - uint32_t Metadata = Data.getULEB128(Cur); + for (uint64_t BlockID = 0; Cur && BlockID < NumBlocks; ++BlockID) { + uint64_t Offset = Data.getULEB128(Cur); + uint64_t Size = Data.getULEB128(Cur); + uint64_t Metadata = Data.getULEB128(Cur); BBEntries.push_back({Offset, Size, Metadata}); } - Entries.push_back({Address, BBEntries}); + Entries.push_back({Address, /*NumBlocks=*/{}, BBEntries}); } if (!Cur) { @@ -985,41 +1022,31 @@ if (!ContentOrErr) return ContentOrErr.takeError(); ArrayRef<uint8_t> Content = *ContentOrErr; - + const uint32_t SizeOfEntry = ELFYAML::getDefaultShEntSize<ELFT>( + Obj.getHeader().e_machine, S->Type, S->Name); // Dump the section by using the Content key when it is truncated. // There is no need to create either "Content" or "Entries" fields when the // section is empty. - if (Content.empty() || Content.size() % 16 != 0) { + if (Content.empty() || Content.size() % SizeOfEntry != 0) { if (!Content.empty()) S->Content = yaml::BinaryRef(Content); return S.release(); } - std::vector<ELFYAML::CallGraphEntry> Entries(Content.size() / 16); + std::vector<ELFYAML::CallGraphEntryWeight> Entries(Content.size() / + SizeOfEntry); DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0); DataExtractor::Cursor Cur(0); - auto ReadEntry = [&](ELFYAML::CallGraphEntry &E) { - uint32_t FromSymIndex = Data.getU32(Cur); - uint32_t ToSymIndex = Data.getU32(Cur); + auto ReadEntry = [&](ELFYAML::CallGraphEntryWeight &E) { E.Weight = Data.getU64(Cur); if (!Cur) { consumeError(Cur.takeError()); return false; } - - Expected<StringRef> From = getSymbolName(Shdr->sh_link, FromSymIndex); - Expected<StringRef> To = getSymbolName(Shdr->sh_link, ToSymIndex); - if (From && To) { - E.From = *From; - E.To = *To; - return true; - } - consumeError(From.takeError()); - consumeError(To.takeError()); - return false; + return true; }; - for (ELFYAML::CallGraphEntry &E : Entries) { + for (ELFYAML::CallGraphEntryWeight &E : Entries) { if (ReadEntry(E)) continue; S->Content = yaml::BinaryRef(Content); @@ -1191,7 +1218,7 @@ Elf_Note Note(*Header); Entries.push_back( - {Note.getName(), Note.getDesc(), (llvm::yaml::Hex32)Note.getType()}); + {Note.getName(), Note.getDesc(), (ELFYAML::ELF_NT)Note.getType()}); Content = Content.drop_front(Header->getSize()); }
diff --git a/src/llvm-project/llvm/tools/obj2yaml/macho2yaml.cpp b/src/llvm-project/llvm/tools/obj2yaml/macho2yaml.cpp index 4934743..b7289bf 100644 --- a/src/llvm-project/llvm/tools/obj2yaml/macho2yaml.cpp +++ b/src/llvm-project/llvm/tools/obj2yaml/macho2yaml.cpp
@@ -226,7 +226,7 @@ auto Start = LoadCmd.Ptr + sizeof(StructType); auto MaxSize = LoadCmd.C.cmdsize - sizeof(StructType); auto Size = strnlen(Start, MaxSize); - LC.PayloadString = StringRef(Start, Size).str(); + LC.Content = StringRef(Start, Size).str(); return Start + Size; }
diff --git a/src/llvm-project/llvm/tools/obj2yaml/obj2yaml.cpp b/src/llvm-project/llvm/tools/obj2yaml/obj2yaml.cpp index da70450..ff6b470 100644 --- a/src/llvm-project/llvm/tools/obj2yaml/obj2yaml.cpp +++ b/src/llvm-project/llvm/tools/obj2yaml/obj2yaml.cpp
@@ -36,7 +36,7 @@ static Error dumpInput(StringRef File) { ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = - MemoryBuffer::getFileOrSTDIN(File, /*FileSize=*/-1, + MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (std::error_code EC = FileOrErr.getError()) return errorCodeToError(EC);
diff --git a/src/llvm-project/llvm/tools/obj2yaml/wasm2yaml.cpp b/src/llvm-project/llvm/tools/obj2yaml/wasm2yaml.cpp index 205ec1e..5b4f672 100644 --- a/src/llvm-project/llvm/tools/obj2yaml/wasm2yaml.cpp +++ b/src/llvm-project/llvm/tools/obj2yaml/wasm2yaml.cpp
@@ -34,7 +34,7 @@ static WasmYAML::Limits makeLimits(const wasm::WasmLimits &Limits) { WasmYAML::Limits L; L.Flags = Limits.Flags; - L.Initial = Limits.Initial; + L.Minimum = Limits.Minimum; L.Maximum = Limits.Maximum; return L; } @@ -100,7 +100,7 @@ SegmentInfo.Name = Segment.Data.Name; SegmentInfo.Index = SegmentIndex; SegmentInfo.Alignment = Segment.Data.Alignment; - SegmentInfo.Flags = Segment.Data.LinkerFlags; + SegmentInfo.Flags = Segment.Data.LinkingFlags; LinkingSec->SegmentInfos.push_back(SegmentInfo); } if (Segment.Data.Comdat != UINT32_MAX) { @@ -132,7 +132,7 @@ case wasm::WASM_SYMBOL_TYPE_FUNCTION: case wasm::WASM_SYMBOL_TYPE_GLOBAL: case wasm::WASM_SYMBOL_TYPE_TABLE: - case wasm::WASM_SYMBOL_TYPE_EVENT: + case wasm::WASM_SYMBOL_TYPE_TAG: Info.ElementIndex = Symbol.ElementIndex; break; case wasm::WASM_SYMBOL_TYPE_SECTION: @@ -238,9 +238,9 @@ Im.GlobalImport.Type = Import.Global.Type; Im.GlobalImport.Mutable = Import.Global.Mutable; break; - case wasm::WASM_EXTERNAL_EVENT: - Im.EventImport.Attribute = Import.Event.Attribute; - Im.EventImport.SigIndex = Import.Event.SigIndex; + case wasm::WASM_EXTERNAL_TAG: + Im.TagImport.Attribute = Import.Tag.Attribute; + Im.TagImport.SigIndex = Import.Tag.SigIndex; break; case wasm::WASM_EXTERNAL_TABLE: // FIXME: Currently we always output an index of 0 for any imported @@ -280,16 +280,16 @@ S = std::move(MemorySec); break; } - case wasm::WASM_SEC_EVENT: { - auto EventSec = std::make_unique<WasmYAML::EventSection>(); - for (auto &Event : Obj.events()) { - WasmYAML::Event E; - E.Index = Event.Index; - E.Attribute = Event.Type.Attribute; - E.SigIndex = Event.Type.SigIndex; - EventSec->Events.push_back(E); + case wasm::WASM_SEC_TAG: { + auto TagSec = std::make_unique<WasmYAML::TagSection>(); + for (auto &Tag : Obj.tags()) { + WasmYAML::Tag T; + T.Index = Tag.Index; + T.Attribute = Tag.Type.Attribute; + T.SigIndex = Tag.Type.SigIndex; + TagSec->Tags.push_back(T); } - S = std::move(EventSec); + S = std::move(TagSec); break; } case wasm::WASM_SEC_GLOBAL: { @@ -327,11 +327,11 @@ auto ElemSec = std::make_unique<WasmYAML::ElemSection>(); for (auto &Segment : Obj.elements()) { WasmYAML::ElemSegment Seg; - Seg.TableIndex = Segment.TableIndex; + Seg.Flags = Segment.Flags; + Seg.TableNumber = Segment.TableNumber; + Seg.ElemKind = Segment.ElemKind; Seg.Offset = Segment.Offset; - for (auto &Func : Segment.Functions) { - Seg.Functions.push_back(Func); - } + append_range(Seg.Functions, Segment.Functions); ElemSec->Segments.push_back(Seg); } S = std::move(ElemSec);
diff --git a/src/llvm-project/llvm/tools/obj2yaml/xcoff2yaml.cpp b/src/llvm-project/llvm/tools/obj2yaml/xcoff2yaml.cpp index dd9d8e8..0309856 100644 --- a/src/llvm-project/llvm/tools/obj2yaml/xcoff2yaml.cpp +++ b/src/llvm-project/llvm/tools/obj2yaml/xcoff2yaml.cpp
@@ -55,7 +55,7 @@ for (const SymbolRef &S : Obj.symbols()) { DataRefImpl SymbolDRI = S.getRawDataRefImpl(); - const XCOFFSymbolEntry *SymbolEntPtr = Obj.toSymbolEntry(SymbolDRI); + const XCOFFSymbolRef SymbolEntRef = Obj.toSymbolRef(SymbolDRI); XCOFFYAML::Symbol Sym; Expected<StringRef> SymNameRefOrErr = Obj.getSymbolName(SymbolDRI); @@ -64,18 +64,18 @@ } Sym.SymbolName = SymNameRefOrErr.get(); - Sym.Value = SymbolEntPtr->Value; + Sym.Value = SymbolEntRef.getValue(); Expected<StringRef> SectionNameRefOrErr = - Obj.getSymbolSectionName(SymbolEntPtr); + Obj.getSymbolSectionName(SymbolEntRef); if (!SectionNameRefOrErr) return errorToErrorCode(SectionNameRefOrErr.takeError()); Sym.SectionName = SectionNameRefOrErr.get(); - Sym.Type = SymbolEntPtr->SymbolType; - Sym.StorageClass = SymbolEntPtr->StorageClass; - Sym.NumberOfAuxEntries = SymbolEntPtr->NumberOfAuxEntries; + Sym.Type = SymbolEntRef.getSymbolType(); + Sym.StorageClass = SymbolEntRef.getStorageClass(); + Sym.NumberOfAuxEntries = SymbolEntRef.getNumberOfAuxEntries(); Symbols.push_back(Sym); }
diff --git a/src/llvm-project/llvm/tools/opt/NewPMDriver.cpp b/src/llvm-project/llvm/tools/opt/NewPMDriver.cpp index 401a58f..8b1fbd0 100644 --- a/src/llvm-project/llvm/tools/opt/NewPMDriver.cpp +++ b/src/llvm-project/llvm/tools/opt/NewPMDriver.cpp
@@ -52,9 +52,19 @@ cl::value_desc("filename")); } // namespace llvm -static cl::opt<bool> - DebugPM("debug-pass-manager", cl::Hidden, - cl::desc("Print pass management debugging information")); +enum class DebugLogging { None, Normal, Verbose, Quiet }; + +static cl::opt<DebugLogging> DebugPM( + "debug-pass-manager", cl::Hidden, cl::ValueOptional, + cl::desc("Print pass management debugging information"), + cl::init(DebugLogging::None), + cl::values( + clEnumValN(DebugLogging::Normal, "", ""), + clEnumValN(DebugLogging::Quiet, "quiet", + "Skip printing info about analyses"), + clEnumValN( + DebugLogging::Verbose, "verbose", + "Print extra information about adaptors and pass managers"))); static cl::list<std::string> PassPlugins("load-pass-plugin", @@ -121,11 +131,13 @@ // Individual pipeline tuning options. extern cl::opt<bool> DisableLoopUnrolling; +namespace llvm { extern cl::opt<PGOKind> PGOKindFlag; extern cl::opt<std::string> ProfileFile; extern cl::opt<CSPGOKind> CSPGOKindFlag; extern cl::opt<std::string> CSProfileGenFile; extern cl::opt<bool> DisableBasicAA; +} // namespace llvm static cl::opt<std::string> ProfileRemappingFile("profile-remapping-file", @@ -137,10 +149,6 @@ static cl::opt<bool> PseudoProbeForProfiling( "new-pm-pseudo-probe-for-profiling", cl::init(false), cl::Hidden, cl::desc("Emit pseudo probes to enable PGO profile generation.")); -static cl::opt<bool> UniqueInternalLinkageNames( - "new-pm-unique-internal-linkage-names", cl::init(false), cl::Hidden, - cl::desc("Uniqueify Internal Linkage Symbol Names by appending the MD5 " - "hash of the module path.")); /// @}} template <typename PassManagerT> @@ -235,7 +243,7 @@ bool ShouldPreserveAssemblyUseListOrder, bool ShouldPreserveBitcodeUseListOrder, bool EmitSummaryIndex, bool EmitModuleHash, - bool EnableDebugify, bool Coroutines) { + bool EnableDebugify) { bool VerifyEachPass = VK == VK_VerifyEachPass; Optional<PGOOptions> P; @@ -279,9 +287,18 @@ P->CSAction = PGOOptions::CSIRUse; } } + LoopAnalysisManager LAM; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager MAM; + PassInstrumentationCallbacks PIC; - StandardInstrumentations SI(DebugPM, VerifyEachPass); - SI.registerCallbacks(PIC); + PrintPassOptions PrintPassOpts; + PrintPassOpts.Verbose = DebugPM == DebugLogging::Verbose; + PrintPassOpts.SkipAnalyses = DebugPM == DebugLogging::Quiet; + StandardInstrumentations SI(DebugPM != DebugLogging::None, VerifyEachPass, + PrintPassOpts); + SI.registerCallbacks(PIC, &FAM); DebugifyEachInstrumentation Debugify; if (DebugifyEach) Debugify.registerCallbacks(PIC); @@ -291,9 +308,7 @@ // to false above so we shouldn't necessarily need to check whether or not the // option has been enabled. PTO.LoopUnrolling = !DisableLoopUnrolling; - PTO.Coroutines = Coroutines; - PTO.UniqueLinkageNames = UniqueInternalLinkageNames; - PassBuilder PB(DebugPM, TM, PTO, P, &PIC); + PassBuilder PB(TM, PTO, P, &PIC); registerEPCallbacks(PB); // Load requested pass plugins and let them register pass builder callbacks @@ -378,11 +393,6 @@ } } - LoopAnalysisManager LAM(DebugPM); - FunctionAnalysisManager FAM(DebugPM); - CGSCCAnalysisManager CGAM(DebugPM); - ModuleAnalysisManager MAM(DebugPM); - // Register the AA manager first so that our version is the one used. FAM.registerPass([&] { return std::move(AA); }); // Register our TargetLibraryInfoImpl. @@ -395,7 +405,7 @@ PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); - ModulePassManager MPM(DebugPM); + ModulePassManager MPM; if (VK > VK_NoVerifier) MPM.addPass(VerifierPass()); if (EnableDebugify) @@ -463,3 +473,8 @@ return true; } + +void llvm::printPasses(raw_ostream &OS) { + PassBuilder PB; + PB.printPassNames(OS); +}
diff --git a/src/llvm-project/llvm/tools/opt/NewPMDriver.h b/src/llvm-project/llvm/tools/opt/NewPMDriver.h index 87a71ce..056f7d6 100644 --- a/src/llvm-project/llvm/tools/opt/NewPMDriver.h +++ b/src/llvm-project/llvm/tools/opt/NewPMDriver.h
@@ -54,6 +54,8 @@ enum CSPGOKind { NoCSPGO, CSInstrGen, CSInstrUse }; } +void printPasses(raw_ostream &OS); + /// Driver function to run the new pass manager over a module. /// /// This function only exists factored away from opt.cpp in order to prevent @@ -71,7 +73,7 @@ bool ShouldPreserveAssemblyUseListOrder, bool ShouldPreserveBitcodeUseListOrder, bool EmitSummaryIndex, bool EmitModuleHash, - bool EnableDebugify, bool Coroutines); + bool EnableDebugify); } // namespace llvm #endif
diff --git a/src/llvm-project/llvm/tools/opt/opt.cpp b/src/llvm-project/llvm/tools/opt/opt.cpp index 5cb59f8..094f517 100644 --- a/src/llvm-project/llvm/tools/opt/opt.cpp +++ b/src/llvm-project/llvm/tools/opt/opt.cpp
@@ -66,14 +66,15 @@ // The OptimizationList is automatically populated with registered Passes by the // PassNameParser. -// -static cl::list<const PassInfo*, bool, PassNameParser> -PassList(cl::desc("Optimizations available:")); +static cl::list<const PassInfo *, bool, PassNameParser> PassList(cl::desc( + "Optimizations available (use '-passes=' for the new pass manager)")); -static cl::opt<bool> - EnableNewPassManager("enable-new-pm", - cl::desc("Enable the new pass manager"), - cl::init(LLVM_ENABLE_NEW_PASS_MANAGER)); +static cl::opt<bool> EnableNewPassManager( + "enable-new-pm", + cl::desc("Enable the new pass manager, translating " + "'opt -foo' to 'opt -passes=foo'. This is strictly for the new PM " + "migration, use '-passes=' when possible."), + cl::init(LLVM_ENABLE_NEW_PASS_MANAGER)); // This flag specifies a textual description of the optimization pass pipeline // to run over the module. This flag switches opt to use the new pass manager @@ -81,11 +82,14 @@ // pass management. static cl::opt<std::string> PassPipeline( "passes", - cl::desc("A textual description of the pass pipeline for optimizing"), - cl::Hidden); + cl::desc( + "A textual description of the pass pipeline. To have analysis passes " + "available before a certain pass, add 'require<foo-analysis>'.")); -// Other command line options... -// +static cl::opt<bool> PrintPasses("print-passes", + cl::desc("Print available passes that can be " + "specified in -passes=foo and exit")); + static cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input bitcode file>"), cl::init("-"), cl::value_desc("filename")); @@ -142,44 +146,46 @@ StripNamedMetadata("strip-named-metadata", cl::desc("Strip module-level named metadata")); -static cl::opt<bool> DisableInline("disable-inlining", - cl::desc("Do not run the inliner pass")); +static cl::opt<bool> + DisableInline("disable-inlining", + cl::desc("Do not run the inliner pass (legacy PM only)")); static cl::opt<bool> DisableOptimizations("disable-opt", cl::desc("Do not run any optimization passes")); -static cl::opt<bool> -StandardLinkOpts("std-link-opts", - cl::desc("Include the standard link time optimizations")); +static cl::opt<bool> StandardLinkOpts( + "std-link-opts", + cl::desc("Include the standard link time optimizations (legacy PM only)")); static cl::opt<bool> -OptLevelO0("O0", - cl::desc("Optimization level 0. Similar to clang -O0")); + OptLevelO0("O0", cl::desc("Optimization level 0. Similar to clang -O0. " + "Use -passes='default<O0>' for the new PM")); static cl::opt<bool> -OptLevelO1("O1", - cl::desc("Optimization level 1. Similar to clang -O1")); + OptLevelO1("O1", cl::desc("Optimization level 1. Similar to clang -O1. " + "Use -passes='default<O1>' for the new PM")); static cl::opt<bool> -OptLevelO2("O2", - cl::desc("Optimization level 2. Similar to clang -O2")); + OptLevelO2("O2", cl::desc("Optimization level 2. Similar to clang -O2. " + "Use -passes='default<O2>' for the new PM")); static cl::opt<bool> -OptLevelOs("Os", - cl::desc("Like -O2 with extra optimizations for size. Similar to clang -Os")); + OptLevelOs("Os", cl::desc("Like -O2 but size-conscious. Similar to clang " + "-Os. Use -passes='default<Os>' for the new PM")); + +static cl::opt<bool> OptLevelOz( + "Oz", + cl::desc("Like -O2 but optimize for code size above all else. Similar to " + "clang -Oz. Use -passes='default<Oz>' for the new PM")); static cl::opt<bool> -OptLevelOz("Oz", - cl::desc("Like -Os but reduces code size further. Similar to clang -Oz")); + OptLevelO3("O3", cl::desc("Optimization level 3. Similar to clang -O3. " + "Use -passes='default<O3>' for the new PM")); -static cl::opt<bool> -OptLevelO3("O3", - cl::desc("Optimization level 3. Similar to clang -O3")); - -static cl::opt<unsigned> -CodeGenOptLevel("codegen-opt-level", - cl::desc("Override optimization level for codegen hooks")); +static cl::opt<unsigned> CodeGenOptLevel( + "codegen-opt-level", + cl::desc("Override optimization level for codegen hooks, legacy PM only")); static cl::opt<std::string> TargetTriple("mtriple", cl::desc("Override target triple for module")); @@ -205,13 +211,31 @@ cl::ZeroOrMore); static cl::opt<bool> -AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization")); + AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization. " + "Legacy pass manager only.")); static cl::opt<bool> EnableDebugify( "enable-debugify", cl::desc( "Start the pipeline with debugify and end it with check-debugify")); +static cl::opt<bool> VerifyDebugInfoPreserve( + "verify-debuginfo-preserve", + cl::desc("Start the pipeline with collecting and end it with checking of " + "debug info preservation.")); + +static cl::opt<bool> VerifyEachDebugInfoPreserve( + "verify-each-debuginfo-preserve", + cl::desc("Start each pass with collecting and end it with checking of " + "debug info preservation.")); + +static cl::opt<std::string> + VerifyDIPreserveExport("verify-di-preserve-export", + cl::desc("Export debug info preservation failures into " + "specified (JSON) file (should be abs path as we use" + " append mode to insert new JSON objects)"), + cl::value_desc("filename"), cl::init("")); + static cl::opt<bool> PrintBreakpoints("print-breakpoints-for-testing", cl::desc("Print select breakpoints location for testing")); @@ -231,10 +255,10 @@ cl::desc("Preserve use-list order when writing LLVM assembly."), cl::init(false), cl::Hidden); -static cl::opt<bool> - RunTwice("run-twice", - cl::desc("Run all passes twice, re-using the same pass manager."), - cl::init(false), cl::Hidden); +static cl::opt<bool> RunTwice("run-twice", + cl::desc("Run all passes twice, re-using the " + "same pass manager (legacy PM only)."), + cl::init(false), cl::Hidden); static cl::opt<bool> DiscardValueNames( "discard-value-names", @@ -289,6 +313,7 @@ cl::desc("The format used for serializing remarks (default: YAML)"), cl::value_desc("format"), cl::init("yaml")); +namespace llvm { cl::opt<PGOKind> PGOKindFlag("pgo-kind", cl::init(NoPGO), cl::Hidden, cl::desc("The kind of profile guided optimization"), @@ -317,6 +342,7 @@ "cs-profilegen-file", cl::desc("Path to the instrumented context sensitive profile."), cl::Hidden); +} // namespace llvm static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { // Add the pass to the pass manager... @@ -477,9 +503,8 @@ "amdgpu-unify-metadata", "amdgpu-printf-runtime-binding", "amdgpu-always-inline"}; - for (const auto &P : PassNameExactToIgnore) - if (Pass == P) - return false; + if (llvm::is_contained(PassNameExactToIgnore, Pass)) + return false; std::vector<StringRef> PassNamePrefix = { "x86-", "xcore-", "wasm-", "systemz-", "ppc-", "nvvm-", "nvptx-", @@ -490,24 +515,22 @@ "safe-stack", "cost-model", "codegenprepare", "interleaved-load-combine", "unreachableblockelim", "verify-safepoint-ir", - "divergence", "atomic-expand", + "atomic-expand", "expandvp", "hardware-loops", "type-promotion", "mve-tail-predication", "interleaved-access", "global-merge", "pre-isel-intrinsic-lowering", "expand-reductions", "indirectbr-expand", "generic-to-nvvm", "expandmemcmp", "loop-reduce", "lower-amx-type", - "polyhedral-info"}; + "pre-amx-config", "lower-amx-intrinsics", + "polyhedral-info", "replace-with-veclib"}; for (const auto &P : PassNamePrefix) if (Pass.startswith(P)) return true; for (const auto &P : PassNameContain) if (Pass.contains(P)) return true; - for (const auto &P : PassNameExact) - if (Pass == P) - return true; - return false; + return llvm::is_contained(PassNameExact, Pass); } // For use in NPM transition. @@ -529,8 +552,6 @@ // Enable debug stream buffering. EnableDebugBuffering = true; - LLVMContext Context; - InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmPrinters(); @@ -570,10 +591,12 @@ initializePostInlineEntryExitInstrumenterPass(Registry); initializeUnreachableBlockElimLegacyPassPass(Registry); initializeExpandReductionsPass(Registry); + initializeExpandVectorPredicationPass(Registry); initializeWasmEHPreparePass(Registry); initializeWriteBitcodePassPass(Registry); initializeHardwareLoopsPass(Registry); initializeTypePromotionPass(Registry); + initializeReplaceWithVeclibLegacyPass(Registry); #ifdef BUILD_EXAMPLES initializeExampleIRTransforms(Registry); @@ -582,11 +605,21 @@ cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .bc modular optimizer and analysis printer\n"); + LLVMContext Context; + if (AnalyzeOnly && NoOutput) { errs() << argv[0] << ": analyze mode conflicts with no-output mode.\n"; return 1; } + // FIXME: once the legacy PM code is deleted, move runPassPipeline() here and + // construct the PassBuilder before parsing IR so we can reuse the same + // PassBuilder for print passes. + if (PrintPasses) { + printPasses(outs()); + return 0; + } + TimeTracerRAII TimeTracer(argv[0]); SMDiagnostic Err; @@ -654,7 +687,8 @@ // specified by an internal option. This is normally done during LTO which is // not performed via opt. updateVCallVisibilityInModule(*M, - /* WholeProgramVisibilityEnabledInLTO */ false); + /* WholeProgramVisibilityEnabledInLTO */ false, + /* DynamicExportSymbols */ {}); // Figure out what stream we are supposed to write to... std::unique_ptr<ToolOutputFile> Out; @@ -669,8 +703,8 @@ OutputFilename = "-"; std::error_code EC; - sys::fs::OpenFlags Flags = OutputAssembly ? sys::fs::OF_Text - : sys::fs::OF_None; + sys::fs::OpenFlags Flags = + OutputAssembly ? sys::fs::OF_TextWithCRLF : sys::fs::OF_None; Out.reset(new ToolOutputFile(OutputFilename, EC, Flags)); if (EC) { errs() << EC.message() << '\n'; @@ -746,7 +780,16 @@ if ((EnableNewPassManager && !shouldForceLegacyPM()) || PassPipeline.getNumOccurrences() > 0) { if (AnalyzeOnly) { - errs() << "Cannot specify -analyze under new pass manager\n"; + errs() << "Cannot specify -analyze under new pass manager, either " + "specify '-enable-new-pm=0', or use the corresponding new pass " + "manager pass, e.g. '-passes=print<scalar-evolution>'. For a " + "full list of passes, see the '--print-passes' flag.\n"; + return 1; + } + if (legacy::debugPassSpecified()) { + errs() + << "-debug-pass does not work with the new PM, either use " + "-debug-pass-manager, or use the legacy PM (-enable-new-pm=0)\n"; return 1; } if (PassPipeline.getNumOccurrences() > 0 && PassList.size() > 0) { @@ -788,7 +831,7 @@ ThinLinkOut.get(), RemarksFile.get(), PassPipeline, Passes, OK, VK, PreserveAssemblyUseListOrder, PreserveBitcodeUseListOrder, EmitSummaryIndex, - EmitModuleHash, EnableDebugify, Coroutines) + EmitModuleHash, EnableDebugify) ? 0 : 1; } @@ -797,10 +840,21 @@ // about to build. If the -debugify-each option is set, wrap each pass with // the (-check)-debugify passes. DebugifyCustomPassManager Passes; - if (DebugifyEach) - Passes.enableDebugifyEach(); + DebugifyStatsMap DIStatsMap; + DebugInfoPerPassMap DIPreservationMap; + if (DebugifyEach) { + Passes.setDebugifyMode(DebugifyMode::SyntheticDebugInfo); + Passes.setDIStatsMap(DIStatsMap); + } else if (VerifyEachDebugInfoPreserve) { + Passes.setDebugifyMode(DebugifyMode::OriginalDebugInfo); + Passes.setDIPreservationMap(DIPreservationMap); + if (!VerifyDIPreserveExport.empty()) + Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport); + } - bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach; + bool AddOneTimeDebugifyPasses = + (EnableDebugify && !DebugifyEach) || + (VerifyDebugInfoPreserve && !VerifyEachDebugInfoPreserve); Passes.add(new TargetLibraryInfoWrapperPass(TLII)); @@ -808,8 +862,17 @@ Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis() : TargetIRAnalysis())); - if (AddOneTimeDebugifyPasses) - Passes.add(createDebugifyModulePass()); + if (AddOneTimeDebugifyPasses) { + if (EnableDebugify) { + Passes.setDIStatsMap(DIStatsMap); + Passes.add(createDebugifyModulePass()); + } else if (VerifyDebugInfoPreserve) { + Passes.setDIPreservationMap(DIPreservationMap); + Passes.add(createDebugifyModulePass( + DebugifyMode::OriginalDebugInfo, "", + &(Passes.getDebugInfoPerPassMap()))); + } + } std::unique_ptr<legacy::FunctionPassManager> FPasses; if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || @@ -953,8 +1016,17 @@ if (!NoVerify && !VerifyEach) Passes.add(createVerifierPass()); - if (AddOneTimeDebugifyPasses) - Passes.add(createCheckDebugifyModulePass(false)); + if (AddOneTimeDebugifyPasses) { + if (EnableDebugify) + Passes.add(createCheckDebugifyModulePass(false)); + else if (VerifyDebugInfoPreserve) { + if (!VerifyDIPreserveExport.empty()) + Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport); + Passes.add(createCheckDebugifyModulePass( + false, "", nullptr, DebugifyMode::OriginalDebugInfo, + &(Passes.getDebugInfoPerPassMap()), VerifyDIPreserveExport)); + } + } // In run twice mode, we want to make sure the output is bit-by-bit // equivalent if we run the pass manager again, so setup two buffers and
diff --git a/src/llvm-project/llvm/tools/sancov/sancov.cpp b/src/llvm-project/llvm/tools/sancov/sancov.cpp index f1d756f..b274310 100644 --- a/src/llvm-project/llvm/tools/sancov/sancov.cpp +++ b/src/llvm-project/llvm/tools/sancov/sancov.cpp
@@ -726,8 +726,7 @@ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); failIfEmpty(AsmInfo, "no asm info for target " + TripleName); - std::unique_ptr<const MCObjectFileInfo> MOFI(new MCObjectFileInfo); - MCContext Ctx(AsmInfo.get(), MRI.get(), MOFI.get()); + MCContext Ctx(TheTriple, AsmInfo.get(), MRI.get(), STI.get()); std::unique_ptr<MCDisassembler> DisAsm( TheTarget->createMCDisassembler(*STI, Ctx)); failIfEmpty(DisAsm, "no disassembler info for target " + TripleName);
diff --git a/src/llvm-project/llvm/tools/sanstats/sanstats.cpp b/src/llvm-project/llvm/tools/sanstats/sanstats.cpp index 1f154e0..54ad35b 100644 --- a/src/llvm-project/llvm/tools/sanstats/sanstats.cpp +++ b/src/llvm-project/llvm/tools/sanstats/sanstats.cpp
@@ -125,8 +125,8 @@ cl::ParseCommandLineOptions(argc, argv, "Sanitizer Statistics Processing Tool"); - ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = - MemoryBuffer::getFile(ClInputFile, -1, false); + ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile( + ClInputFile, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (!MBOrErr) { errs() << argv[0] << ": " << ClInputFile << ": " << MBOrErr.getError().message() << '\n';
diff --git a/src/llvm-project/llvm/tools/split-file/.clang-tidy b/src/llvm-project/llvm/tools/split-file/.clang-tidy index 87ec2ff..acd9361 100644 --- a/src/llvm-project/llvm/tools/split-file/.clang-tidy +++ b/src/llvm-project/llvm/tools/split-file/.clang-tidy
@@ -1,19 +1,8 @@ -# Almost identical to the top-level .clang-tidy, except that {Member,Parameter,Variable}Case use camelBack. -Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,readability-identifier-naming' +InheritParentConfig: true CheckOptions: - - key: readability-identifier-naming.ClassCase - value: CamelCase - - key: readability-identifier-naming.EnumCase - value: CamelCase - - key: readability-identifier-naming.FunctionCase - value: camelBack - key: readability-identifier-naming.MemberCase value: camelBack - key: readability-identifier-naming.ParameterCase value: camelBack - - key: readability-identifier-naming.UnionCase - value: CamelCase - key: readability-identifier-naming.VariableCase value: camelBack - - key: readability-identifier-naming.IgnoreMainLikeFunctions - value: 1
diff --git a/src/llvm-project/llvm/tools/split-file/split-file.cpp b/src/llvm-project/llvm/tools/split-file/split-file.cpp index a012b4a..3ebbda4 100644 --- a/src/llvm-project/llvm/tools/split-file/split-file.cpp +++ b/src/llvm-project/llvm/tools/split-file/split-file.cpp
@@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h"
diff --git a/src/llvm-project/llvm/tools/verify-uselistorder/verify-uselistorder.cpp b/src/llvm-project/llvm/tools/verify-uselistorder/verify-uselistorder.cpp index cbd2f85..28d3467 100644 --- a/src/llvm-project/llvm/tools/verify-uselistorder/verify-uselistorder.cpp +++ b/src/llvm-project/llvm/tools/verify-uselistorder/verify-uselistorder.cpp
@@ -136,7 +136,7 @@ bool TempFile::writeAssembly(const Module &M) const { LLVM_DEBUG(dbgs() << " - write assembly\n"); std::error_code EC; - raw_fd_ostream OS(Filename, EC, sys::fs::OF_Text); + raw_fd_ostream OS(Filename, EC, sys::fs::OF_TextWithCRLF); if (EC) { errs() << "verify-uselistorder: error: " << EC.message() << "\n"; return true;