Path: blob/main/contrib/llvm-project/lld/COFF/ICF.cpp
34870 views
//===- ICF.cpp ------------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// ICF is short for Identical Code Folding. That is a size optimization to9// identify and merge two or more read-only sections (typically functions)10// that happened to have the same contents. It usually reduces output size11// by a few percent.12//13// On Windows, ICF is enabled by default.14//15// See ELF/ICF.cpp for the details about the algorithm.16//17//===----------------------------------------------------------------------===//1819#include "ICF.h"20#include "COFFLinkerContext.h"21#include "Chunks.h"22#include "Symbols.h"23#include "lld/Common/ErrorHandler.h"24#include "lld/Common/Timer.h"25#include "llvm/ADT/Hashing.h"26#include "llvm/Support/Debug.h"27#include "llvm/Support/Parallel.h"28#include "llvm/Support/TimeProfiler.h"29#include "llvm/Support/raw_ostream.h"30#include "llvm/Support/xxhash.h"31#include <algorithm>32#include <atomic>33#include <vector>3435using namespace llvm;3637namespace lld::coff {3839class ICF {40public:41ICF(COFFLinkerContext &c) : ctx(c){};42void run();4344private:45void segregate(size_t begin, size_t end, bool constant);4647bool assocEquals(const SectionChunk *a, const SectionChunk *b);4849bool equalsConstant(const SectionChunk *a, const SectionChunk *b);50bool equalsVariable(const SectionChunk *a, const SectionChunk *b);5152bool isEligible(SectionChunk *c);5354size_t findBoundary(size_t begin, size_t end);5556void forEachClassRange(size_t begin, size_t end,57std::function<void(size_t, size_t)> fn);5859void forEachClass(std::function<void(size_t, size_t)> fn);6061std::vector<SectionChunk *> chunks;62int cnt = 0;63std::atomic<bool> repeat = {false};6465COFFLinkerContext &ctx;66};6768// Returns true if section S is subject of ICF.69//70// Microsoft's documentation71// (https://msdn.microsoft.com/en-us/library/bxwfs976.aspx; visited April72// 2017) says that /opt:icf folds both functions and read-only data.73// Despite that, the MSVC linker folds only functions. We found74// a few instances of programs that are not safe for data merging.75// Therefore, we merge only functions just like the MSVC tool. However, we also76// merge read-only sections in a couple of cases where the address of the77// section is insignificant to the user program and the behaviour matches that78// of the Visual C++ linker.79bool ICF::isEligible(SectionChunk *c) {80// Non-comdat chunks, dead chunks, and writable chunks are not eligible.81bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;82if (!c->isCOMDAT() || !c->live || writable)83return false;8485// Under regular (not safe) ICF, all code sections are eligible.86if ((ctx.config.doICF == ICFLevel::All) &&87c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)88return true;8990// .pdata and .xdata unwind info sections are eligible.91StringRef outSecName = c->getSectionName().split('$').first;92if (outSecName == ".pdata" || outSecName == ".xdata")93return true;9495// So are vtables.96const char *itaniumVtablePrefix =97ctx.config.machine == I386 ? "__ZTV" : "_ZTV";98if (c->sym && (c->sym->getName().starts_with("??_7") ||99c->sym->getName().starts_with(itaniumVtablePrefix)))100return true;101102// Anything else not in an address-significance table is eligible.103return !c->keepUnique;104}105106// Split an equivalence class into smaller classes.107void ICF::segregate(size_t begin, size_t end, bool constant) {108while (begin < end) {109// Divide [Begin, End) into two. Let Mid be the start index of the110// second group.111auto bound = std::stable_partition(112chunks.begin() + begin + 1, chunks.begin() + end, [&](SectionChunk *s) {113if (constant)114return equalsConstant(chunks[begin], s);115return equalsVariable(chunks[begin], s);116});117size_t mid = bound - chunks.begin();118119// Split [Begin, End) into [Begin, Mid) and [Mid, End). We use Mid as an120// equivalence class ID because every group ends with a unique index.121for (size_t i = begin; i < mid; ++i)122chunks[i]->eqClass[(cnt + 1) % 2] = mid;123124// If we created a group, we need to iterate the main loop again.125if (mid != end)126repeat = true;127128begin = mid;129}130}131132// Returns true if two sections' associative children are equal.133bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) {134// Ignore associated metadata sections that don't participate in ICF, such as135// debug info and CFGuard metadata.136auto considerForICF = [](const SectionChunk &assoc) {137StringRef Name = assoc.getSectionName();138return !(Name.starts_with(".debug") || Name == ".gfids$y" ||139Name == ".giats$y" || Name == ".gljmp$y");140};141auto ra = make_filter_range(a->children(), considerForICF);142auto rb = make_filter_range(b->children(), considerForICF);143return std::equal(ra.begin(), ra.end(), rb.begin(), rb.end(),144[&](const SectionChunk &ia, const SectionChunk &ib) {145return ia.eqClass[cnt % 2] == ib.eqClass[cnt % 2];146});147}148149// Compare "non-moving" part of two sections, namely everything150// except relocation targets.151bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) {152if (a->relocsSize != b->relocsSize)153return false;154155// Compare relocations.156auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {157if (r1.Type != r2.Type ||158r1.VirtualAddress != r2.VirtualAddress) {159return false;160}161Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);162Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);163if (b1 == b2)164return true;165if (auto *d1 = dyn_cast<DefinedRegular>(b1))166if (auto *d2 = dyn_cast<DefinedRegular>(b2))167return d1->getValue() == d2->getValue() &&168d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];169return false;170};171if (!std::equal(a->getRelocs().begin(), a->getRelocs().end(),172b->getRelocs().begin(), eq))173return false;174175// Compare section attributes and contents.176return a->getOutputCharacteristics() == b->getOutputCharacteristics() &&177a->getSectionName() == b->getSectionName() &&178a->header->SizeOfRawData == b->header->SizeOfRawData &&179a->checksum == b->checksum && a->getContents() == b->getContents() &&180a->getMachine() == b->getMachine() && assocEquals(a, b);181}182183// Compare "moving" part of two sections, namely relocation targets.184bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) {185// Compare relocations.186auto eqSym = [&](Symbol *b1, Symbol *b2) {187if (b1 == b2)188return true;189if (auto *d1 = dyn_cast<DefinedRegular>(b1))190if (auto *d2 = dyn_cast<DefinedRegular>(b2))191return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];192return false;193};194auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {195Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);196Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);197return eqSym(b1, b2);198};199200Symbol *e1 = a->getEntryThunk();201Symbol *e2 = b->getEntryThunk();202if ((e1 || e2) && (!e1 || !e2 || !eqSym(e1, e2)))203return false;204205return std::equal(a->getRelocs().begin(), a->getRelocs().end(),206b->getRelocs().begin(), eq) &&207assocEquals(a, b);208}209210// Find the first Chunk after Begin that has a different class from Begin.211size_t ICF::findBoundary(size_t begin, size_t end) {212for (size_t i = begin + 1; i < end; ++i)213if (chunks[begin]->eqClass[cnt % 2] != chunks[i]->eqClass[cnt % 2])214return i;215return end;216}217218void ICF::forEachClassRange(size_t begin, size_t end,219std::function<void(size_t, size_t)> fn) {220while (begin < end) {221size_t mid = findBoundary(begin, end);222fn(begin, mid);223begin = mid;224}225}226227// Call Fn on each class group.228void ICF::forEachClass(std::function<void(size_t, size_t)> fn) {229// If the number of sections are too small to use threading,230// call Fn sequentially.231if (chunks.size() < 1024) {232forEachClassRange(0, chunks.size(), fn);233++cnt;234return;235}236237// Shard into non-overlapping intervals, and call Fn in parallel.238// The sharding must be completed before any calls to Fn are made239// so that Fn can modify the Chunks in its shard without causing data240// races.241const size_t numShards = 256;242size_t step = chunks.size() / numShards;243size_t boundaries[numShards + 1];244boundaries[0] = 0;245boundaries[numShards] = chunks.size();246parallelFor(1, numShards, [&](size_t i) {247boundaries[i] = findBoundary((i - 1) * step, chunks.size());248});249parallelFor(1, numShards + 1, [&](size_t i) {250if (boundaries[i - 1] < boundaries[i]) {251forEachClassRange(boundaries[i - 1], boundaries[i], fn);252}253});254++cnt;255}256257// Merge identical COMDAT sections.258// Two sections are considered the same if their section headers,259// contents and relocations are all the same.260void ICF::run() {261llvm::TimeTraceScope timeScope("ICF");262ScopedTimer t(ctx.icfTimer);263264// Collect only mergeable sections and group by hash value.265uint32_t nextId = 1;266for (Chunk *c : ctx.symtab.getChunks()) {267if (auto *sc = dyn_cast<SectionChunk>(c)) {268if (isEligible(sc))269chunks.push_back(sc);270else271sc->eqClass[0] = nextId++;272}273}274275// Make sure that ICF doesn't merge sections that are being handled by string276// tail merging.277for (MergeChunk *mc : ctx.mergeChunkInstances)278if (mc)279for (SectionChunk *sc : mc->sections)280sc->eqClass[0] = nextId++;281282// Initially, we use hash values to partition sections.283parallelForEach(chunks, [&](SectionChunk *sc) {284sc->eqClass[0] = xxh3_64bits(sc->getContents());285});286287// Combine the hashes of the sections referenced by each section into its288// hash.289for (unsigned cnt = 0; cnt != 2; ++cnt) {290parallelForEach(chunks, [&](SectionChunk *sc) {291uint32_t hash = sc->eqClass[cnt % 2];292for (Symbol *b : sc->symbols())293if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))294hash += sym->getChunk()->eqClass[cnt % 2];295// Set MSB to 1 to avoid collisions with non-hash classes.296sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31);297});298}299300// From now on, sections in Chunks are ordered so that sections in301// the same group are consecutive in the vector.302llvm::stable_sort(chunks, [](const SectionChunk *a, const SectionChunk *b) {303return a->eqClass[0] < b->eqClass[0];304});305306// Compare static contents and assign unique IDs for each static content.307forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); });308309// Split groups by comparing relocations until convergence is obtained.310do {311repeat = false;312forEachClass(313[&](size_t begin, size_t end) { segregate(begin, end, false); });314} while (repeat);315316log("ICF needed " + Twine(cnt) + " iterations");317318// Merge sections in the same classes.319forEachClass([&](size_t begin, size_t end) {320if (end - begin == 1)321return;322323log("Selected " + chunks[begin]->getDebugName());324for (size_t i = begin + 1; i < end; ++i) {325log(" Removed " + chunks[i]->getDebugName());326chunks[begin]->replace(chunks[i]);327}328});329}330331// Entry point to ICF.332void doICF(COFFLinkerContext &ctx) { ICF(ctx).run(); }333334} // namespace lld::coff335336337