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-29 12:28:31 +03:00
|
|
|
#include <tbb/concurrent_unordered_map.h>
|
2021-01-29 13:40:35 +03:00
|
|
|
#include <tbb/concurrent_vector.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-29 14:46:34 +03:00
|
|
|
namespace tbb {
|
|
|
|
template<> struct tbb_hash<Digest> {
|
|
|
|
size_t operator()(const Digest &k) const {
|
|
|
|
return *(i64 *)&k[0];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
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);
|
2021-01-29 13:49:33 +03:00
|
|
|
bool is_empty = (isec.shdr.sh_size == 0);
|
2021-01-28 09:47:34 +03:00
|
|
|
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 &&
|
2021-01-29 13:49:33 +03:00
|
|
|
!is_empty && !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-29 12:06:58 +03:00
|
|
|
static bool is_leaf(InputSection &isec) {
|
|
|
|
if (!isec.rels.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (FdeRecord &fde : isec.fdes)
|
|
|
|
if (fde.rels.size() > 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct LeafHasher {
|
2021-01-29 12:28:31 +03:00
|
|
|
size_t operator()(const InputSection *isec) const {
|
|
|
|
size_t h = std::hash<std::string_view>()(isec->get_contents());
|
|
|
|
for (FdeRecord &fde : isec->fdes) {
|
2021-01-29 12:06:58 +03:00
|
|
|
size_t h2 = std::hash<std::string_view>()(fde.contents.substr(8));
|
|
|
|
h ^= h2 + 0x9e3779b9 + (h << 6) + (h >> 2);
|
|
|
|
}
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-01-29 12:28:31 +03:00
|
|
|
struct LeafEq {
|
|
|
|
bool operator()(const InputSection *a, const InputSection *b) const {
|
|
|
|
if (a->get_contents() != b->get_contents())
|
2021-01-29 12:06:58 +03:00
|
|
|
return false;
|
2021-01-29 12:28:31 +03:00
|
|
|
if (a->fdes.size() != b->fdes.size())
|
2021-01-29 12:06:58 +03:00
|
|
|
return false;
|
2021-01-29 12:28:31 +03:00
|
|
|
for (i64 i = 0; i < a->fdes.size(); i++) {
|
|
|
|
if (a->fdes[i].contents.size() != b->fdes[i].contents.size())
|
2021-01-29 12:06:58 +03:00
|
|
|
return false;
|
2021-01-29 12:28:31 +03:00
|
|
|
if (a->fdes[i].contents.substr(8) != b->fdes[i].contents.substr(8))
|
2021-01-29 12:06:58 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-01-29 14:18:04 +03:00
|
|
|
static void merge_leaf_nodes() {
|
|
|
|
Timer t("leaf");
|
|
|
|
|
2021-01-29 14:55:54 +03:00
|
|
|
static Counter eligible("icf_eligibles");
|
|
|
|
static Counter non_eligible("icf_non_eligibles");
|
|
|
|
static Counter leaf("icf_leaf_nodes");
|
|
|
|
|
2021-01-29 14:18:04 +03:00
|
|
|
tbb::concurrent_unordered_map<InputSection *, InputSection *,
|
|
|
|
LeafHasher, LeafEq> map;
|
|
|
|
|
|
|
|
tbb::parallel_for((i64)0, (i64)out::objs.size(), [&](i64 i) {
|
|
|
|
for (InputSection *isec : out::objs[i]->sections) {
|
2021-01-29 14:55:54 +03:00
|
|
|
if (!isec)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!is_eligible(*isec)) {
|
|
|
|
non_eligible.inc();
|
2021-01-29 14:18:04 +03:00
|
|
|
continue;
|
2021-01-29 14:55:54 +03:00
|
|
|
}
|
2021-01-29 14:18:04 +03:00
|
|
|
|
|
|
|
if (is_leaf(*isec)) {
|
2021-01-29 14:55:54 +03:00
|
|
|
leaf.inc();
|
2021-01-29 14:18:04 +03:00
|
|
|
isec->icf_leaf = true;
|
|
|
|
auto [it, inserted] = map.insert({isec, isec});
|
|
|
|
if (!inserted && isec->get_priority() < it->second->get_priority())
|
|
|
|
it->second = isec;
|
|
|
|
} else {
|
2021-01-29 14:55:54 +03:00
|
|
|
eligible.inc();
|
2021-01-29 14:18:04 +03:00
|
|
|
isec->icf_eligible = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
tbb::parallel_for((i64)0, (i64)out::objs.size(), [&](i64 i) {
|
|
|
|
for (InputSection *isec : out::objs[i]->sections) {
|
2021-01-29 14:56:36 +03:00
|
|
|
if (isec && isec->icf_leaf) {
|
|
|
|
auto it = map.find(isec);
|
|
|
|
assert(it != map.end());
|
|
|
|
isec->leader = it->second;
|
|
|
|
}
|
2021-01-29 14:18:04 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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-29 12:42:11 +03:00
|
|
|
InputSection *isec = sym.input_section;
|
|
|
|
|
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-29 12:42:11 +03:00
|
|
|
} else if (!isec) {
|
2021-01-28 01:26:42 +03:00
|
|
|
hash_i64(3);
|
2021-01-29 12:42:11 +03:00
|
|
|
} else if (isec->leader) {
|
2021-01-28 01:26:42 +03:00
|
|
|
hash_i64(4);
|
2021-01-29 12:42:11 +03:00
|
|
|
hash_i64(isec->leader->get_priority());
|
|
|
|
} else if (isec->icf_eligible) {
|
2021-01-29 12:32:02 +03:00
|
|
|
hash_i64(5);
|
2021-01-29 06:45:37 +03:00
|
|
|
} else {
|
2021-01-29 12:32:02 +03:00
|
|
|
hash_i64(6);
|
2021-01-29 12:42:11 +03:00
|
|
|
hash_i64(isec->get_priority());
|
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-29 12:06:58 +03:00
|
|
|
// Bytes 0 to 4 contains the length of this record, and
|
|
|
|
// bytes 4 to 8 contain an offset to CIE.
|
2021-01-28 11:24:19 +03:00
|
|
|
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-29 06:45:37 +03:00
|
|
|
static std::vector<InputSection *> gather_sections() {
|
2021-01-29 06:57:00 +03:00
|
|
|
Timer t("gather_sections");
|
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-29 12:37:49 +03:00
|
|
|
if (isec && isec->icf_eligible)
|
2021-01-27 14:18:03 +03:00
|
|
|
num_sections[i]++;
|
2021-01-27 13:07:55 +03:00
|
|
|
});
|
|
|
|
|
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-29 06:45:37 +03:00
|
|
|
std::vector<InputSection *> sections(section_indices.back() + num_sections.back());
|
2021-01-27 13:07:55 +03:00
|
|
|
|
2021-01-29 06:45:37 +03:00
|
|
|
// Fill `sections` 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-29 12:37:49 +03:00
|
|
|
for (InputSection *isec : out::objs[i]->sections)
|
|
|
|
if (isec && isec->icf_eligible)
|
2021-01-29 06:45:37 +03:00
|
|
|
sections[idx++] = isec;
|
|
|
|
});
|
2021-01-28 13:19:39 +03:00
|
|
|
|
2021-01-29 06:45:37 +03:00
|
|
|
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
|
|
|
sections[i]->icf_idx = i;
|
2021-01-27 14:49:29 +03:00
|
|
|
});
|
|
|
|
|
2021-01-29 06:45:37 +03:00
|
|
|
return sections;
|
|
|
|
}
|
2021-01-27 14:30:22 +03:00
|
|
|
|
2021-01-29 06:45:37 +03:00
|
|
|
static std::vector<Digest> compute_digests(std::span<InputSection *> sections) {
|
|
|
|
Timer t("compute_digests");
|
2021-01-27 14:30:22 +03:00
|
|
|
|
2021-01-29 06:50:16 +03:00
|
|
|
std::vector<Digest> digests(sections.size());
|
|
|
|
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
2021-01-29 06:45:37 +03:00
|
|
|
digests[i] = compute_digest(*sections[i]);
|
2021-01-27 14:45:32 +03:00
|
|
|
});
|
2021-01-29 06:45:37 +03:00
|
|
|
return digests;
|
2021-01-27 13:07:55 +03:00
|
|
|
}
|
|
|
|
|
2021-01-29 06:53:03 +03:00
|
|
|
static void gather_edges(std::span<InputSection *> sections,
|
|
|
|
std::vector<u32> &edges, std::vector<u32> &edge_indices) {
|
|
|
|
Timer t("gather_edges");
|
2021-01-29 06:56:46 +03:00
|
|
|
|
|
|
|
std::vector<i64> num_edges(sections.size());
|
2021-01-29 06:57:55 +03:00
|
|
|
edge_indices.resize(sections.size());
|
2021-01-29 06:56:46 +03:00
|
|
|
|
|
|
|
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
|
|
|
InputSection &isec = *sections[i];
|
|
|
|
assert(isec.icf_eligible);
|
|
|
|
|
|
|
|
for (i64 j = 0; j < isec.rels.size(); j++) {
|
|
|
|
if (!isec.has_fragments[j]) {
|
|
|
|
ElfRela &rel = isec.rels[j];
|
|
|
|
Symbol &sym = *isec.file->symbols[rel.r_sym];
|
|
|
|
if (!sym.frag && sym.input_section && sym.input_section->icf_eligible)
|
|
|
|
num_edges[i]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
for (i64 i = 0; i < num_edges.size() - 1; i++)
|
|
|
|
edge_indices[i + 1] = edge_indices[i] + num_edges[i];
|
|
|
|
|
|
|
|
edges.resize(edge_indices.back() + num_edges.back());
|
|
|
|
|
|
|
|
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++) {
|
|
|
|
if (!isec.has_fragments[j]) {
|
|
|
|
ElfRela &rel = isec.rels[j];
|
|
|
|
Symbol &sym = *isec.file->symbols[rel.r_sym];
|
|
|
|
if (!sym.frag && sym.input_section && sym.input_section->icf_eligible)
|
|
|
|
edges[idx++] = sym.input_section->icf_idx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2021-01-29 06:53:03 +03:00
|
|
|
}
|
|
|
|
|
2021-01-29 09:34:53 +03:00
|
|
|
static void propagate(std::vector<std::vector<Digest>> &digests,
|
2021-01-29 07:42:42 +03:00
|
|
|
std::span<u32> edges, std::span<u32> edge_indices,
|
2021-01-29 14:25:00 +03:00
|
|
|
i64 slot, tbb::affinity_partitioner &ap) {
|
2021-01-29 07:42:42 +03:00
|
|
|
tbb::parallel_for((i64)0, (i64)digests[0].size(), [&](i64 i) {
|
|
|
|
SHA256_CTX ctx;
|
|
|
|
SHA256_Init(&ctx);
|
|
|
|
SHA256_Update(&ctx, digests[slot][i].data(), HASH_SIZE);
|
|
|
|
|
2021-01-29 14:25:00 +03:00
|
|
|
i64 begin = edge_indices[i];
|
|
|
|
i64 end = (i + 1 == digests[0].size()) ? edges.size() : edge_indices[i + 1];
|
|
|
|
|
2021-01-29 07:42:42 +03:00
|
|
|
for (i64 j = begin; j < end; j++)
|
|
|
|
SHA256_Update(&ctx, digests[slot][edges[j]].data(), HASH_SIZE);
|
|
|
|
|
|
|
|
digests[slot ^ 1][i] = digest_final(ctx);
|
2021-01-29 14:25:00 +03:00
|
|
|
}, ap);
|
2021-01-29 07:42:42 +03:00
|
|
|
}
|
|
|
|
|
2021-01-29 08:17:09 +03:00
|
|
|
static i64 count_num_classes(std::span<Digest> digests) {
|
2021-01-29 08:13:44 +03:00
|
|
|
std::vector<Digest> vec(digests.begin(), digests.end());
|
|
|
|
tbb::parallel_sort(vec);
|
|
|
|
|
|
|
|
tbb::enumerable_thread_specific<i64> num_classes;
|
|
|
|
tbb::parallel_for((i64)0, (i64)vec.size() - 1, [&](i64 i) {
|
|
|
|
if (vec[i] != vec[i + 1])
|
|
|
|
num_classes.local()++;
|
|
|
|
});
|
|
|
|
return num_classes.combine(std::plus());
|
|
|
|
}
|
|
|
|
|
2021-01-29 13:40:35 +03:00
|
|
|
static void print_icf_sections() {
|
|
|
|
tbb::concurrent_vector<InputSection *> leaders;
|
|
|
|
tbb::concurrent_unordered_multimap<InputSection *, InputSection *> map;
|
|
|
|
|
|
|
|
tbb::parallel_for_each(out::objs, [&](ObjectFile *file) {
|
2021-01-29 13:43:26 +03:00
|
|
|
for (InputSection *isec : file->sections) {
|
2021-01-29 13:40:35 +03:00
|
|
|
if (isec && isec->leader) {
|
|
|
|
if (isec == isec->leader) {
|
|
|
|
leaders.push_back(isec);
|
|
|
|
} else {
|
|
|
|
map.insert({isec->leader, isec});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
tbb::parallel_sort(leaders.begin(), leaders.end(),
|
|
|
|
[&](InputSection *a, InputSection *b) {
|
|
|
|
return a->get_priority() < b->get_priority();
|
|
|
|
});
|
|
|
|
|
|
|
|
i64 saved_bytes = 0;
|
|
|
|
|
|
|
|
for (InputSection *leader : leaders) {
|
|
|
|
auto [begin, end] = map.equal_range(leader);
|
|
|
|
if (begin == end)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
SyncOut() << "selected section " << *leader;
|
|
|
|
|
|
|
|
i64 n = 0;
|
|
|
|
for (auto it = begin; it != end; it++) {
|
|
|
|
SyncOut() << " removing identical section " << *it->second;
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
saved_bytes += leader->get_contents().size() * n;
|
|
|
|
}
|
|
|
|
|
|
|
|
SyncOut() << "ICF saved " << saved_bytes << " bytes";
|
|
|
|
}
|
|
|
|
|
2021-01-27 13:07:55 +03:00
|
|
|
void icf_sections() {
|
|
|
|
Timer t("icf");
|
2021-01-29 14:18:04 +03:00
|
|
|
merge_leaf_nodes();
|
2021-01-29 12:06:58 +03:00
|
|
|
|
2021-01-28 06:39:22 +03:00
|
|
|
// Prepare for the propagation rounds.
|
2021-01-29 06:45:37 +03:00
|
|
|
std::vector<InputSection *> sections = gather_sections();
|
|
|
|
|
2021-01-29 07:42:42 +03:00
|
|
|
std::vector<std::vector<Digest>> digests(2);
|
|
|
|
digests[0] = compute_digests(sections);
|
|
|
|
digests[1].resize(digests[0].size());
|
|
|
|
|
2021-01-29 06:53:03 +03:00
|
|
|
std::vector<u32> edges;
|
2021-01-29 07:42:42 +03:00
|
|
|
std::vector<u32> edge_indices;
|
2021-01-29 06:53:03 +03:00
|
|
|
gather_edges(sections, edges, edge_indices);
|
|
|
|
|
2021-01-29 07:42:42 +03:00
|
|
|
i64 slot = 0;
|
|
|
|
|
|
|
|
// Execute the propagation rounds until convergence is obtained.
|
2021-01-29 08:39:42 +03:00
|
|
|
{
|
2021-01-29 14:25:00 +03:00
|
|
|
Timer t("propagate");
|
2021-01-29 15:09:06 +03:00
|
|
|
static Counter round("icf_round");
|
2021-01-29 14:25:00 +03:00
|
|
|
tbb::affinity_partitioner ap;
|
2021-01-29 15:09:06 +03:00
|
|
|
i64 num_classes = -1;
|
2021-01-29 14:25:00 +03:00
|
|
|
|
2021-01-29 15:09:06 +03:00
|
|
|
for (;;) {
|
|
|
|
for (i64 j = 0; j < 10; j++) {
|
|
|
|
propagate(digests, edges, edge_indices, slot, ap);
|
|
|
|
slot ^= 1;
|
2021-01-29 08:39:42 +03:00
|
|
|
}
|
2021-01-29 15:09:06 +03:00
|
|
|
round.inc(10);
|
2021-01-29 15:13:10 +03:00
|
|
|
|
2021-01-29 15:09:06 +03:00
|
|
|
i64 n = count_num_classes(digests[slot]);
|
|
|
|
if (n == num_classes)
|
|
|
|
break;
|
|
|
|
num_classes = n;
|
2021-01-29 08:17:09 +03:00
|
|
|
}
|
2021-01-29 07:42:42 +03:00
|
|
|
}
|
2021-01-28 06:39:22 +03:00
|
|
|
|
2021-01-29 15:13:10 +03:00
|
|
|
// Group sections by SHA digest.
|
2021-01-29 14:25:00 +03:00
|
|
|
Timer t_merge("merge");
|
2021-01-29 14:46:34 +03:00
|
|
|
|
|
|
|
tbb::concurrent_unordered_map<Digest, InputSection *> map;
|
2021-01-29 09:29:41 +03:00
|
|
|
std::span<Digest> digest = digests[slot];
|
2021-01-28 08:15:06 +03:00
|
|
|
|
2021-01-29 14:46:34 +03:00
|
|
|
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
|
|
|
InputSection *isec = sections[i];
|
|
|
|
auto [it, inserted] = map.insert({digest[i], isec});
|
|
|
|
if (!inserted && isec->get_priority() < it->second->get_priority())
|
|
|
|
it->second = isec;
|
2021-01-29 09:29:41 +03:00
|
|
|
});
|
2021-01-29 11:20:05 +03:00
|
|
|
|
2021-01-29 14:46:34 +03:00
|
|
|
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
|
|
|
auto it = map.find(digest[i]);
|
|
|
|
assert(it != map.end());
|
|
|
|
sections[i]->leader = it->second;
|
2021-01-28 06:39:22 +03:00
|
|
|
});
|
2021-01-28 08:35:56 +03:00
|
|
|
|
2021-01-29 13:40:35 +03:00
|
|
|
if (config.print_icf_sections)
|
|
|
|
print_icf_sections();
|
|
|
|
|
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) {
|
2021-01-29 12:47:05 +03:00
|
|
|
InputSection *isec = sym->input_section;
|
|
|
|
if (isec && isec->leader && isec->leader != isec) {
|
|
|
|
sym->input_section = isec->leader;
|
2021-01-29 13:01:23 +03:00
|
|
|
isec->kill();
|
2021-01-29 12:47:05 +03:00
|
|
|
}
|
2021-01-28 08:35:56 +03:00
|
|
|
}
|
|
|
|
});
|
2021-01-27 13:07:55 +03:00
|
|
|
}
|