wasmi/engine/executor/instrs/
copy.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
use super::{Executor, InstructionPtr};
use crate::{
    core::UntypedVal,
    engine::bytecode::{AnyConst32, Const32, FixedRegSpan, Instruction, Reg, RegSpan},
};
use core::slice;
use smallvec::SmallVec;

impl<'engine> Executor<'engine> {
    /// Executes a generic `copy` [`Instruction`].
    fn execute_copy_impl<T>(&mut self, result: Reg, value: T, f: fn(&mut Self, T) -> UntypedVal) {
        let value = f(self, value);
        self.set_register(result, value);
        self.next_instr()
    }

    /// Executes an [`Instruction::Copy`].
    pub fn execute_copy(&mut self, result: Reg, value: Reg) {
        self.execute_copy_impl(result, value, |this, value| this.get_register(value))
    }

    /// Executes an [`Instruction::Copy2`].
    pub fn execute_copy_2(&mut self, results: FixedRegSpan<2>, values: [Reg; 2]) {
        self.execute_copy_2_impl(results, values);
        self.next_instr()
    }

    /// Internal implementation of [`Instruction::Copy2`] execution.
    fn execute_copy_2_impl(&mut self, results: FixedRegSpan<2>, values: [Reg; 2]) {
        let result0 = results.span().head();
        let result1 = result0.next();
        // We need `tmp` in case `results[0] == values[1]` to avoid overwriting `values[1]` before reading it.
        let tmp = self.get_register(values[1]);
        self.set_register(result0, self.get_register(values[0]));
        self.set_register(result1, tmp);
    }

    /// Executes an [`Instruction::CopyImm32`].
    pub fn execute_copy_imm32(&mut self, result: Reg, value: AnyConst32) {
        self.execute_copy_impl(result, value, |_, value| UntypedVal::from(u32::from(value)))
    }

    /// Executes an [`Instruction::CopyI64Imm32`].
    pub fn execute_copy_i64imm32(&mut self, result: Reg, value: Const32<i64>) {
        self.execute_copy_impl(result, value, |_, value| UntypedVal::from(i64::from(value)))
    }

    /// Executes an [`Instruction::CopyF64Imm32`].
    pub fn execute_copy_f64imm32(&mut self, result: Reg, value: Const32<f64>) {
        self.execute_copy_impl(result, value, |_, value| UntypedVal::from(f64::from(value)))
    }

    /// Executes an [`Instruction::CopySpan`].
    ///
    /// # Note
    ///
    /// - This instruction assumes that `results` and `values` _do_ overlap
    ///   and thus requires a costly temporary buffer to avoid overwriting
    ///   intermediate copy results.
    /// - If `results` and `values` do _not_ overlap [`Instruction::CopySpanNonOverlapping`] is used.
    pub fn execute_copy_span(&mut self, results: RegSpan, values: RegSpan, len: u16) {
        self.execute_copy_span_impl(results, values, len);
        self.next_instr();
    }

    /// Internal implementation of [`Instruction::CopySpan`] execution.
    pub fn execute_copy_span_impl(&mut self, results: RegSpan, values: RegSpan, len: u16) {
        let results = results.iter(len);
        let values = values.iter(len);
        let mut tmp = <SmallVec<[UntypedVal; 8]>>::default();
        tmp.extend(values.into_iter().map(|value| self.get_register(value)));
        for (result, value) in results.into_iter().zip(tmp) {
            self.set_register(result, value);
        }
    }

    /// Executes an [`Instruction::CopySpanNonOverlapping`].
    ///
    /// # Note
    ///
    /// - This instruction assumes that `results` and `values` do _not_ overlap
    ///   and thus can copy all the elements without a costly temporary buffer.
    /// - If `results` and `values` _do_ overlap [`Instruction::CopySpan`] is used.
    pub fn execute_copy_span_non_overlapping(
        &mut self,
        results: RegSpan,
        values: RegSpan,
        len: u16,
    ) {
        self.execute_copy_span_non_overlapping_impl(results, values, len);
        self.next_instr();
    }

    /// Internal implementation of [`Instruction::CopySpanNonOverlapping`] execution.
    pub fn execute_copy_span_non_overlapping_impl(
        &mut self,
        results: RegSpan,
        values: RegSpan,
        len: u16,
    ) {
        let results = results.iter(len);
        let values = values.iter(len);
        for (result, value) in results.into_iter().zip(values.into_iter()) {
            let value = self.get_register(value);
            self.set_register(result, value);
        }
    }

    /// Executes an [`Instruction::CopyMany`].
    pub fn execute_copy_many(&mut self, results: RegSpan, values: [Reg; 2]) {
        self.ip.add(1);
        self.ip = self.execute_copy_many_impl(self.ip, results, &values);
        self.next_instr()
    }

    /// Internal implementation of [`Instruction::CopyMany`] execution.
    pub fn execute_copy_many_impl(
        &mut self,
        ip: InstructionPtr,
        results: RegSpan,
        values: &[Reg],
    ) -> InstructionPtr {
        // We need `tmp` since `values[n]` might be overwritten by previous copies.
        let mut tmp = <SmallVec<[UntypedVal; 8]>>::default();
        let mut ip = ip;
        tmp.extend(values.iter().map(|value| self.get_register(*value)));
        while let Instruction::RegisterList { regs } = ip.get() {
            tmp.extend(regs.iter().map(|value| self.get_register(*value)));
            ip.add(1);
        }
        let values = match ip.get() {
            Instruction::Register { reg } => slice::from_ref(reg),
            Instruction::Register2 { regs } => regs,
            Instruction::Register3 { regs } => regs,
            unexpected => unreachable!(
                "unexpected Instruction found while copying many values: {unexpected:?}"
            ),
        };
        tmp.extend(values.iter().map(|value| self.get_register(*value)));
        for (result, value) in results.iter_sized(tmp.len()).zip(tmp) {
            self.set_register(result, value);
        }
        ip
    }

    /// Executes an [`Instruction::CopyManyNonOverlapping`].
    pub fn execute_copy_many_non_overlapping(&mut self, results: RegSpan, values: [Reg; 2]) {
        self.ip.add(1);
        self.ip = self.execute_copy_many_non_overlapping_impl(self.ip, results, &values);
        self.next_instr()
    }

    /// Internal implementation of [`Instruction::CopyManyNonOverlapping`] execution.
    pub fn execute_copy_many_non_overlapping_impl(
        &mut self,
        ip: InstructionPtr,
        results: RegSpan,
        values: &[Reg],
    ) -> InstructionPtr {
        let mut ip = ip;
        let mut result = results.head();
        let mut copy_values = |values: &[Reg]| {
            for &value in values {
                let value = self.get_register(value);
                self.set_register(result, value);
                result = result.next();
            }
        };
        copy_values(values);
        while let Instruction::RegisterList { regs } = ip.get() {
            copy_values(regs);
            ip.add(1);
        }
        let values = match ip.get() {
            Instruction::Register { reg } => slice::from_ref(reg),
            Instruction::Register2 { regs } => regs,
            Instruction::Register3 { regs } => regs,
            unexpected => unreachable!("unexpected Instruction found while copying many non-overlapping values: {unexpected:?}"),
        };
        copy_values(values);
        ip
    }
}