Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
rui314 committed Jul 27, 2024
1 parent 2bbba15 commit 34ed39c
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 92 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ list(APPEND MOLD_ELF_TEMPLATE_FILES
elf/output-chunks.cc
elf/passes.cc
elf/relocatable.cc
elf/shrink-sections.cc
elf/thunks.cc
elf/tls.cc
)
Expand Down
84 changes: 2 additions & 82 deletions elf/arch-riscv.cc
Original file line number Diff line number Diff line change
Expand Up @@ -764,33 +764,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
}
}

template <>
void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
// If a section is not relaxed, we can copy it as a one big chunk.
if (extra.r_deltas.empty()) {
copy_contents(ctx, buf);
return;
}

// A relaxed section is copied piece-wise.
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 pos = 0;

for (i64 i = 0; i < rels.size(); i++) {
i64 delta = extra.r_deltas[i + 1] - extra.r_deltas[i];
if (delta == 0)
continue;
assert(delta > 0);

const ElfRel<E> &r = rels[i];
memcpy(buf, contents.data() + pos, r.r_offset - pos);
buf += r.r_offset - pos;
pos = r.r_offset + delta;
}

memcpy(buf, contents.data() + pos, contents.size() - pos);
}

template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
Expand Down Expand Up @@ -918,11 +891,6 @@ u64 get_eflags(Context<E> &ctx) {
return ret;
}

static bool is_resizable(InputSection<E> *isec) {
return isec && isec->is_alive && (isec->shdr().sh_flags & SHF_ALLOC) &&
(isec->shdr().sh_flags & SHF_EXECINSTR);
}

// Returns the distance between a relocated place and a symbol.
static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
InputSection<E> &isec, const ElfRel<E> &rel) {
Expand All @@ -945,7 +913,8 @@ static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
}

// Scan relocations to shrink sections.
static void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
template <>
void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
isec.extra.r_deltas.resize(rels.size() + 1);

Expand Down Expand Up @@ -1126,55 +1095,6 @@ static void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc)
isec.sh_size -= delta;
}

// Shrink sections by interpreting relocations.
//
// This operation seems to be optional, because by default longest
// instructions are being used. However, calling this function is actually
// mandatory because of R_RISCV_ALIGN. R_RISCV_ALIGN is a directive to the
// linker to align the location referred to by the relocation to a
// specified byte boundary. We at least have to interpret them to satisfy
// the alignment constraints.
template <>
i64 riscv_resize_sections<E>(Context<E> &ctx) {
Timer t(ctx, "riscv_resize_sections");

// True if we can use the 2-byte instructions. This is usually true on
// Unix because RV64GC is generally considered the baseline hardware.
bool use_rvc = get_eflags(ctx) & EF_RISCV_RVC;

// Find all the relocations that can be relaxed.
// This step should only shrink sections.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (is_resizable(isec.get()))
shrink_section(ctx, *isec, use_rvc);
});

// Fix symbol values.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file != file)
continue;

InputSection<E> *isec = sym->get_input_section();
if (!isec || isec->extra.r_deltas.empty())
continue;

std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
auto it = std::lower_bound(rels.begin(), rels.end(), sym->value,
[&](const ElfRel<E> &r, u64 val) {
return r.r_offset < val;
});

sym->value -= isec->extra.r_deltas[it - rels.begin()];
}
});

// Re-compute section offset again to finalize them.
compute_section_sizes(ctx);
return set_osec_offsets(ctx);
}

// ISA name handlers
//
// An example of ISA name is "rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0".
Expand Down
33 changes: 29 additions & 4 deletions elf/input-sections.cc
Original file line number Diff line number Diff line change
Expand Up @@ -453,11 +453,36 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
if (shdr().sh_type == SHT_NOBITS || sh_size == 0)
return;

// Copy data
if constexpr (is_riscv<E>)
copy_contents_riscv(ctx, buf);
else
// Copy data. In RISC-V and LoongArch object files, sections are not
// an atomic unit of copying because of relaxation. That is, some
// relocations are allowed to remove bytes from the middle of a
// section and shrink the overall size of it.
if constexpr (is_riscv<E>) {
if (extra.r_deltas.empty()) {
// If a section is not relaxed, we can copy it as a one big chunk.
copy_contents(ctx, buf);
} else {
// A relaxed section is copied piece-wise.
std::span<const ElfRel<E>> rels = get_rels(ctx);
u8 *buf2 = buf;
i64 pos = 0;

for (i64 i = 0; i < rels.size(); i++) {
i64 delta = extra.r_deltas[i + 1] - extra.r_deltas[i];
if (delta == 0)
continue;
assert(delta > 0);

const ElfRel<E> &r = rels[i];
memcpy(buf2, contents.data() + pos, r.r_offset - pos);
buf2 += r.r_offset - pos;
pos = r.r_offset + delta;
}
memcpy(buf2, contents.data() + pos, contents.size() - pos);
}
} else {
copy_contents(ctx, buf);
}

// Apply relocations
if (!ctx.arg.relocatable) {
Expand Down
2 changes: 1 addition & 1 deletion elf/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ int elf_main(int argc, char **argv) {
// be replaced with shorter instruction sequences if destinations
// are close enough. Do this optimization.
if constexpr (is_riscv<E>)
filesize = riscv_resize_sections(ctx);
filesize = shrink_sections(ctx);

// At this point, memory layout is fixed.

Expand Down
14 changes: 9 additions & 5 deletions elf/mold.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,6 @@ class __attribute__((aligned(4))) InputSection {
void apply_toc_rel(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel,
u8 *loc, u64 S, i64 A, u64 P, ElfRel<E> **dynrel);

void copy_contents_riscv(Context<E> &ctx, u8 *buf);

u64 get_thunk_addr(i64 idx);

std::optional<u64> get_tombstone(Symbol<E> &sym, SectionFragment<E> *frag);
Expand Down Expand Up @@ -1406,6 +1404,15 @@ std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx);
template <typename E>
void lto_cleanup(Context<E> &ctx);

//
// shrink-sections.cc
//

template <typename E> i64 shrink_sections(Context<E> &ctx);

template <typename E>
void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc);

//
// gc-sections.cc
//
Expand Down Expand Up @@ -1536,9 +1543,6 @@ class RiscvAttributesSection : public Chunk<E> {
template <is_riscv E>
u64 get_eflags(Context<E> &ctx);

template <is_riscv E>
i64 riscv_resize_sections(Context<E> &ctx);

//
// arch-ppc64v1.cc
//
Expand Down
63 changes: 63 additions & 0 deletions elf/shrink-sections.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Shrink sections by interpreting relocations.

#if MOLD_RV64LE || MOLD_RV64BE || MOLD_RV32LE || MOLD_RV32BE

#include "mold.h"

#include <tbb/parallel_for_each.h>

namespace mold::elf {

using E = MOLD_TARGET;

static bool is_resizable(InputSection<E> *isec) {
return isec && isec->is_alive && (isec->shdr().sh_flags & SHF_ALLOC) &&
(isec->shdr().sh_flags & SHF_EXECINSTR);
}

template <>
i64 shrink_sections<E>(Context<E> &ctx) {
Timer t(ctx, "shrink_sections");

// True if we can use the 2-byte instructions. This is usually true on
// Unix because RV64GC is generally considered the baseline hardware.
bool use_rvc = false;
if constexpr (is_riscv<E>)
use_rvc = get_eflags(ctx) & EF_RISCV_RVC;

// Find all the relocations that can be relaxed.
// This step should only shrink sections.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (is_resizable(isec.get()))
shrink_section(ctx, *isec, use_rvc);
});

// Fix symbol values.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file != file)
continue;

InputSection<E> *isec = sym->get_input_section();
if (!isec || isec->extra.r_deltas.empty())
continue;

std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
auto it = std::lower_bound(rels.begin(), rels.end(), sym->value,
[&](const ElfRel<E> &r, u64 val) {
return r.r_offset < val;
});

sym->value -= isec->extra.r_deltas[it - rels.begin()];
}
});

// Re-compute section offset again to finalize them.
compute_section_sizes(ctx);
return set_osec_offsets(ctx);
}

} // namespace mold::elf

#endif

0 comments on commit 34ed39c

Please sign in to comment.