use super::InstrumentationKind;
pub(crate) struct InstrumentationPoint {
offset: usize,
kind: InstrumentationKind,
cost: crate::Fee,
}
impl InstrumentationKind {
fn merge_same_point(self, other: Self) -> Option<Self> {
use InstrumentationKind as IK;
Some(match (self, other) {
(IK::Unreachable, _) => IK::Unreachable,
(_, IK::Unreachable) => IK::Unreachable,
(IK::TableInit | IK::TableGrow | IK::TableFill | IK::TableCopy, _) => unreachable!(),
(IK::MemoryInit | IK::MemoryGrow | IK::MemoryCopy | IK::MemoryFill, _) => {
unreachable!()
}
(_, IK::TableInit | IK::TableGrow | IK::TableFill | IK::TableCopy) => other,
(_, IK::MemoryInit | IK::MemoryGrow | IK::MemoryCopy | IK::MemoryFill) => other,
(
IK::Pure,
IK::Pure | IK::PreControlFlow | IK::PostControlFlow | IK::BetweenControlFlow,
) => other,
(IK::PreControlFlow, IK::PreControlFlow | IK::Pure) => IK::PreControlFlow,
(IK::PreControlFlow, IK::PostControlFlow | IK::BetweenControlFlow) => {
IK::BetweenControlFlow
}
(IK::PostControlFlow, IK::PostControlFlow | IK::Pure) => IK::PostControlFlow,
(IK::PostControlFlow, IK::PreControlFlow | IK::BetweenControlFlow) => {
IK::BetweenControlFlow
}
(
IK::BetweenControlFlow,
IK::Pure | IK::PreControlFlow | IK::PostControlFlow | IK::BetweenControlFlow,
) => IK::BetweenControlFlow,
})
}
fn merge_across_instructions(self, other: Self) -> Option<Self> {
use InstrumentationKind as IK;
match (self, other) {
(IK::Pure, IK::Pure) => Some(IK::Pure),
(IK::Unreachable, IK::Unreachable) => Some(IK::Unreachable),
(IK::PostControlFlow, IK::PreControlFlow) => Some(IK::BetweenControlFlow),
(IK::Pure, IK::PreControlFlow) => Some(IK::PreControlFlow),
(IK::PostControlFlow, IK::Pure) => Some(IK::PostControlFlow),
_ => None,
}
}
}
impl super::FunctionState {
pub(crate) fn instrumentation_count(&self) -> usize {
let Self {
kinds,
offsets,
costs,
..
} = self;
debug_assert!(kinds.len() == offsets.len() && kinds.len() == costs.len());
kinds.len()
}
pub(crate) fn instrumentation_at(&self, index: usize) -> Option<InstrumentationPoint> {
Some(InstrumentationPoint {
kind: *self.kinds.get(index)?,
offset: *self.offsets.get(index)?,
cost: *self.costs.get(index)?,
})
}
pub(crate) fn set_instrumentation_point(&mut self, index: usize, point: InstrumentationPoint) {
self.kinds[index] = point.kind;
self.costs[index] = point.cost;
self.offsets[index] = point.offset;
}
pub(crate) fn optimize_with(
&mut self,
merge_point: impl Fn(
&InstrumentationPoint,
&InstrumentationPoint,
) -> Option<InstrumentationPoint>,
) {
let mut output_idx = 0;
let mut prev_point = match self.instrumentation_at(0) {
Some(point) => point,
None => return,
};
for input_idx in 1..self.instrumentation_count() {
let current_point = self
.instrumentation_at(input_idx)
.expect("should always succeed");
if let Some(merged_point) = merge_point(&prev_point, ¤t_point) {
prev_point = merged_point;
} else {
self.set_instrumentation_point(output_idx, prev_point);
prev_point = current_point;
output_idx += 1;
}
}
self.set_instrumentation_point(output_idx, prev_point);
output_idx += 1;
self.kinds.truncate(output_idx);
self.costs.truncate(output_idx);
self.offsets.truncate(output_idx);
}
pub(crate) fn optimize(&mut self) {
self.optimize_with(|prev, next| {
if prev.offset == next.offset {
Some(InstrumentationPoint {
offset: prev.offset,
cost: prev.cost.checked_add(next.cost)?,
kind: prev.kind.merge_same_point(next.kind)?,
})
} else {
None
}
});
self.optimize_with(|prev, next| {
Some(InstrumentationPoint {
offset: std::cmp::min(prev.offset, next.offset),
kind: prev.kind.merge_across_instructions(next.kind)?,
cost: prev.cost.checked_add(next.cost)?,
})
});
}
}