#![deny(clippy::arithmetic_side_effects)]
use hashbrown::HashMap;
use rayon::prelude::*;
use crate::error::Result;
use crate::ids::SectionId;
use crate::passes::merge::{
MergeGroup, MergeInput, MergeKind, MergedSections, merge_split_sections,
};
use super::inputs::ElfInput;
use super::object::SectionKind;
use super::place::Placement;
use super::sections::{NONE, Sections};
#[derive(Debug)]
pub struct Merged<'s, 'a> {
pub groups: Vec<MergeGroup>,
pub group_output: Vec<u32>,
pub group_first: Vec<SectionId>,
pub input_of: Vec<u32>,
pub merged: MergedSections<'s, 'a>,
}
impl Merged<'_, '_> {
#[must_use]
pub fn group_of(&self, id: SectionId) -> Option<u32> {
let input = *self.input_of.get(id.index())?;
if input == NONE {
return None;
}
self.merged.section_group(input as usize)
}
#[must_use]
pub fn offset_in_group(&self, id: SectionId, offset: u64) -> Option<u64> {
let input = *self.input_of.get(id.index())?;
if input == NONE {
return None;
}
self.merged.output_offset(input as usize, offset)
}
}
const WIDE_MERGE_PIECES: usize = 4 << 20;
pub fn merge<'s, 'a, F: crate::elf::read::ElfFormat>(
files: &'s [ElfInput<'a, F>],
sections: &Sections,
placement: &Placement<'_>,
tail_merge: bool,
widen: Option<usize>,
) -> Result<Merged<'s, 'a>> {
let mut groups = Vec::new();
let mut group_output = Vec::new();
let mut group_first = Vec::new();
let mut keys: HashMap<(u32, u8, u64, u64), u32, foldhash::fast::FixedState> =
HashMap::with_hasher(foldhash::fast::FixedState::with_seed(0x6d65_7267));
let mut inputs: Vec<MergeInput<'s, 'a>> = Vec::new();
let mut input_of = vec![NONE; sections.len()];
let merge_ids: Vec<SectionId> = sections
.kind
.par_iter()
.zip(sections.live.par_iter())
.enumerate()
.with_min_len(1 << 16)
.filter(|&(_, (&kind, &live))| kind == SectionKind::Merge && live)
.map(|(index, _)| SectionId::new(index))
.collect();
for id in merge_ids {
let Some((file_index, index)) = sections.locate(id) else {
continue;
};
let Some(object) = files.get(file_index).and_then(|f| f.object.as_ref()) else {
continue;
};
let Some(section) = object.section(index) else {
continue;
};
let Some(output) = placement.output_of(id) else {
continue;
};
let Some(split) = object.splits.get(section.split as usize) else {
continue;
};
let (tag, unit) = match split.kind() {
MergeKind::Strings { char_size } => (0u8, u64::from(char_size)),
MergeKind::Fixed { entry_size } => (1u8, entry_size),
};
let key = (output, tag, unit, split.alignment());
let next = u32::try_from(groups.len()).unwrap_or(NONE);
let group = *keys.entry(key).or_insert_with(|| {
groups.push(MergeGroup {
kind: split.kind(),
alignment: split.alignment(),
tail_merge,
});
group_output.push(output);
group_first.push(id);
next
});
if let Some(slot) = input_of.get_mut(id.index()) {
*slot = u32::try_from(inputs.len()).unwrap_or(NONE);
}
inputs.push(MergeInput { group, split });
}
let pieces: usize = inputs.iter().map(|input| input.split.num_pieces()).sum();
let merged = match widen.filter(|_| pieces >= WIDE_MERGE_PIECES) {
Some(threads) => rayon::ThreadPoolBuilder::new()
.num_threads(threads)
.build()
.map_err(|e| crate::error::Error::Internal(format!("cannot create thread pool: {e}")))?
.install(|| merge_split_sections(&groups, &inputs, None))?,
None => merge_split_sections(&groups, &inputs, None)?,
};
Ok(Merged {
groups,
group_output,
group_first,
input_of,
merged,
})
}