2021-01-27 13:07:55 +03:00
|
|
|
#include "mold.h"
|
|
|
|
|
2021-01-27 13:46:25 +03:00
|
|
|
#include <array>
|
2021-01-27 15:36:36 +03:00
|
|
|
#include <mutex>
|
2021-01-27 13:46:25 +03:00
|
|
|
#include <openssl/rand.h>
|
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 15:00:48 +03:00
|
|
|
#include <tbb/partitioner.h>
|
2021-01-27 13:07:55 +03:00
|
|
|
|
|
|
|
static constexpr i64 HASH_SIZE = 16;
|
|
|
|
|
|
|
|
static bool is_eligible(InputSection &isec) {
|
|
|
|
return (isec.shdr.sh_flags & SHF_ALLOC) &&
|
2021-01-27 14:45:32 +03:00
|
|
|
(isec.shdr.sh_type != SHT_NOBITS) &&
|
2021-01-27 13:07:55 +03:00
|
|
|
!(isec.shdr.sh_flags & SHF_WRITE) &&
|
|
|
|
!(isec.shdr.sh_type == SHT_INIT_ARRAY || isec.name == ".init") &&
|
|
|
|
!(isec.shdr.sh_type == SHT_FINI_ARRAY || isec.name == ".fini");
|
|
|
|
}
|
|
|
|
|
2021-01-27 15:42:13 +03:00
|
|
|
static void update_string(SHA256_CTX &ctx, std::string_view str) {
|
2021-01-27 15:24:28 +03:00
|
|
|
u64 size = str.size();
|
2021-01-27 15:42:13 +03:00
|
|
|
SHA256_Update(&ctx, &size, 8);
|
|
|
|
SHA256_Update(&ctx, str.data(), str.size());
|
2021-01-27 15:24:28 +03:00
|
|
|
}
|
|
|
|
|
2021-01-27 15:42:13 +03:00
|
|
|
static void update_i64(SHA256_CTX &ctx, i64 val) {
|
|
|
|
SHA256_Update(&ctx, &val, 8);
|
2021-01-27 15:24:28 +03:00
|
|
|
}
|
|
|
|
|
2021-01-27 13:46:25 +03:00
|
|
|
static std::array<u8, HASH_SIZE> compute_digest(InputSection &isec) {
|
2021-01-27 13:21:29 +03:00
|
|
|
SHA256_CTX ctx;
|
|
|
|
SHA256_Init(&ctx);
|
|
|
|
|
2021-01-27 15:42:13 +03:00
|
|
|
update_string(ctx, isec.get_contents());
|
|
|
|
update_i64(ctx, isec.shdr.sh_flags);
|
|
|
|
update_i64(ctx, isec.rels.size());
|
2021-01-27 13:21:29 +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-27 15:42:13 +03:00
|
|
|
update_i64(ctx, rel.r_offset);
|
|
|
|
update_i64(ctx, rel.r_type);
|
|
|
|
update_i64(ctx, 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-27 15:42:13 +03:00
|
|
|
update_i64(ctx, 1);
|
|
|
|
update_i64(ctx, ref.addend);
|
|
|
|
update_string(ctx, ref.frag->data);
|
2021-01-27 15:24:28 +03:00
|
|
|
continue;
|
|
|
|
}
|
2021-01-27 14:06:42 +03:00
|
|
|
|
2021-01-27 13:21:29 +03:00
|
|
|
Symbol &sym = *isec.file->symbols[rel.r_sym];
|
|
|
|
|
|
|
|
if (SectionFragment *frag = sym.fragref.frag) {
|
2021-01-27 15:42:13 +03:00
|
|
|
update_i64(ctx, 2);
|
|
|
|
update_i64(ctx, sym.fragref.addend);
|
|
|
|
update_string(ctx, frag->data);
|
2021-01-27 13:21:29 +03:00
|
|
|
} else if (!sym.input_section) {
|
2021-01-27 15:42:13 +03:00
|
|
|
update_i64(ctx, 3);
|
|
|
|
update_i64(ctx, sym.value);
|
2021-01-27 13:21:29 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-27 15:28:25 +03:00
|
|
|
u8 digest[SHA256_SIZE];
|
2021-01-27 13:46:25 +03:00
|
|
|
assert(SHA256_Final(digest, &ctx) == 1);
|
2021-01-27 13:07:55 +03:00
|
|
|
|
2021-01-27 13:46:25 +03:00
|
|
|
std::array<u8, HASH_SIZE> arr;
|
|
|
|
memcpy(arr.data(), digest, HASH_SIZE);
|
|
|
|
return arr;
|
|
|
|
}
|
2021-01-27 13:07:55 +03:00
|
|
|
|
2021-01-27 14:28:06 +03:00
|
|
|
static std::array<u8, HASH_SIZE> get_random_bytes() {
|
|
|
|
std::array<u8, HASH_SIZE> arr;
|
|
|
|
assert(RAND_bytes(arr.data(), HASH_SIZE) == 1);
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
2021-01-27 14:18:03 +03:00
|
|
|
struct Entry {
|
|
|
|
InputSection *isec;
|
|
|
|
bool is_eligible;
|
|
|
|
std::array<u8, HASH_SIZE> digest;
|
|
|
|
};
|
|
|
|
|
2021-01-27 13:46:25 +03:00
|
|
|
static void gather_sections(std::vector<InputSection *> §ions,
|
|
|
|
std::vector<std::array<u8, HASH_SIZE>> &digests,
|
2021-01-27 14:28:06 +03:00
|
|
|
std::vector<u32> &edges,
|
|
|
|
std::vector<u32> &edge_indices) {
|
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-27 14:18:03 +03:00
|
|
|
std::vector<i64> section_indices(out::objs.size() + 1);
|
|
|
|
for (i64 i = 0; i < out::objs.size(); i++)
|
|
|
|
section_indices[i + 1] = section_indices[i] + num_sections[i];
|
2021-01-27 13:46:25 +03:00
|
|
|
|
2021-01-27 14:18:03 +03:00
|
|
|
std::vector<Entry> entries(section_indices.back());
|
2021-01-27 14:28:06 +03:00
|
|
|
tbb::enumerable_thread_specific<i64> num_eligibles;
|
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-27 13:07:55 +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
|
|
|
Entry &ent = entries[idx++];
|
|
|
|
ent.isec = isec;
|
|
|
|
ent.is_eligible = is_eligible(*isec);
|
2021-01-27 14:28:06 +03:00
|
|
|
ent.digest = ent.is_eligible ? compute_digest(*isec) : get_random_bytes();
|
|
|
|
|
2021-01-27 14:18:03 +03:00
|
|
|
if (ent.is_eligible)
|
2021-01-27 14:28:06 +03:00
|
|
|
num_eligibles.local() += 1;
|
2021-01-27 13:07:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2021-01-27 14:06:42 +03:00
|
|
|
|
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
|
|
|
|
|
|
|
// Initialize sections and digests
|
|
|
|
sections.reserve(entries.size());
|
|
|
|
digests.reserve(entries.size());
|
|
|
|
|
|
|
|
for (Entry &ent : entries) {
|
|
|
|
sections.push_back(ent.isec);
|
|
|
|
digests.push_back(std::move(ent.digest));
|
|
|
|
}
|
|
|
|
|
2021-01-27 14:49:29 +03:00
|
|
|
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
|
|
|
sections[i]->icf_idx = i;
|
|
|
|
});
|
|
|
|
|
2021-01-27 14:28:06 +03:00
|
|
|
// Initialize edges and edge_indices
|
|
|
|
std::vector<i64> num_edges(num_eligibles.combine(std::plus()));
|
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++) {
|
|
|
|
if (isec.has_fragments[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ElfRela &rel = isec.rels[j];
|
|
|
|
Symbol &sym = *isec.file->symbols[rel.r_sym];
|
2021-01-27 14:49:29 +03:00
|
|
|
if (!sym.fragref.frag && sym.input_section)
|
2021-01-27 14:30:22 +03:00
|
|
|
num_edges[i]++;
|
|
|
|
}
|
|
|
|
});
|
2021-01-27 14:31:44 +03:00
|
|
|
|
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());
|
|
|
|
|
|
|
|
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[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ElfRela &rel = isec.rels[j];
|
|
|
|
Symbol &sym = *isec.file->symbols[rel.r_sym];
|
2021-01-27 15:37:30 +03:00
|
|
|
|
2021-01-27 14:49:29 +03:00
|
|
|
if (!sym.fragref.frag && sym.input_section) {
|
|
|
|
assert(sym.input_section->icf_idx != -1);
|
2021-01-27 14:45:32 +03:00
|
|
|
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-27 13:46:25 +03:00
|
|
|
std::vector<InputSection *> sections;
|
2021-01-27 15:00:48 +03:00
|
|
|
std::vector<std::array<u8, HASH_SIZE>> digests0;
|
2021-01-27 13:46:25 +03:00
|
|
|
std::vector<u32> edges;
|
2021-01-27 15:00:48 +03:00
|
|
|
std::vector<u32> edge_indices;
|
|
|
|
|
|
|
|
gather_sections(sections, digests0, edges, edge_indices);
|
|
|
|
|
|
|
|
std::vector<std::vector<std::array<u8, HASH_SIZE>>> digests(2);
|
2021-01-27 15:36:36 +03:00
|
|
|
digests[0] = digests0;
|
|
|
|
digests[1] = std::move(digests0);
|
2021-01-27 15:00:48 +03:00
|
|
|
|
|
|
|
i64 slot = 0;
|
|
|
|
|
|
|
|
Timer t2("rounds");
|
2021-01-27 13:46:25 +03:00
|
|
|
|
2021-01-27 15:36:36 +03:00
|
|
|
for (i64 i = 0; i < 30; i++) {
|
2021-01-27 15:26:05 +03:00
|
|
|
tbb::enumerable_thread_specific<i64> num_classes;
|
|
|
|
tbb::parallel_for((i64)0, (i64)edge_indices.size() - 1, [&](i64 i) {
|
|
|
|
if (digests[slot][i] != digests[slot][i + 1])
|
|
|
|
num_classes.local() += 1;
|
|
|
|
});
|
|
|
|
SyncOut() << "num_classes=" << num_classes.combine(std::plus());
|
|
|
|
|
2021-01-27 15:00:48 +03:00
|
|
|
tbb::parallel_for((i64)0, (i64)edge_indices.size(), [&](i64 i) {
|
|
|
|
SHA256_CTX ctx;
|
|
|
|
SHA256_Init(&ctx);
|
|
|
|
SHA256_Update(&ctx, digests[slot][i].data(), HASH_SIZE);
|
|
|
|
|
|
|
|
i64 begin = edge_indices[i];
|
|
|
|
i64 end = (i + 1 == edge_indices.size()) ? edges.size() : edge_indices[i + 1];
|
|
|
|
for (i64 j = begin; j < end; j++)
|
|
|
|
SHA256_Update(&ctx, digests[slot][edges[j]].data(), HASH_SIZE);
|
|
|
|
|
|
|
|
assert(SHA256_Final(digests[slot ^ 1][i].data(), &ctx) == 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
slot ^= 1;
|
|
|
|
}
|
2021-01-27 13:07:55 +03:00
|
|
|
}
|