wasm/execution/
const_interpreter_loop.rs

1use alloc::vec::Vec;
2
3use crate::{
4    addrs::ModuleAddr,
5    assert_validated::UnwrapValidatedExt,
6    config::Config,
7    core::{
8        indices::{FuncIdx, GlobalIdx},
9        reader::{
10            span::Span,
11            types::{FuncType, ResultType},
12            WasmReader,
13        },
14    },
15    unreachable_validated,
16    value::{self, Ref},
17    value_stack::Stack,
18    RefType, RuntimeError, Store, Value,
19};
20
21// TODO update this documentation
22/// Execute a validated constant expression. These type of expressions are used
23/// for initializing global variables, data and element segments.
24///
25/// # Arguments
26/// TODO
27///
28/// # Safety
29///
30/// 1. the constant expression in the reader must be valid
31/// 2. the module address must be valid in the given store
32///
33// TODO this signature might change to support hooks or match the spec better
34pub(crate) unsafe fn run_const<'wasm, T: Config>(
35    wasm: &mut WasmReader<'wasm>,
36    stack: &mut Stack,
37    module: ModuleAddr,
38    store: &Store<'wasm, T>,
39) -> Result<(), RuntimeError> {
40    use crate::core::reader::types::opcode::*;
41    loop {
42        let first_instr_byte = wasm.read_u8().unwrap_validated();
43
44        #[cfg(feature = "log")]
45        crate::core::utils::print_beautiful_instruction_name_1_byte(first_instr_byte, wasm.pc);
46
47        let instruction_fn = match first_instr_byte {
48            END => end::<T>,
49            GLOBAL_GET => global_get::<T>,
50            I32_CONST => i32_const::<T>,
51            F32_CONST => f32_const::<T>,
52            F64_CONST => f64_const::<T>,
53            I64_CONST => i64_const::<T>,
54            REF_NULL => ref_null::<T>,
55            REF_FUNC => ref_func::<T>,
56            FD_EXTENSIONS => fd_extensions::<T>,
57
58            0x00..=0x0A
59            | 0x0C..=0x22
60            | 0x24..=0x40
61            | 0x45..=0xBF
62            | 0xC0..=0xCF
63            | 0xD1
64            | 0xD3..=0xFC
65            | 0xFE..=0xFF => {
66                unreachable_validated!();
67            }
68        };
69
70        let args = Args {
71            wasm,
72            stack,
73            module,
74            store,
75        };
76
77        // SAFETY: All possible instruction handler functions use the same safety requirements, as
78        // they are defined through the same macro. These are the same requirements defined by the
79        // current function, which must be fulfilled.
80        let should_break = unsafe { instruction_fn(args) }?;
81        if should_break {
82            break;
83        }
84    }
85    Ok(())
86}
87
88/// # Safety
89///
90/// 1. the constant expression in bytecode in the given span must be valid
91/// 2. the module address must be valid in the given store
92pub(crate) unsafe fn run_const_span<T: Config>(
93    wasm: &[u8],
94    span: &Span,
95    module: ModuleAddr,
96    store: &Store<T>,
97) -> Result<Option<Value>, RuntimeError> {
98    let mut wasm = WasmReader::new(wasm);
99
100    wasm.move_start_to(*span).unwrap_validated();
101
102    let mut stack = Stack::new::<T>(
103        Vec::new(),
104        &FuncType {
105            params: ResultType {
106                valtypes: Vec::new(),
107            },
108            returns: ResultType {
109                valtypes: Vec::new(),
110            },
111        },
112        &[],
113    )?;
114
115    // SAFETY: The current caller makes the same safety guarantees.
116    unsafe { run_const(&mut wasm, &mut stack, module, store)? };
117
118    Ok(stack.peek_value())
119}
120
121struct Args<'reader, 'resumable, 'store, 'wasm, T: Config> {
122    wasm: &'reader mut WasmReader<'wasm>,
123    stack: &'resumable mut Stack,
124    module: ModuleAddr,
125    store: &'store Store<'wasm, T>,
126}
127
128macro_rules! define_instruction {
129    ($name:ident, $opcode:expr, $contents:expr) => {
130        /// # Safety
131        ///
132        /// 1. the constant expression in the reader must be valid
133        /// 2. the module address must be valid in the given store
134        // Disable inlining to inspect the emitted code of individual instruction handlers
135        // #[inline(never)]
136        unsafe fn $name<T: Config>(args: Args<T>) -> Result<bool, RuntimeError> {
137            $contents(args)
138        }
139    };
140}
141
142define_instruction!(end, opcode::END, |Args { .. }| {
143    trace!("Constant instruction: END");
144    Ok(true)
145});
146
147define_instruction!(
148    global_get,
149    opcode::GLOBAL_GET,
150    |Args {
151         wasm,
152         module,
153         store,
154         stack,
155     }| {
156        // SAFETY: Validation guarantees there to be a valid global
157        // index next.
158        let global_idx = unsafe { GlobalIdx::read_unchecked(wasm) };
159
160        // SAFETY: The caller ensures that the given module address is
161        // valid in the given store.
162        let module_instance = unsafe { store.modules.get(module) };
163
164        // SAFETY: Validation guarantees the global index to be valid in
165        // the current module.
166        let global_addr = *unsafe { module_instance.global_addrs.get(global_idx) };
167
168        // SAFETY: The global address just came from the same store.
169        // Therefore, it must be valid in this store.
170        let global = unsafe { store.inner.globals.get(global_addr) };
171
172        trace!(
173            "Constant instruction: global.get [{global_idx}] -> [{:?}]",
174            global
175        );
176        stack.push_value(global.value)?;
177        Ok(false)
178    }
179);
180
181define_instruction!(
182    i32_const,
183    opcode::I32_CONST,
184    |Args { wasm, stack, .. }| {
185        let constant = wasm.read_var_i32().unwrap_validated();
186        trace!("Constant instruction: i32.const [] -> [{constant}]");
187        stack.push_value(constant.into())?;
188        Ok(false)
189    }
190);
191
192define_instruction!(
193    f32_const,
194    opcode::F32_CONST,
195    |Args { wasm, stack, .. }| {
196        let constant = value::F32::from_bits(wasm.read_f32().unwrap_validated());
197        trace!("Constanting instruction: f32.const [] -> [{constant}]");
198        stack.push_value(constant.into())?;
199        Ok(false)
200    }
201);
202
203define_instruction!(
204    f64_const,
205    opcode::F64_CONST,
206    |Args { wasm, stack, .. }| {
207        let constant = value::F64::from_bits(wasm.read_f64().unwrap_validated());
208        trace!("Constanting instruction: f64.const [] -> [{constant}]");
209        stack.push_value(constant.into())?;
210        Ok(false)
211    }
212);
213
214define_instruction!(
215    i64_const,
216    opcode::I64_CONST,
217    |Args { wasm, stack, .. }| {
218        let constant = wasm.read_var_i64().unwrap_validated();
219        trace!("Constant instruction: i64.const [] -> [{constant}]");
220        stack.push_value(constant.into())?;
221        Ok(false)
222    }
223);
224
225define_instruction!(
226    ref_null,
227    opcode::REF_NULL,
228    |Args { wasm, stack, .. }| {
229        let reftype = RefType::read(wasm).unwrap_validated();
230
231        stack.push_value(Value::Ref(Ref::Null(reftype)))?;
232        trace!("Instruction: ref.null '{:?}' -> [{:?}]", reftype, reftype);
233        Ok(false)
234    }
235);
236
237define_instruction!(
238    ref_func,
239    opcode::REF_FUNC,
240    |Args {
241         wasm,
242         module,
243         store,
244         stack,
245     }| {
246        // SAFETY: Validation guarantees there to be a valid function
247        // index next.
248        let func_idx = unsafe { FuncIdx::read_unchecked(wasm) };
249        // SAFETY: Validation guarantees the function index to be valid
250        // in the current module.
251        let func_addr = unsafe { store.modules.get(module).func_addrs.get(func_idx) };
252        stack.push_value(Value::Ref(Ref::Func(*func_addr)))?;
253        Ok(false)
254    }
255);
256
257define_instruction!(
258    fd_extensions,
259    opcode::FD_EXTENSIONS,
260    |Args { wasm, stack, .. }| {
261        use crate::core::reader::types::opcode::fd_extensions::*;
262
263        match wasm.read_var_u32().unwrap_validated() {
264            V128_CONST => {
265                let mut data = [0; 16];
266                for byte_ref in &mut data {
267                    *byte_ref = wasm.read_u8().unwrap_validated();
268                }
269
270                stack.push_value(Value::V128(data))?;
271            }
272            0x00..=0x0B | 0x0D.. => unreachable_validated!(),
273        }
274
275        Ok(false)
276    }
277);