Index: COFF/Chunks.cpp =================================================================== --- COFF/Chunks.cpp +++ COFF/Chunks.cpp @@ -346,6 +346,7 @@ void SectionChunk::printDiscardedMessage() const { // Removed by dead-stripping. If it's removed by ICF, ICF already // printed out the name, so don't repeat that here. +// XXX if (Sym && this == Repl) { if (Discarded) message("Discarded comdat symbol " + Sym->getName()); Index: COFF/Config.h =================================================================== --- COFF/Config.h +++ COFF/Config.h @@ -148,6 +148,9 @@ // Used for /lldmap. std::string MapFile; + // Used for /redundancyReport. + std::string RedudancyReportFile; + uint64_t ImageBase = -1; uint64_t StackReserve = 1024 * 1024; uint64_t StackCommit = 4096; Index: COFF/Driver.cpp =================================================================== --- COFF/Driver.cpp +++ COFF/Driver.cpp @@ -944,6 +944,8 @@ Config->WriteSymtab = false; Config->MapFile = getMapFile(Args); + if (auto *Arg = Args.getLastArg(OPT_redundancyReport)) + Config->RedudancyReportFile = Arg->getValue(); if (ErrorCount) return; Index: COFF/Options.td =================================================================== --- COFF/Options.td +++ COFF/Options.td @@ -104,6 +104,7 @@ // Flags for debugging def lldmap : F<"lldmap">; def lldmap_file : Joined<["/", "-"], "lldmap:">; +def redundancyReport: Joined<["/", "-"], "redundancyReport:">; //============================================================================== // The flags below do nothing. They are defined only for link.exe compatibility. Index: COFF/SymbolTable.cpp =================================================================== --- COFF/SymbolTable.cpp +++ COFF/SymbolTable.cpp @@ -245,6 +245,7 @@ } else if (SP == SP_NEW) { replaceBody(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C); } else if (SP == SP_EXISTING && IsCOMDAT && C) { +// XXX here? C->markDiscarded(); // Discard associative chunks that we've parsed so far. No need to recurse // because an associative section cannot have children. Index: COFF/Writer.cpp =================================================================== --- COFF/Writer.cpp +++ COFF/Writer.cpp @@ -128,6 +128,7 @@ void writeSections(); void sortExceptionTable(); void writeBuildId(); + void writeRedundancyReport(); llvm::Optional createSymbol(Defined *D); size_t addEntryToStringTable(StringRef Str); @@ -251,6 +252,7 @@ } writeMapFile(OutputSections); + writeRedundancyReport(); if (auto EC = Buffer->commit()) fatal(EC, "failed to write the output file"); @@ -442,6 +444,7 @@ // Don't write dead symbols or symbols in codeview sections to the symbol // table. + // XXX but do include them in stats if (!Def->isLive()) return None; if (auto *D = dyn_cast(Def)) @@ -825,6 +828,44 @@ BuildId->DI->PDB70.Age = 1; } +void Writer::writeRedundancyReport() { + if (Config->RedudancyReportFile.empty()) + return; + + FILE* f = fopen(Config->RedudancyReportFile.c_str(), "wb"); + + // First, bin chunks by name. + // XXX i think this misses stuff from .obj files never added to the link. + // probably fine. + struct Entry { + int total_size = 0; + int count = 0; + }; + std::map Map; + for (Chunk *C : Symtab->getChunks()) { + auto *SC = dyn_cast(C); + if (!SC || SC->isLive()) + continue; + + Entry& e = Map[C->getDebugName()]; + e.count++; + e.total_size += SC->getSize(); + } + + std::vector> Entries(Map.begin(), Map.end()); + std::sort(Entries.begin(), Entries.end(), + [](const std::pair &a, + const std::pair &b) { + return a.second.total_size > b.second.total_size; + }); + + for (const std::pair& E : Entries) { + fprintf(f, "%d\t%d\t%.*s\n", E.second.total_size, E.second.count, + (int)E.first.size(), E.first.data()); + } + fclose(f); +} + OutputSection *Writer::findSection(StringRef Name) { for (OutputSection *Sec : OutputSections) if (Sec->getName() == Name)