use crate::{
engine::EngineFunc,
ir::{index, Op, Slot, SlotSpan, VisitResults},
module::ModuleHeader,
Engine,
Error,
FuncType,
};
pub trait RelinkResult {
fn relink_result(
&mut self,
module: &ModuleHeader,
new_result: Slot,
old_result: Slot,
) -> Result<bool, Error>;
}
struct Visitor {
new_result: Slot,
old_result: Slot,
replaced: Result<bool, Error>,
}
impl Visitor {
fn new(new_result: Slot, old_result: Slot) -> Self {
Self {
new_result,
old_result,
replaced: Ok(false),
}
}
}
impl VisitResults for Visitor {
#[inline]
fn visit_result_reg(&mut self, slot: &mut Slot) {
if self.replaced.is_err() {
return;
}
self.replaced = relink_simple(slot, self.new_result, self.old_result);
}
#[inline(always)]
fn visit_result_regs(&mut self, _slots: &mut SlotSpan, _len: Option<u16>) {}
}
impl RelinkResult for Op {
fn relink_result(
&mut self,
module: &ModuleHeader,
new_result: Slot,
old_result: Slot,
) -> Result<bool, Error> {
match self {
Self::CallInternal0 { results, func } | Self::CallInternal { results, func } => {
relink_call_internal(
results,
EngineFunc::from(*func),
module,
new_result,
old_result,
)
}
Self::CallImported0 { results, func } | Self::CallImported { results, func } => {
relink_call_imported(results, *func, module, new_result, old_result)
}
Self::CallIndirect0 { results, func_type }
| Self::CallIndirect0Imm16 { results, func_type }
| Self::CallIndirect { results, func_type }
| Self::CallIndirectImm16 { results, func_type } => {
relink_call_indirect(results, *func_type, module, new_result, old_result)
}
instr => {
let mut visitor = Visitor::new(new_result, old_result);
instr.visit_results(&mut visitor);
visitor.replaced
}
}
}
}
fn relink_simple(result: &mut Slot, new_result: Slot, old_result: Slot) -> Result<bool, Error> {
if *result != old_result {
return Ok(false);
}
debug_assert_ne!(*result, new_result);
*result = new_result;
Ok(true)
}
fn get_engine(module: &ModuleHeader) -> Engine {
module.engine().upgrade().unwrap_or_else(|| {
panic!(
"engine for result relinking does not exist: {:?}",
module.engine()
)
})
}
fn relink_call_internal(
results: &mut SlotSpan,
func: EngineFunc,
module: &ModuleHeader,
new_result: Slot,
old_result: Slot,
) -> Result<bool, Error> {
let Some(module_func) = module.get_func_index(func) else {
panic!("missing module func for compiled func: {func:?}")
};
let engine = get_engine(module);
let func_type = module.get_type_of_func(module_func);
let len_results = engine.resolve_func_type(func_type, FuncType::len_results);
if len_results != 1 {
return Ok(false);
}
relink_simple(results.head_mut(), new_result, old_result)
}
fn relink_call_imported(
results: &mut SlotSpan,
func: index::Func,
module: &ModuleHeader,
new_result: Slot,
old_result: Slot,
) -> Result<bool, Error> {
let engine = get_engine(module);
let func_idx = u32::from(func).into();
let func_type = module.get_type_of_func(func_idx);
let len_results = engine.resolve_func_type(func_type, |func_type| func_type.results().len());
if len_results != 1 {
return Ok(false);
}
relink_simple(results.head_mut(), new_result, old_result)
}
fn relink_call_indirect(
results: &mut SlotSpan,
func_type: index::FuncType,
module: &ModuleHeader,
new_result: Slot,
old_result: Slot,
) -> Result<bool, Error> {
let engine = get_engine(module);
let func_type_idx = u32::from(func_type).into();
let func_type = module.get_func_type(func_type_idx);
let len_results = engine.resolve_func_type(func_type, |func_type| func_type.results().len());
if len_results != 1 {
return Ok(false);
}
relink_simple(results.head_mut(), new_result, old_result)
}