Skip to main content

wasmi/engine/executor/instrs/
copy.rs

1use super::{Executor, InstructionPtr};
2use crate::{
3    core::UntypedVal,
4    engine::utils::unreachable_unchecked,
5    ir::{AnyConst32, Const32, FixedSlotSpan, Op, Slot, SlotSpan},
6};
7use core::slice;
8
9impl Executor<'_> {
10    /// Executes a generic `copy` [`Op`].
11    fn execute_copy_impl<T>(&mut self, result: Slot, value: T, f: fn(&mut Self, T) -> UntypedVal) {
12        let value = f(self, value);
13        self.set_stack_slot(result, value);
14        self.next_instr()
15    }
16
17    /// Executes an [`Op::Copy`].
18    pub fn execute_copy(&mut self, result: Slot, value: Slot) {
19        self.execute_copy_impl(result, value, |this, value| this.get_stack_slot(value))
20    }
21
22    /// Executes an [`Op::Copy2`].
23    pub fn execute_copy_2(&mut self, results: FixedSlotSpan<2>, values: [Slot; 2]) {
24        self.execute_copy_2_impl(results, values);
25        self.next_instr()
26    }
27
28    /// Internal implementation of [`Op::Copy2`] execution.
29    fn execute_copy_2_impl(&mut self, results: FixedSlotSpan<2>, values: [Slot; 2]) {
30        let result0 = results.span().head();
31        let result1 = result0.next();
32        // We need `tmp` in case `results[0] == values[1]` to avoid overwriting `values[1]` before reading it.
33        let tmp = self.get_stack_slot(values[1]);
34        self.set_stack_slot(result0, self.get_stack_slot(values[0]));
35        self.set_stack_slot(result1, tmp);
36    }
37
38    /// Executes an [`Op::CopyImm32`].
39    pub fn execute_copy_imm32(&mut self, result: Slot, value: AnyConst32) {
40        self.execute_copy_impl(result, value, |_, value| UntypedVal::from(u32::from(value)))
41    }
42
43    /// Executes an [`Op::CopyI64Imm32`].
44    pub fn execute_copy_i64imm32(&mut self, result: Slot, value: Const32<i64>) {
45        self.execute_copy_impl(result, value, |_, value| UntypedVal::from(i64::from(value)))
46    }
47
48    /// Executes an [`Op::CopyF64Imm32`].
49    pub fn execute_copy_f64imm32(&mut self, result: Slot, value: Const32<f64>) {
50        self.execute_copy_impl(result, value, |_, value| UntypedVal::from(f64::from(value)))
51    }
52
53    /// Executes an [`Op::CopySpan`].
54    ///
55    /// # Note
56    ///
57    /// - This instruction assumes that `results` and `values` do _not_ overlap
58    ///   and thus can copy all the elements without a costly temporary buffer.
59    /// - If `results` and `values` _do_ overlap [`Op::CopySpan`] is used.
60    pub fn execute_copy_span(&mut self, results: SlotSpan, values: SlotSpan, len: u16) {
61        self.execute_copy_span_impl(results, values, len);
62        self.next_instr();
63    }
64
65    /// Internal implementation of [`Op::CopySpan`] execution.
66    pub fn execute_copy_span_impl(&mut self, results: SlotSpan, values: SlotSpan, len: u16) {
67        let results = results.iter(len);
68        let values = values.iter(len);
69        for (result, value) in results.into_iter().zip(values.into_iter()) {
70            let value = self.get_stack_slot(value);
71            self.set_stack_slot(result, value);
72        }
73    }
74
75    /// Executes an [`Op::CopyMany`].
76    pub fn execute_copy_many(&mut self, results: SlotSpan, values: [Slot; 2]) {
77        self.ip.add(1);
78        self.ip = self.execute_copy_many_impl(self.ip, results, &values);
79        self.next_instr()
80    }
81
82    /// Internal implementation of [`Op::CopyMany`] execution.
83    pub fn execute_copy_many_impl(
84        &mut self,
85        ip: InstructionPtr,
86        results: SlotSpan,
87        values: &[Slot],
88    ) -> InstructionPtr {
89        let mut ip = ip;
90        let mut result = results.head();
91        let mut copy_values = |values: &[Slot]| {
92            for &value in values {
93                let value = self.get_stack_slot(value);
94                self.set_stack_slot(result, value);
95                result = result.next();
96            }
97        };
98        copy_values(values);
99        while let Op::SlotList { regs } = ip.get() {
100            copy_values(regs);
101            ip.add(1);
102        }
103        let values = match ip.get() {
104            Op::Slot { slot } => slice::from_ref(slot),
105            Op::Slot2 { slots } => slots,
106            Op::Slot3 { slots } => slots,
107            unexpected => {
108                // Safety: Wasmi translator guarantees that slot-list finalizer exists.
109                unsafe {
110                    unreachable_unchecked!("expected slot-list finalizer but found: {unexpected:?}")
111                }
112            }
113        };
114        copy_values(values);
115        ip
116    }
117}