1
1
mirror of https://github.com/rui314/mold.git synced 2025-01-03 22:36:34 +03:00
mold/icf.cc

337 lines
9.4 KiB
C++
Raw Normal View History

2021-01-27 13:07:55 +03:00
#include "mold.h"
2021-01-27 13:46:25 +03:00
#include <array>
2021-01-27 13:21:29 +03:00
#include <openssl/sha.h>
2021-01-27 14:28:06 +03:00
#include <tbb/enumerable_thread_specific.h>
2021-01-27 13:07:55 +03:00
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
2021-01-27 14:18:03 +03:00
#include <tbb/parallel_sort.h>
2021-01-27 13:07:55 +03:00
static constexpr i64 HASH_SIZE = 16;
2021-01-28 06:39:22 +03:00
typedef std::array<u8, HASH_SIZE> Digest;
2021-01-27 13:07:55 +03:00
static bool is_eligible(InputSection &isec) {
2021-01-28 09:47:34 +03:00
bool is_alloc = (isec.shdr.sh_flags & SHF_ALLOC);
2021-01-28 13:19:39 +03:00
bool is_executable = (isec.shdr.sh_flags & SHF_EXECINSTR);
2021-01-28 09:47:34 +03:00
bool is_writable = (isec.shdr.sh_flags & SHF_WRITE);
bool is_bss = (isec.shdr.sh_type == SHT_NOBITS);
bool is_init = (isec.shdr.sh_type == SHT_INIT_ARRAY || isec.name == ".init");
bool is_fini = (isec.shdr.sh_type == SHT_FINI_ARRAY || isec.name == ".fini");
bool is_enumerable = is_c_identifier(isec.name);
2021-01-28 13:19:39 +03:00
return is_alloc && is_executable && !is_writable && !is_bss &&
!is_init && !is_fini && !is_enumerable;
2021-01-27 13:07:55 +03:00
}
2021-01-28 06:39:22 +03:00
static Digest digest_final(SHA256_CTX &ctx) {
2021-01-27 16:01:45 +03:00
u8 digest[SHA256_SIZE];
assert(SHA256_Final(digest, &ctx) == 1);
2021-01-28 06:39:22 +03:00
Digest arr;
2021-01-27 16:01:45 +03:00
memcpy(arr.data(), digest, HASH_SIZE);
return arr;
}
2021-01-28 06:39:22 +03:00
static Digest compute_digest(InputSection &isec) {
2021-01-27 13:21:29 +03:00
SHA256_CTX ctx;
SHA256_Init(&ctx);
2021-01-28 01:26:42 +03:00
auto hash_i64 = [&](i64 val) {
SHA256_Update(&ctx, &val, 8);
};
2021-01-28 07:39:21 +03:00
auto hash_string = [&](std::string_view str) {
hash_i64(str.size());
SHA256_Update(&ctx, str.data(), str.size());
};
2021-01-28 01:26:42 +03:00
auto hash_symbol = [&](Symbol &sym) {
2021-01-28 11:07:55 +03:00
if (SectionFragment *frag = sym.frag) {
2021-01-28 01:26:42 +03:00
hash_i64(2);
hash_string(frag->data);
2021-01-28 01:20:28 +03:00
} else if (!sym.input_section) {
2021-01-28 01:26:42 +03:00
hash_i64(3);
2021-01-28 01:20:28 +03:00
} else {
2021-01-28 01:26:42 +03:00
hash_i64(4);
2021-01-28 01:20:28 +03:00
}
2021-01-28 11:07:55 +03:00
hash_i64(sym.value);
2021-01-28 01:20:28 +03:00
};
2021-01-28 01:26:42 +03:00
hash_string(isec.get_contents());
hash_i64(isec.shdr.sh_flags);
hash_i64(isec.fdes.size());
hash_i64(isec.rels.size());
2021-01-27 13:21:29 +03:00
2021-01-28 01:20:28 +03:00
for (FdeRecord &fde : isec.fdes) {
2021-01-28 11:24:19 +03:00
// Bytes 4 to 8 contain an offset to CIE
hash_string(fde.contents.substr(0, 4));
hash_string(fde.contents.substr(8));
2021-01-28 01:26:42 +03:00
hash_i64(fde.rels.size());
2021-01-28 01:20:28 +03:00
2021-01-28 11:24:19 +03:00
for (EhReloc &rel : std::span(fde.rels).subspan(1)) {
2021-01-28 01:26:42 +03:00
hash_symbol(rel.sym);
hash_i64(rel.type);
hash_i64(rel.offset);
hash_i64(rel.addend);
2021-01-28 01:20:28 +03:00
}
}
2021-01-27 15:24:28 +03:00
i64 ref_idx = 0;
for (i64 i = 0; i < isec.rels.size(); i++) {
ElfRela &rel = isec.rels[i];
2021-01-28 01:26:42 +03:00
hash_i64(rel.r_offset);
hash_i64(rel.r_type);
hash_i64(rel.r_addend);
2021-01-27 13:21:29 +03:00
2021-01-27 15:24:28 +03:00
if (isec.has_fragments[i]) {
SectionFragmentRef &ref = isec.rel_fragments[ref_idx++];
2021-01-28 01:26:42 +03:00
hash_i64(1);
hash_i64(ref.addend);
hash_string(ref.frag->data);
2021-01-27 15:43:01 +03:00
} else {
2021-01-28 01:26:42 +03:00
hash_symbol(*isec.file->symbols[rel.r_sym]);
2021-01-27 13:21:29 +03:00
}
}
2021-01-27 16:01:45 +03:00
return digest_final(ctx);
2021-01-27 13:46:25 +03:00
}
2021-01-27 13:07:55 +03:00
2021-01-28 10:49:08 +03:00
static Digest pack_number(i64 val) {
2021-01-28 06:39:22 +03:00
Digest arr;
2021-01-28 10:49:08 +03:00
memset(arr.data(), 0, HASH_SIZE);
memcpy(arr.data(), &val, 8);
2021-01-27 14:28:06 +03:00
return arr;
}
2021-01-28 07:14:16 +03:00
static void gather_sections(std::vector<Digest> &digests,
std::vector<InputSection *> &sections,
2021-01-28 07:47:55 +03:00
std::vector<u32> &edge_indices,
std::vector<u32> &edges) {
2021-01-27 16:18:45 +03:00
Timer t("gather");
2021-01-28 07:38:28 +03:00
2021-01-28 08:15:06 +03:00
struct Entry {
InputSection *isec;
Digest digest;
bool is_eligible;
};
2021-01-28 07:38:28 +03:00
// Count the number of input sections for each input file.
2021-01-27 14:18:03 +03:00
std::vector<i64> num_sections(out::objs.size());
2021-01-27 13:07:55 +03:00
tbb::parallel_for((i64)0, (i64)out::objs.size(), [&](i64 i) {
2021-01-27 14:18:03 +03:00
for (InputSection *isec : out::objs[i]->sections)
2021-01-27 14:45:32 +03:00
if (isec)
2021-01-27 14:18:03 +03:00
num_sections[i]++;
2021-01-27 13:07:55 +03:00
});
2021-01-28 07:38:28 +03:00
// Assign each object file a unique index in `entries`.
2021-01-28 07:30:12 +03:00
std::vector<i64> section_indices(out::objs.size());
for (i64 i = 0; i < out::objs.size() - 1; i++)
2021-01-27 14:18:03 +03:00
section_indices[i + 1] = section_indices[i] + num_sections[i];
2021-01-27 13:46:25 +03:00
2021-01-28 07:30:12 +03:00
std::vector<Entry> entries(section_indices.back() + num_sections.back());
2021-01-27 14:28:06 +03:00
tbb::enumerable_thread_specific<i64> num_eligibles;
2021-01-27 13:07:55 +03:00
2021-01-28 07:38:28 +03:00
// Fill `entries` contents.
2021-01-27 13:07:55 +03:00
tbb::parallel_for((i64)0, (i64)out::objs.size(), [&](i64 i) {
2021-01-27 14:18:03 +03:00
i64 idx = section_indices[i];
2021-01-28 13:19:39 +03:00
for (i64 j = 0; j < out::objs[i]->sections.size(); j++) {
InputSection *isec = out::objs[i]->sections[j];
2021-01-28 07:20:09 +03:00
if (!isec)
continue;
Entry &ent = entries[idx++];
ent.isec = isec;
ent.is_eligible = is_eligible(*isec);
2021-01-28 13:19:39 +03:00
ent.digest =
ent.is_eligible ? compute_digest(*isec) : pack_number((i << 32) | j);
2021-01-28 07:20:09 +03:00
if (ent.is_eligible)
num_eligibles.local() += 1;
2021-01-27 13:07:55 +03:00
}
});
2021-01-27 14:06:42 +03:00
2021-01-28 07:38:28 +03:00
// Sort `entries` so that all eligible sections precede non-eligible sections.
// Eligible sections are sorted by SHA hash.
2021-01-27 14:18:03 +03:00
tbb::parallel_sort(entries.begin(), entries.end(),
[](const Entry &a, const Entry &b) {
if (!a.is_eligible || !b.is_eligible)
return a.is_eligible && !b.is_eligible;
return a.digest < b.digest;
});
2021-01-27 14:28:06 +03:00
2021-01-28 07:38:28 +03:00
// Copy contents from `entries` to `sections` and `digests`.
2021-01-28 07:13:14 +03:00
sections.resize(num_eligibles.combine(std::plus()));
2021-01-27 16:18:45 +03:00
digests.resize(entries.size());
2021-01-27 14:28:06 +03:00
2021-01-28 07:13:14 +03:00
tbb::parallel_for((i64)0, (i64)entries.size(), [&](i64 i) {
2021-01-27 16:18:45 +03:00
Entry &ent = entries[i];
ent.isec->icf_idx = i;
2021-01-28 07:23:22 +03:00
digests[i] = ent.digest;
2021-01-28 07:13:14 +03:00
if (i < sections.size())
sections[i] = ent.isec;
2021-01-27 14:49:29 +03:00
});
2021-01-28 07:38:28 +03:00
// Count the number of outgoing edges for each eligible section.
2021-01-28 07:13:14 +03:00
std::vector<i64> num_edges(sections.size());
2021-01-27 14:30:22 +03:00
tbb::parallel_for((i64)0, (i64)num_edges.size(), [&](i64 i) {
assert(entries[i].is_eligible);
InputSection &isec = *sections[i];
for (i64 j = 0; j < isec.rels.size(); j++) {
2021-01-28 07:28:45 +03:00
if (!isec.has_fragments[j]) {
ElfRela &rel = isec.rels[j];
Symbol &sym = *isec.file->symbols[rel.r_sym];
2021-01-28 11:07:55 +03:00
if (!sym.frag && sym.input_section)
2021-01-28 07:28:45 +03:00
num_edges[i]++;
}
2021-01-27 14:30:22 +03:00
}
});
2021-01-27 14:31:44 +03:00
2021-01-28 07:38:28 +03:00
// Assign each eligible section a unique index in `edges`.
2021-01-27 14:45:32 +03:00
edge_indices.resize(num_edges.size());
for (i64 i = 0; i < num_edges.size() - 1; i++)
2021-01-27 14:31:44 +03:00
edge_indices[i + 1] = edge_indices[i] + num_edges[i];
2021-01-27 14:45:32 +03:00
edges.resize(edge_indices.back() + num_edges.back());
2021-01-28 07:38:28 +03:00
// Fill `edges` contents.
2021-01-27 14:45:32 +03:00
tbb::parallel_for((i64)0, (i64)num_edges.size(), [&](i64 i) {
InputSection &isec = *sections[i];
i64 idx = edge_indices[i];
for (i64 j = 0; j < isec.rels.size(); j++) {
2021-01-28 07:28:45 +03:00
if (!isec.has_fragments[j]) {
ElfRela &rel = isec.rels[j];
Symbol &sym = *isec.file->symbols[rel.r_sym];
2021-01-28 11:07:55 +03:00
if (!sym.frag && sym.input_section) {
2021-01-28 07:28:45 +03:00
assert(sym.input_section->icf_idx != -1);
edges[idx++] = sym.input_section->icf_idx;
}
2021-01-27 14:49:29 +03:00
}
2021-01-27 14:45:32 +03:00
}
});
2021-01-27 13:07:55 +03:00
}
void icf_sections() {
Timer t("icf");
2021-01-28 06:39:22 +03:00
// Prepare for the propagation rounds.
std::vector<Digest> digests0;
2021-01-28 07:14:16 +03:00
std::vector<InputSection *> sections;
2021-01-27 15:00:48 +03:00
std::vector<u32> edge_indices;
2021-01-28 07:47:55 +03:00
std::vector<u32> edges;
2021-01-27 15:00:48 +03:00
2021-01-28 07:47:55 +03:00
gather_sections(digests0, sections, edge_indices, edges);
2021-01-27 15:00:48 +03:00
2021-01-28 06:39:22 +03:00
std::vector<std::vector<Digest>> digests(2);
2021-01-27 16:12:05 +03:00
digests[0] = std::move(digests0);
digests[1] = digests[0];
2021-01-27 15:00:48 +03:00
i64 slot = 0;
2021-01-27 16:01:45 +03:00
auto count_num_classes = [&]() {
2021-01-27 15:26:05 +03:00
tbb::enumerable_thread_specific<i64> num_classes;
2021-01-28 07:47:55 +03:00
tbb::parallel_for((i64)0, (i64)sections.size() - 1, [&](i64 i) {
2021-01-27 15:26:05 +03:00
if (digests[slot][i] != digests[slot][i + 1])
2021-01-28 07:48:44 +03:00
num_classes.local()++;
2021-01-27 15:26:05 +03:00
});
2021-01-27 16:01:45 +03:00
return num_classes.combine(std::plus());
};
i64 num_classes = count_num_classes();
2021-01-28 09:20:07 +03:00
// SyncOut() << "num_classes=" << num_classes;
2021-01-28 07:47:55 +03:00
Timer t2("propagate");
2021-01-28 01:13:56 +03:00
static Counter round("icf_round");
2021-01-27 15:50:02 +03:00
2021-01-28 06:39:22 +03:00
// Execute the propagation rounds until convergence is obtained.
2021-01-27 16:01:45 +03:00
for (;;) {
2021-01-28 01:13:56 +03:00
round.inc();
2021-01-28 07:47:55 +03:00
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
2021-01-27 15:00:48 +03:00
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, digests[slot][i].data(), HASH_SIZE);
i64 begin = edge_indices[i];
2021-01-28 07:47:55 +03:00
i64 end = (i + 1 == sections.size()) ? edges.size() : edge_indices[i + 1];
2021-01-27 15:00:48 +03:00
for (i64 j = begin; j < end; j++)
SHA256_Update(&ctx, digests[slot][edges[j]].data(), HASH_SIZE);
2021-01-27 16:01:45 +03:00
digests[slot ^ 1][i] = digest_final(ctx);
2021-01-27 15:00:48 +03:00
});
slot ^= 1;
2021-01-27 16:01:45 +03:00
i64 n = count_num_classes();
2021-01-28 09:20:07 +03:00
// SyncOut() << "num_classes=" << n;
2021-01-27 16:01:45 +03:00
if (n == num_classes)
break;
num_classes = n;
2021-01-27 15:00:48 +03:00
}
2021-01-28 08:04:23 +03:00
t2.stop();
2021-01-28 06:39:22 +03:00
2021-01-28 07:57:20 +03:00
// Group sections by SHA1 digest.
2021-01-28 08:04:23 +03:00
Timer t3("merge");
2021-01-28 08:15:06 +03:00
struct Entry {
InputSection *isec;
Digest digest;
};
std::vector<Entry> entries;
2021-01-28 07:57:20 +03:00
entries.resize(sections.size());
2021-01-28 06:39:22 +03:00
2021-01-28 07:57:20 +03:00
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
entries[i] = {sections[i], digests[slot][i]};
});
2021-01-28 06:39:22 +03:00
2021-01-28 08:04:23 +03:00
tbb::parallel_sort(entries.begin(), entries.end(), [](auto &a, auto &b) {
2021-01-28 08:15:06 +03:00
if (a.digest != b.digest)
return a.digest < b.digest;
return a.isec->get_priority() < b.isec->get_priority();
2021-01-28 08:04:23 +03:00
});
2021-01-28 06:39:22 +03:00
tbb::parallel_for((i64)0, (i64)entries.size() - 1, [&](i64 i) {
2021-01-28 13:19:39 +03:00
// if (i > 44159)
// return;
2021-01-28 08:15:06 +03:00
if (i == 0 || entries[i - 1].digest != entries[i].digest) {
InputSection *leader = entries[i].isec;
2021-01-28 08:16:44 +03:00
i64 j = i + 1;
2021-01-28 13:19:39 +03:00
while (j < entries.size() && entries[i].digest == entries[j].digest) {
// if (i == 44158)
// SyncOut() << "isec=" << *leader << "\nisec=" << *entries[j].isec << "\n";
2021-01-28 09:20:07 +03:00
entries[j++].isec->leader = leader;
2021-01-28 13:19:39 +03:00
}
2021-01-28 08:15:06 +03:00
}
2021-01-28 06:39:22 +03:00
});
2021-01-28 08:35:56 +03:00
// Re-assign input sections to symbols.
tbb::parallel_for_each(out::objs, [](ObjectFile *file) {
for (Symbol *sym : file->symbols) {
if (sym->input_section && sym->input_section->leader)
sym->input_section = sym->input_section->leader;
}
});
2021-01-28 11:03:56 +03:00
tbb::enumerable_thread_specific<i64> saved_bytes;
tbb::parallel_for_each(entries, [&](Entry &ent) {
2021-01-28 08:35:56 +03:00
InputSection &isec = *ent.isec;
2021-01-28 09:20:07 +03:00
if (isec.leader) {
2021-01-28 09:47:34 +03:00
// SyncOut() << "Merge " << *isec.leader << " with " << isec;
2021-01-28 09:20:07 +03:00
isec.file->kill(isec.get_section_idx());
2021-01-28 11:03:56 +03:00
saved_bytes.local() += isec.get_contents().size();
2021-01-28 09:20:07 +03:00
}
2021-01-28 08:35:56 +03:00
});
2021-01-28 11:03:56 +03:00
SyncOut() << "saved_bytes=" << saved_bytes.combine(std::plus());
2021-01-27 13:07:55 +03:00
}