wasm/execution/interpreter_loop/
memory.rs

1use crate::{
2    assert_validated::UnwrapValidatedExt,
3    core::{
4        indices::{DataIdx, Idx, MemIdx},
5        reader::types::{memarg::MemArg, opcode},
6        utils::ToUsizeExt,
7    },
8    execution::interpreter_loop::{
9        calculate_mem_address, data_drop, define_instruction_fn, from_lanes, memory_init, to_lanes,
10        Args, InterpreterLoopOutcome,
11    },
12    value::{F32, F64},
13    Value,
14};
15use core::{array, num::NonZeroU64, ops::ControlFlow};
16
17// t.load
18define_instruction_fn! {
19    i32_load,
20    fuel_check = flat(opcode::I32_LOAD),
21    |Args {
22         store_inner,
23         modules,
24         resumable,
25         wasm,
26         current_module,
27         ..
28     }| {
29        let memarg = MemArg::read(wasm).unwrap_validated();
30        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
31
32        // SAFETY: The current module address must come from the current
33        // store, because it is the only parameter to this function that
34        // can contain module addresses. All stores guarantee all
35        // addresses in them to be valid within themselves.
36        let module = unsafe { modules.get(*current_module) };
37
38        // SAFETY: Validation guarantees at least one memory to exist.
39        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
40        // SAFETY: This memory address was just read from the current
41        // store. Therefore, it is valid in the current store.
42        let mem_inst = unsafe { store_inner.memories.get(mem_addr) };
43
44        let idx = calculate_mem_address(&memarg, relative_address)?;
45        let data = mem_inst.mem.load(idx)?;
46
47        resumable.stack.push_value(Value::I32(data))?;
48        trace!("Instruction: i32.load [{relative_address}] -> [{data}]");
49        Ok(ControlFlow::Continue(()))
50    }
51}
52
53define_instruction_fn! {
54    i64_load,
55    fuel_check = flat(opcode::I64_LOAD),
56    |Args {
57         store_inner,
58         modules,
59         resumable,
60         wasm,
61         current_module,
62         ..
63     }| {
64        let memarg = MemArg::read(wasm).unwrap_validated();
65        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
66
67        // SAFETY: The current module address must come from the current
68        // store, because it is the only parameter to this function that
69        // can contain module addresses. All stores guarantee all
70        // addresses in them to be valid within themselves.
71        let module = unsafe { modules.get(*current_module) };
72
73        // SAFETY: Validation guarantees at least one memory to exist.
74        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
75        // SAFETY: This memory address was just read from the current
76        // store. Therefore, it is valid in the current store.
77        let mem = unsafe { store_inner.memories.get(mem_addr) };
78
79        let idx = calculate_mem_address(&memarg, relative_address)?;
80        let data = mem.mem.load(idx)?;
81
82        resumable.stack.push_value(Value::I64(data))?;
83        trace!("Instruction: i64.load [{relative_address}] -> [{data}]");
84        Ok(ControlFlow::Continue(()))
85    }
86}
87
88define_instruction_fn! {
89    f32_load,
90    fuel_check = flat(opcode::F32_LOAD),
91    |Args {
92         store_inner,
93         modules,
94         resumable,
95         wasm,
96         current_module,
97         ..
98     }| {
99        let memarg = MemArg::read(wasm).unwrap_validated();
100        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
101
102        // SAFETY: The current module address must come from the current
103        // store, because it is the only parameter to this function that
104        // can contain module addresses. All stores guarantee all
105        // addresses in them to be valid within themselves.
106        let module = unsafe { modules.get(*current_module) };
107
108        // SAFETY: Validation guarantees at least one memory to exist.
109        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
110        // SAFETY: This memory address was just read from the current
111        // store. Therefore, it is valid in the current store.
112        let mem = unsafe { store_inner.memories.get(mem_addr) };
113
114        let idx = calculate_mem_address(&memarg, relative_address)?;
115        let data = mem.mem.load(idx)?;
116
117        resumable.stack.push_value(Value::F32(data))?;
118        trace!("Instruction: f32.load [{relative_address}] -> [{data}]");
119        Ok(ControlFlow::Continue(()))
120    }
121}
122
123define_instruction_fn! {
124    f64_load,
125    fuel_check = flat(opcode::F64_LOAD),
126    |Args {
127         store_inner,
128         modules,
129         resumable,
130         wasm,
131         current_module,
132         ..
133     }| {
134        let memarg = MemArg::read(wasm).unwrap_validated();
135        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
136
137        // SAFETY: The current module address must come from the current
138        // store, because it is the only parameter to this function that
139        // can contain module addresses. All stores guarantee all
140        // addresses in them to be valid within themselves.
141        let module = unsafe { modules.get(*current_module) };
142
143        // SAFETY: Validation guarantees at least one memory to exist.
144        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
145        // SAFETY: This memory address was just read from the current
146        // store. Therefore, it is valid in the current store.
147        let mem = unsafe { store_inner.memories.get(mem_addr) };
148
149        let idx = calculate_mem_address(&memarg, relative_address)?;
150        let data = mem.mem.load(idx)?;
151
152        resumable.stack.push_value(Value::F64(data))?;
153        trace!("Instruction: f64.load [{relative_address}] -> [{data}]");
154        Ok(ControlFlow::Continue(()))
155    }
156}
157
158define_instruction_fn! {
159    v128_load,
160    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD),
161    |Args {
162         wasm,
163         resumable,
164         modules,
165         current_module,
166         store_inner,
167         ..
168     }| {
169        let memarg = MemArg::read(wasm).unwrap_validated();
170        // SAFETY: The current module address must come from the current
171        // store, because it is the only parameter to this function that
172        // can contain module addresses. All stores guarantee all
173        // addresses in them to be valid within themselves.
174        let module = unsafe { modules.get(*current_module) };
175
176        // SAFETY: Validation guarantees at least one memory to
177        // exist.
178        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
179        // SAFETY: This memory address was just read from the
180        // current store. Therefore, it is valid in the current
181        // store.
182        let memory = unsafe { store_inner.memories.get(mem_addr) };
183
184        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
185        let idx = calculate_mem_address(&memarg, relative_address)?;
186
187        let data: u128 = memory.mem.load(idx)?;
188        resumable.stack.push_value(data.to_le_bytes().into())?;
189        Ok(ControlFlow::Continue(()))
190    }
191}
192
193// t.loadN_sx
194define_instruction_fn! {
195    i32_load8_s,
196    fuel_check = flat(opcode::I32_LOAD8_S),
197    |Args {
198         store_inner,
199         modules,
200         resumable,
201         wasm,
202         current_module,
203         ..
204     }| {
205        let memarg = MemArg::read(wasm).unwrap_validated();
206        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
207
208        // SAFETY: The current module address must come from the current
209        // store, because it is the only parameter to this function that
210        // can contain module addresses. All stores guarantee all
211        // addresses in them to be valid within themselves.
212        let module = unsafe { modules.get(*current_module) };
213
214        // SAFETY: Validation guarantees at least one memory to exist.
215        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
216        // SAFETY: This memory address was just read from the current
217        // store. Therefore, it is valid in the current store.
218        let mem = unsafe { store_inner.memories.get(mem_addr) };
219
220        let idx = calculate_mem_address(&memarg, relative_address)?;
221        let data: i8 = mem.mem.load(idx)?;
222
223        resumable.stack.push_value(Value::I32(data as u32))?;
224        trace!("Instruction: i32.load8_s [{relative_address}] -> [{data}]");
225        Ok(ControlFlow::Continue(()))
226    }
227}
228
229define_instruction_fn! {
230    i32_load8_u,
231    fuel_check = flat(opcode::I32_LOAD8_U),
232    |Args {
233         store_inner,
234         modules,
235         resumable,
236         wasm,
237         current_module,
238         ..
239     }| {
240        let memarg = MemArg::read(wasm).unwrap_validated();
241        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
242
243        // SAFETY: The current module address must come from the current
244        // store, because it is the only parameter to this function that
245        // can contain module addresses. All stores guarantee all
246        // addresses in them to be valid within themselves.
247        let module = unsafe { modules.get(*current_module) };
248
249        // SAFETY: Validation guarantees at least one memory to exist.
250        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
251        // SAFETY: This memory address was just read from the current
252        // store. Therefore, it is valid in the current store.
253        let mem = unsafe { store_inner.memories.get(mem_addr) };
254
255        let idx = calculate_mem_address(&memarg, relative_address)?;
256        let data: u8 = mem.mem.load(idx)?;
257
258        resumable.stack.push_value(Value::I32(data as u32))?;
259        trace!("Instruction: i32.load8_u [{relative_address}] -> [{data}]");
260        Ok(ControlFlow::Continue(()))
261    }
262}
263
264define_instruction_fn! {
265    i32_load16_s,
266    fuel_check = flat(opcode::I32_LOAD16_S),
267    |Args {
268         store_inner,
269         modules,
270         resumable,
271         wasm,
272         current_module,
273         ..
274     }| {
275        let memarg = MemArg::read(wasm).unwrap_validated();
276        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
277
278        // SAFETY: The current module address must come from the current
279        // store, because it is the only parameter to this function that
280        // can contain module addresses. All stores guarantee all
281        // addresses in them to be valid within themselves.
282        let module = unsafe { modules.get(*current_module) };
283
284        // SAFETY: Validation guarantees at least one memory to exist.
285        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
286        // SAFETY: This memory address was just read from the current
287        // store. Therefore, it is valid in the current store.
288        let mem = unsafe { store_inner.memories.get(mem_addr) };
289
290        let idx = calculate_mem_address(&memarg, relative_address)?;
291        let data: i16 = mem.mem.load(idx)?;
292
293        resumable.stack.push_value(Value::I32(data as u32))?;
294        trace!("Instruction: i32.load16_s [{relative_address}] -> [{data}]");
295        Ok(ControlFlow::Continue(()))
296    }
297}
298
299define_instruction_fn! {
300    i32_load16_u,
301    fuel_check = flat(opcode::I32_LOAD16_U),
302    |Args {
303         store_inner,
304         modules,
305         resumable,
306         wasm,
307         current_module,
308         ..
309     }| {
310        let memarg = MemArg::read(wasm).unwrap_validated();
311        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
312
313        // SAFETY: The current module address must come from the current
314        // store, because it is the only parameter to this function that
315        // can contain module addresses. All stores guarantee all
316        // addresses in them to be valid within themselves.
317        let module = unsafe { modules.get(*current_module) };
318
319        // SAFETY: Validation guarantees at least one memory to exist.
320        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
321        // SAFETY: This memory address was just read from the current
322        // store. Therefore, it is valid in the current store.
323        let mem = unsafe { store_inner.memories.get(mem_addr) };
324
325        let idx = calculate_mem_address(&memarg, relative_address)?;
326        let data: u16 = mem.mem.load(idx)?;
327
328        resumable.stack.push_value(Value::I32(data as u32))?;
329        trace!("Instruction: i32.load16_u [{relative_address}] -> [{data}]");
330        Ok(ControlFlow::Continue(()))
331    }
332}
333
334define_instruction_fn! {
335    i64_load8_s,
336    fuel_check = flat(opcode::I64_LOAD8_S),
337    |Args {
338         store_inner,
339         modules,
340         resumable,
341         wasm,
342         current_module,
343         ..
344     }| {
345        let memarg = MemArg::read(wasm).unwrap_validated();
346        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
347
348        // SAFETY: The current module address must come from the current
349        // store, because it is the only parameter to this function that
350        // can contain module addresses. All stores guarantee all
351        // addresses in them to be valid within themselves.
352        let module = unsafe { modules.get(*current_module) };
353
354        // SAFETY: Validation guarantees at least one memory to exist.
355        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
356        // SAFETY: This memory address was just read from the current
357        // store. Therefore, it is valid in the current store.
358        let mem = unsafe { store_inner.memories.get(mem_addr) };
359
360        let idx = calculate_mem_address(&memarg, relative_address)?;
361        let data: i8 = mem.mem.load(idx)?;
362
363        resumable.stack.push_value(Value::I64(data as u64))?;
364        trace!("Instruction: i64.load8_s [{relative_address}] -> [{data}]");
365        Ok(ControlFlow::Continue(()))
366    }
367}
368
369define_instruction_fn! {
370    i64_load8_u,
371    fuel_check = flat(opcode::I64_LOAD8_U),
372    |Args {
373         store_inner,
374         modules,
375         resumable,
376         wasm,
377         current_module,
378         ..
379     }| {
380        let memarg = MemArg::read(wasm).unwrap_validated();
381        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
382
383        // SAFETY: The current module address must come from the current
384        // store, because it is the only parameter to this function that
385        // can contain module addresses. All stores guarantee all
386        // addresses in them to be valid within themselves.
387        let module = unsafe { modules.get(*current_module) };
388
389        // SAFETY: Validation guarantees at least one memory to exist.
390        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
391        // SAFETY: This memory address was just read from the current
392        // store. Therefore, it is valid in the current store.
393        let mem = unsafe { store_inner.memories.get(mem_addr) };
394
395        let idx = calculate_mem_address(&memarg, relative_address)?;
396        let data: u8 = mem.mem.load(idx)?;
397
398        resumable.stack.push_value(Value::I64(data as u64))?;
399        trace!("Instruction: i64.load8_u [{relative_address}] -> [{data}]");
400        Ok(ControlFlow::Continue(()))
401    }
402}
403
404define_instruction_fn! {
405    i64_load16_s,
406    fuel_check = flat(opcode::I64_LOAD16_S),
407    |Args {
408         store_inner,
409         modules,
410         resumable,
411         wasm,
412         current_module,
413         ..
414     }| {
415        let memarg = MemArg::read(wasm).unwrap_validated();
416        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
417
418        // SAFETY: The current module address must come from the current
419        // store, because it is the only parameter to this function that
420        // can contain module addresses. All stores guarantee all
421        // addresses in them to be valid within themselves.
422        let module = unsafe { modules.get(*current_module) };
423
424        // SAFETY: Validation guarantees at least one memory to exist.
425        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
426        // SAFETY: This memory address was just read from the current
427        // store. Therefore, it is valid in the current store.
428        let mem = unsafe { store_inner.memories.get(mem_addr) };
429
430        let idx = calculate_mem_address(&memarg, relative_address)?;
431        let data: i16 = mem.mem.load(idx)?;
432
433        resumable.stack.push_value(Value::I64(data as u64))?;
434        trace!("Instruction: i64.load16_s [{relative_address}] -> [{data}]");
435        Ok(ControlFlow::Continue(()))
436    }
437}
438
439define_instruction_fn! {
440    i64_load16_u,
441    fuel_check = flat(opcode::I64_LOAD16_U),
442    |Args {
443         store_inner,
444         modules,
445         resumable,
446         wasm,
447         current_module,
448         ..
449     }| {
450        let memarg = MemArg::read(wasm).unwrap_validated();
451        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
452
453        // SAFETY: The current module address must come from the current
454        // store, because it is the only parameter to this function that
455        // can contain module addresses. All stores guarantee all
456        // addresses in them to be valid within themselves.
457        let module = unsafe { modules.get(*current_module) };
458
459        // SAFETY: Validation guarantees at least one memory to exist.
460        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
461        // SAFETY: This memory address was just read from the current
462        // store. Therefore, it is valid in the current store.
463        let mem = unsafe { store_inner.memories.get(mem_addr) };
464
465        let idx = calculate_mem_address(&memarg, relative_address)?;
466        let data: u16 = mem.mem.load(idx)?;
467
468        resumable.stack.push_value(Value::I64(data as u64))?;
469        trace!("Instruction: i64.load16_u [{relative_address}] -> [{data}]");
470        Ok(ControlFlow::Continue(()))
471    }
472}
473
474define_instruction_fn! {
475    i64_load32_s,
476    fuel_check = flat(opcode::I64_LOAD32_S),
477    |Args {
478         store_inner,
479         modules,
480         resumable,
481         wasm,
482         current_module,
483         ..
484     }| {
485        let memarg = MemArg::read(wasm).unwrap_validated();
486        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
487
488        // SAFETY: The current module address must come from the current
489        // store, because it is the only parameter to this function that
490        // can contain module addresses. All stores guarantee all
491        // addresses in them to be valid within themselves.
492        let module = unsafe { modules.get(*current_module) };
493
494        // SAFETY: Validation guarantees at least one memory to exist.
495        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
496        // SAFETY: This memory address was just read from the current
497        // store. Therefore, it is valid in the current store.
498        let mem = unsafe { store_inner.memories.get(mem_addr) };
499
500        let idx = calculate_mem_address(&memarg, relative_address)?;
501        let data: i32 = mem.mem.load(idx)?;
502
503        resumable.stack.push_value(Value::I64(data as u64))?;
504        trace!("Instruction: i64.load32_s [{relative_address}] -> [{data}]");
505        Ok(ControlFlow::Continue(()))
506    }
507}
508
509define_instruction_fn! {
510    i64_load32_u,
511    fuel_check = flat(opcode::I64_LOAD32_U),
512    |Args {
513         store_inner,
514         modules,
515         resumable,
516         wasm,
517         current_module,
518         ..
519     }| {
520        let memarg = MemArg::read(wasm).unwrap_validated();
521        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
522
523        // SAFETY: The current module address must come from the current
524        // store, because it is the only parameter to this function that
525        // can contain module addresses. All stores guarantee all
526        // addresses in them to be valid within themselves.
527        let module = unsafe { modules.get(*current_module) };
528
529        // SAFETY: Validation guarantees at least one memory to exist.
530        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
531        // SAFETY: This memory address was just read from the current
532        // store. Therefore, it is valid in the current store.
533        let mem = unsafe { store_inner.memories.get(mem_addr) };
534
535        let idx = calculate_mem_address(&memarg, relative_address)?;
536        let data: u32 = mem.mem.load(idx)?;
537
538        resumable.stack.push_value(Value::I64(data as u64))?;
539        trace!("Instruction: i64.load32_u [{relative_address}] -> [{data}]");
540        Ok(ControlFlow::Continue(()))
541    }
542}
543
544// v128.loadNxM_sx
545define_instruction_fn! {
546    v128_load8x8_s,
547    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD8X8_S),
548    |Args {
549         wasm,
550         resumable,
551         modules,
552         current_module,
553         store_inner,
554         ..
555     }| {
556        let memarg = MemArg::read(wasm).unwrap_validated();
557        // SAFETY: The current module address must come from the current
558        // store, because it is the only parameter to this function that
559        // can contain module addresses. All stores guarantee all
560        // addresses in them to be valid within themselves.
561        let module = unsafe { modules.get(*current_module) };
562
563        // SAFETY: Validation guarantees at least one memory to
564        // exist.
565        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
566        // SAFETY: This memory address was just read from the
567        // current store. Therefore, it is valid in the current
568        // store.
569        let memory = unsafe { store_inner.memories.get(mem_addr) };
570
571        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
572        let idx = calculate_mem_address(&memarg, relative_address)?;
573
574        let half_data: [u8; 8] = memory.mem.load_bytes::<8>(idx)?; // v128 load always loads half of a v128
575
576        // Special case where we have only half of a v128. To convert it to lanes via `to_lanes`, pad the data with zeros
577        let data: [u8; 16] = array::from_fn(|i| *half_data.get(i).unwrap_or(&0));
578        let half_lanes: [i8; 8] = to_lanes::<1, 16, i8>(data)[..8].try_into().unwrap();
579
580        let extended_lanes = half_lanes.map(|lane| lane as i16);
581
582        resumable
583            .stack
584            .push_value(Value::V128(from_lanes(extended_lanes)))?;
585        Ok(ControlFlow::Continue(()))
586    }
587}
588define_instruction_fn! {
589    v128_load8x8_u,
590    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD8X8_U),
591    |Args {
592         wasm,
593         resumable,
594         modules,
595         current_module,
596         store_inner,
597         ..
598     }| {
599        let memarg = MemArg::read(wasm).unwrap_validated();
600        // SAFETY: The current module address must come from the current
601        // store, because it is the only parameter to this function that
602        // can contain module addresses. All stores guarantee all
603        // addresses in them to be valid within themselves.
604        let module = unsafe { modules.get(*current_module) };
605
606        // SAFETY: Validation guarantees at least one memory to
607        // exist.
608        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
609        // SAFETY: This memory address was just read from the
610        // current store. Therefore, it is valid in the current
611        // store.
612        let memory = unsafe { store_inner.memories.get(mem_addr) };
613
614        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
615        let idx = calculate_mem_address(&memarg, relative_address)?;
616
617        let half_data: [u8; 8] = memory.mem.load_bytes::<8>(idx)?; // v128 load always loads half of a v128
618
619        // Special case where we have only half of a v128. To convert it to lanes via `to_lanes`, pad the data with zeros
620        let data: [u8; 16] = array::from_fn(|i| *half_data.get(i).unwrap_or(&0));
621        let half_lanes: [u8; 8] = to_lanes::<1, 16, u8>(data)[..8].try_into().unwrap();
622
623        let extended_lanes = half_lanes.map(|lane| lane as u16);
624
625        resumable
626            .stack
627            .push_value(Value::V128(from_lanes(extended_lanes)))?;
628        Ok(ControlFlow::Continue(()))
629    }
630}
631define_instruction_fn! {
632    v128_load16x4_s,
633    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD16X4_S),
634    |Args {
635         wasm,
636         resumable,
637         modules,
638         current_module,
639         store_inner,
640         ..
641     }| {
642        let memarg = MemArg::read(wasm).unwrap_validated();
643        // SAFETY: The current module address must come from the current
644        // store, because it is the only parameter to this function that
645        // can contain module addresses. All stores guarantee all
646        // addresses in them to be valid within themselves.
647        let module = unsafe { modules.get(*current_module) };
648
649        // SAFETY: Validation guarantees at least one memory to
650        // exist.
651        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
652        // SAFETY: This memory address was just read from the
653        // current store. Therefore, it is valid in the current
654        // store.
655        let memory = unsafe { store_inner.memories.get(mem_addr) };
656
657        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
658        let idx = calculate_mem_address(&memarg, relative_address)?;
659
660        let half_data: [u8; 8] = memory.mem.load_bytes::<8>(idx)?; // v128 load always loads half of a v128
661
662        // Special case where we have only half of a v128. To convert it to lanes via `to_lanes`, pad the data with zeros
663        let data: [u8; 16] = array::from_fn(|i| *half_data.get(i).unwrap_or(&0));
664        let half_lanes: [i16; 4] = to_lanes::<2, 8, i16>(data)[..4].try_into().unwrap();
665
666        let extended_lanes = half_lanes.map(|lane| lane as i32);
667
668        resumable
669            .stack
670            .push_value(Value::V128(from_lanes(extended_lanes)))?;
671        Ok(ControlFlow::Continue(()))
672    }
673}
674define_instruction_fn! {
675    v128_load16x4_u,
676    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD16X4_U),
677    |Args {
678         wasm,
679         resumable,
680         modules,
681         current_module,
682         store_inner,
683         ..
684     }| {
685        let memarg = MemArg::read(wasm).unwrap_validated();
686        // SAFETY: The current module address must come from the current
687        // store, because it is the only parameter to this function that
688        // can contain module addresses. All stores guarantee all
689        // addresses in them to be valid within themselves.
690        let module = unsafe { modules.get(*current_module) };
691
692        // SAFETY: Validation guarantees at least one memory to
693        // exist.
694        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
695        // SAFETY: This memory address was just read from the
696        // current store. Therefore, it is valid in the current
697        // store.
698        let memory = unsafe { store_inner.memories.get(mem_addr) };
699
700        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
701        let idx = calculate_mem_address(&memarg, relative_address)?;
702
703        let half_data: [u8; 8] = memory.mem.load_bytes::<8>(idx)?; // v128 load always loads half of a v128
704
705        // Special case where we have only half of a v128. To convert it to lanes via `to_lanes`, pad the data with zeros
706        let data: [u8; 16] = array::from_fn(|i| *half_data.get(i).unwrap_or(&0));
707        let half_lanes: [u16; 4] = to_lanes::<2, 8, u16>(data)[..4].try_into().unwrap();
708
709        let extended_lanes = half_lanes.map(|lane| lane as u32);
710
711        resumable
712            .stack
713            .push_value(Value::V128(from_lanes(extended_lanes)))?;
714        Ok(ControlFlow::Continue(()))
715    }
716}
717define_instruction_fn! {
718    v128_load32x2_s,
719    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD32X2_S),
720    |Args {
721         wasm,
722         resumable,
723         modules,
724         current_module,
725         store_inner,
726         ..
727     }| {
728        let memarg = MemArg::read(wasm).unwrap_validated();
729        // SAFETY: The current module address must come from the current
730        // store, because it is the only parameter to this function that
731        // can contain module addresses. All stores guarantee all
732        // addresses in them to be valid within themselves.
733        let module = unsafe { modules.get(*current_module) };
734
735        // SAFETY: Validation guarantees at least one memory to
736        // exist.
737        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
738        // SAFETY: This memory address was just read from the
739        // current store. Therefore, it is valid in the current
740        // store.
741        let memory = unsafe { store_inner.memories.get(mem_addr) };
742
743        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
744        let idx = calculate_mem_address(&memarg, relative_address)?;
745
746        let half_data: [u8; 8] = memory.mem.load_bytes::<8>(idx)?; // v128 load always loads half of a v128
747
748        // Special case where we have only half of a v128. To convert it to lanes via `to_lanes`, pad the data with zeros
749        let data: [u8; 16] = array::from_fn(|i| *half_data.get(i).unwrap_or(&0));
750        let half_lanes: [i32; 2] = to_lanes::<4, 4, i32>(data)[..2].try_into().unwrap();
751
752        let extended_lanes = half_lanes.map(|lane| lane as i64);
753
754        resumable
755            .stack
756            .push_value(Value::V128(from_lanes(extended_lanes)))?;
757        Ok(ControlFlow::Continue(()))
758    }
759}
760define_instruction_fn! {
761    v128_load32x2_u,
762    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD32X2_U),
763    |Args {
764         wasm,
765         resumable,
766         modules,
767         current_module,
768         store_inner,
769         ..
770     }| {
771        let memarg = MemArg::read(wasm).unwrap_validated();
772        // SAFETY: The current module address must come from the current
773        // store, because it is the only parameter to this function that
774        // can contain module addresses. All stores guarantee all
775        // addresses in them to be valid within themselves.
776        let module = unsafe { modules.get(*current_module) };
777
778        // SAFETY: Validation guarantees at least one memory to
779        // exist.
780        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
781        // SAFETY: This memory address was just read from the
782        // current store. Therefore, it is valid in the current
783        // store.
784        let memory = unsafe { store_inner.memories.get(mem_addr) };
785
786        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
787        let idx = calculate_mem_address(&memarg, relative_address)?;
788
789        let half_data: [u8; 8] = memory.mem.load_bytes::<8>(idx)?; // v128 load always loads half of a v128
790
791        // Special case where we have only half of a v128. To convert it to lanes via `to_lanes`, pad the data with zeros
792        let data: [u8; 16] = array::from_fn(|i| *half_data.get(i).unwrap_or(&0));
793        let half_lanes: [u32; 2] = to_lanes::<4, 4, u32>(data)[..2].try_into().unwrap();
794
795        let extended_lanes = half_lanes.map(|lane| lane as u64);
796
797        resumable
798            .stack
799            .push_value(Value::V128(from_lanes(extended_lanes)))?;
800        Ok(ControlFlow::Continue(()))
801    }
802}
803
804// v128.loadN_splat
805define_instruction_fn! {
806    v128_load8_splat,
807    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD8_SPLAT),
808    |Args {
809         wasm,
810         resumable,
811         modules,
812         current_module,
813         store_inner,
814         ..
815     }| {
816        let memarg = MemArg::read(wasm).unwrap_validated();
817        // SAFETY: The current module address must come from the current
818        // store, because it is the only parameter to this function that
819        // can contain module addresses. All stores guarantee all
820        // addresses in them to be valid within themselves.
821        let module = unsafe { modules.get(*current_module) };
822
823        // SAFETY: Validation guarantees at least one memory to
824        // exist.
825        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
826        // SAFETY: This memory address was just read from the
827        // current store. Therefore, it is valid in the current
828        // store.
829        let memory = unsafe { store_inner.memories.get(mem_addr) };
830        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
831        let idx = calculate_mem_address(&memarg, relative_address)?;
832
833        let lane = memory.mem.load::<1, u8>(idx)?;
834        resumable
835            .stack
836            .push_value(Value::V128(from_lanes([lane; 16])))?;
837        Ok(ControlFlow::Continue(()))
838    }
839}
840define_instruction_fn! {
841    v128_load16_splat,
842    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD16_SPLAT),
843    |Args {
844         wasm,
845         resumable,
846         modules,
847         current_module,
848         store_inner,
849         ..
850     }| {
851        let memarg = MemArg::read(wasm).unwrap_validated();
852        // SAFETY: The current module address must come from the current
853        // store, because it is the only parameter to this function that
854        // can contain module addresses. All stores guarantee all
855        // addresses in them to be valid within themselves.
856        let module = unsafe { modules.get(*current_module) };
857
858        // SAFETY: Validation guarantees at least one memory to
859        // exist.
860        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
861        // SAFETY: This memory address was just read from the
862        // current store. Therefore, it is valid in the current
863        // store.
864        let memory = unsafe { store_inner.memories.get(mem_addr) };
865        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
866        let idx = calculate_mem_address(&memarg, relative_address)?;
867
868        let lane = memory.mem.load::<2, u16>(idx)?;
869        resumable
870            .stack
871            .push_value(Value::V128(from_lanes([lane; 8])))?;
872        Ok(ControlFlow::Continue(()))
873    }
874}
875define_instruction_fn! {
876    v128_load32_splat,
877    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD32_SPLAT),
878    |Args {
879         wasm,
880         resumable,
881         modules,
882         current_module,
883         store_inner,
884         ..
885     }| {
886        let memarg = MemArg::read(wasm).unwrap_validated();
887        // SAFETY: The current module address must come from the current
888        // store, because it is the only parameter to this function that
889        // can contain module addresses. All stores guarantee all
890        // addresses in them to be valid within themselves.
891        let module = unsafe { modules.get(*current_module) };
892
893        // SAFETY: Validation guarantees at least one memory to
894        // exist.
895        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
896        // SAFETY: This memory address was just read from the
897        // current store. Therefore, it is valid in the current
898        // store.
899        let memory = unsafe { store_inner.memories.get(mem_addr) };
900        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
901        let idx = calculate_mem_address(&memarg, relative_address)?;
902
903        let lane = memory.mem.load::<4, u32>(idx)?;
904        resumable
905            .stack
906            .push_value(Value::V128(from_lanes([lane; 4])))?;
907        Ok(ControlFlow::Continue(()))
908    }
909}
910define_instruction_fn! {
911    v128_load64_splat,
912    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD64_SPLAT),
913    |Args {
914         wasm,
915         resumable,
916         modules,
917         current_module,
918         store_inner,
919         ..
920     }| {
921        let memarg = MemArg::read(wasm).unwrap_validated();
922        // SAFETY: The current module address must come from the current
923        // store, because it is the only parameter to this function that
924        // can contain module addresses. All stores guarantee all
925        // addresses in them to be valid within themselves.
926        let module = unsafe { modules.get(*current_module) };
927
928        // SAFETY: Validation guarantees at least one memory to
929        // exist.
930        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
931        // SAFETY: This memory address was just read from the
932        // current store. Therefore, it is valid in the current
933        // store.
934        let memory = unsafe { store_inner.memories.get(mem_addr) };
935        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
936        let idx = calculate_mem_address(&memarg, relative_address)?;
937
938        let lane = memory.mem.load::<8, u64>(idx)?;
939        resumable
940            .stack
941            .push_value(Value::V128(from_lanes([lane; 2])))?;
942        Ok(ControlFlow::Continue(()))
943    }
944}
945
946// v128.loadN_zero
947define_instruction_fn! {
948    v128_load32_zero,
949    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD32_ZERO),
950    |Args {
951         wasm,
952         resumable,
953         modules,
954         current_module,
955         store_inner,
956         ..
957     }| {
958        let memarg = MemArg::read(wasm).unwrap_validated();
959
960        // SAFETY: The current module address must come from the current
961        // store, because it is the only parameter to this function that
962        // can contain module addresses. All stores guarantee all
963        // addresses in them to be valid within themselves.
964        let module = unsafe { modules.get(*current_module) };
965
966        // SAFETY: Validation guarantees at least one memory to
967        // exist.
968        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
969        // SAFETY: This memory address was just read from the
970        // current store. Therefore, it is valid in the current
971        // store.
972        let memory = unsafe { store_inner.memories.get(mem_addr) };
973
974        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
975        let idx = calculate_mem_address(&memarg, relative_address)?;
976
977        let data = memory.mem.load::<4, u32>(idx)? as u128;
978        resumable
979            .stack
980            .push_value(Value::V128(data.to_le_bytes()))?;
981        Ok(ControlFlow::Continue(()))
982    }
983}
984define_instruction_fn! {
985    v128_load64_zero,
986    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD64_ZERO),
987    |Args {
988         wasm,
989         resumable,
990         modules,
991         current_module,
992         store_inner,
993         ..
994     }| {
995        let memarg = MemArg::read(wasm).unwrap_validated();
996        // SAFETY: The current module address must come from the current
997        // store, because it is the only parameter to this function that
998        // can contain module addresses. All stores guarantee all
999        // addresses in them to be valid within themselves.
1000        let module = unsafe { modules.get(*current_module) };
1001
1002        // SAFETY: Validation guarantees at least one memory to
1003        // exist.
1004        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1005        // SAFETY: This memory address was just read from the
1006        // current store. Therefore, it is valid in the current
1007        // store.
1008        let memory = unsafe { store_inner.memories.get(mem_addr) };
1009
1010        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1011        let idx = calculate_mem_address(&memarg, relative_address)?;
1012
1013        let data = memory.mem.load::<8, u64>(idx)? as u128;
1014        resumable
1015            .stack
1016            .push_value(Value::V128(data.to_le_bytes()))?;
1017        Ok(ControlFlow::Continue(()))
1018    }
1019}
1020
1021// v128.loadN_lane
1022define_instruction_fn! {
1023    v128_load8_lane,
1024    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD8_LANE),
1025    |Args {
1026         wasm,
1027         resumable,
1028         modules,
1029         current_module,
1030         store_inner,
1031         ..
1032     }| {
1033        let data: [u8; 16] = resumable.stack.pop_value().try_into().unwrap_validated();
1034        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1035        let memarg = MemArg::read(wasm).unwrap_validated();
1036        // SAFETY: The current module address must come from the current
1037        // store, because it is the only parameter to this function that
1038        // can contain module addresses. All stores guarantee all
1039        // addresses in them to be valid within themselves.
1040        let module = unsafe { modules.get(*current_module) };
1041
1042        // SAFETY: Validation guarantees at least one memory to
1043        // exist.
1044        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1045        // SAFETY: This memory address was just read from the
1046        // current store. Therefore, it is valid in the current
1047        // store.
1048        let memory = unsafe { store_inner.memories.get(mem_addr) };
1049        let idx = calculate_mem_address(&memarg, relative_address)?;
1050        let lane_idx = usize::from(wasm.read_u8().unwrap_validated());
1051        let mut lanes: [u8; 16] = to_lanes(data);
1052        *lanes.get_mut(lane_idx).unwrap_validated() = memory.mem.load::<1, u8>(idx)?;
1053        resumable.stack.push_value(Value::V128(from_lanes(lanes)))?;
1054        Ok(ControlFlow::Continue(()))
1055    }
1056}
1057
1058define_instruction_fn! {
1059    v128_load16_lane,
1060    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD16_LANE),
1061    |Args {
1062         wasm,
1063         resumable,
1064         modules,
1065         current_module,
1066         store_inner,
1067         ..
1068     }| {
1069        let data: [u8; 16] = resumable.stack.pop_value().try_into().unwrap_validated();
1070        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1071        let memarg = MemArg::read(wasm).unwrap_validated();
1072        // SAFETY: The current module address must come from the current
1073        // store, because it is the only parameter to this function that
1074        // can contain module addresses. All stores guarantee all
1075        // addresses in them to be valid within themselves.
1076        let module = unsafe { modules.get(*current_module) };
1077
1078        // SAFETY: Validation guarantees at least one memory to
1079        // exist.
1080        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1081        // SAFETY: This memory address was just read from the
1082        // current store. Therefore, it is valid in the current
1083        // store.
1084        let memory = unsafe { store_inner.memories.get(mem_addr) };
1085        let idx = calculate_mem_address(&memarg, relative_address)?;
1086        let lane_idx = usize::from(wasm.read_u8().unwrap_validated());
1087        let mut lanes: [u16; 8] = to_lanes(data);
1088        *lanes.get_mut(lane_idx).unwrap_validated() = memory.mem.load::<2, u16>(idx)?;
1089        resumable.stack.push_value(Value::V128(from_lanes(lanes)))?;
1090        Ok(ControlFlow::Continue(()))
1091    }
1092}
1093define_instruction_fn! {
1094    v128_load32_lane,
1095    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD32_LANE),
1096    |Args {
1097         wasm,
1098         resumable,
1099         modules,
1100         current_module,
1101         store_inner,
1102         ..
1103     }| {
1104        let data: [u8; 16] = resumable.stack.pop_value().try_into().unwrap_validated();
1105        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1106        let memarg = MemArg::read(wasm).unwrap_validated();
1107        // SAFETY: The current module address must come from the current
1108        // store, because it is the only parameter to this function that
1109        // can contain module addresses. All stores guarantee all
1110        // addresses in them to be valid within themselves.
1111        let module = unsafe { modules.get(*current_module) };
1112
1113        // SAFETY: Validation guarantees at least one memory to
1114        // exist.
1115        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1116        // SAFETY: This memory address was just read from the
1117        // current store. Therefore, it is valid in the current
1118        // store.
1119        let memory = unsafe { store_inner.memories.get(mem_addr) };
1120        let idx = calculate_mem_address(&memarg, relative_address)?;
1121        let lane_idx = usize::from(wasm.read_u8().unwrap_validated());
1122        let mut lanes: [u32; 4] = to_lanes(data);
1123        *lanes.get_mut(lane_idx).unwrap_validated() = memory.mem.load::<4, u32>(idx)?;
1124        resumable.stack.push_value(Value::V128(from_lanes(lanes)))?;
1125        Ok(ControlFlow::Continue(()))
1126    }
1127}
1128define_instruction_fn! {
1129    v128_load64_lane,
1130    fuel_check = flat_fd(opcode::fd_extensions::V128_LOAD64_LANE),
1131    |Args {
1132         wasm,
1133         resumable,
1134         modules,
1135         current_module,
1136         store_inner,
1137         ..
1138     }| {
1139        let data: [u8; 16] = resumable.stack.pop_value().try_into().unwrap_validated();
1140        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1141        let memarg = MemArg::read(wasm).unwrap_validated();
1142        // SAFETY: The current module address must come from the current
1143        // store, because it is the only parameter to this function that
1144        // can contain module addresses. All stores guarantee all
1145        // addresses in them to be valid within themselves.
1146        let module = unsafe { modules.get(*current_module) };
1147
1148        // SAFETY: Validation guarantees at least one memory to
1149        // exist.
1150        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1151        // SAFETY: This memory address was just read from the
1152        // current store. Therefore, it is valid in the current
1153        // store.
1154        let memory = unsafe { store_inner.memories.get(mem_addr) };
1155        let idx = calculate_mem_address(&memarg, relative_address)?;
1156        let lane_idx = usize::from(wasm.read_u8().unwrap_validated());
1157        let mut lanes: [u64; 2] = to_lanes(data);
1158        *lanes.get_mut(lane_idx).unwrap_validated() = memory.mem.load::<8, u64>(idx)?;
1159        resumable.stack.push_value(Value::V128(from_lanes(lanes)))?;
1160        Ok(ControlFlow::Continue(()))
1161    }
1162}
1163
1164// t.store
1165define_instruction_fn! {
1166    i32_store,
1167    fuel_check = flat(opcode::I32_STORE),
1168    |Args {
1169         store_inner,
1170         modules,
1171         resumable,
1172         wasm,
1173         current_module,
1174         ..
1175     }| {
1176        let memarg = MemArg::read(wasm).unwrap_validated();
1177
1178        let data_to_store: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1179        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1180
1181        // SAFETY: The current module address must come from the current
1182        // store, because it is the only parameter to this function that
1183        // can contain module addresses. All stores guarantee all
1184        // addresses in them to be valid within themselves.
1185        let module = unsafe { modules.get(*current_module) };
1186
1187        // SAFETY: Validation guarantees at least one memory to exist.
1188        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1189        // SAFETY: This memory address was just read from the current
1190        // store. Therefore, it is valid in the current store.
1191        let mem = unsafe { store_inner.memories.get(mem_addr) };
1192
1193        let idx = calculate_mem_address(&memarg, relative_address)?;
1194        mem.mem.store(idx, data_to_store)?;
1195
1196        trace!("Instruction: i32.store [{relative_address} {data_to_store}] -> []");
1197        Ok(ControlFlow::Continue(()))
1198    }
1199}
1200
1201define_instruction_fn! {
1202    i64_store,
1203    fuel_check = flat(opcode::I64_STORE),
1204    |Args {
1205         store_inner,
1206         modules,
1207         resumable,
1208         wasm,
1209         current_module,
1210         ..
1211     }| {
1212        let memarg = MemArg::read(wasm).unwrap_validated();
1213
1214        let data_to_store: u64 = resumable.stack.pop_value().try_into().unwrap_validated();
1215        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1216
1217        // SAFETY: The current module address must come from the current
1218        // store, because it is the only parameter to this function that
1219        // can contain module addresses. All stores guarantee all
1220        // addresses in them to be valid within themselves.
1221        let module = unsafe { modules.get(*current_module) };
1222
1223        // SAFETY: Validation guarantees at least one memory to exist.
1224        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1225        // SAFETY: This memory address was just read from the current
1226        // store. Therefore, it is valid in the current store.
1227        let mem = unsafe { store_inner.memories.get(mem_addr) };
1228
1229        let idx = calculate_mem_address(&memarg, relative_address)?;
1230        mem.mem.store(idx, data_to_store)?;
1231
1232        trace!("Instruction: i64.store [{relative_address} {data_to_store}] -> []");
1233        Ok(ControlFlow::Continue(()))
1234    }
1235}
1236
1237define_instruction_fn! {
1238    f32_store,
1239    fuel_check = flat(opcode::F32_STORE),
1240    |Args {
1241         store_inner,
1242         modules,
1243         resumable,
1244         wasm,
1245         current_module,
1246         ..
1247     }| {
1248        let memarg = MemArg::read(wasm).unwrap_validated();
1249
1250        let data_to_store: F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1251        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1252
1253        // SAFETY: The current module address must come from the current
1254        // store, because it is the only parameter to this function that
1255        // can contain module addresses. All stores guarantee all
1256        // addresses in them to be valid within themselves.
1257        let module = unsafe { modules.get(*current_module) };
1258
1259        // SAFETY: Validation guarantees at least one memory to exist.
1260        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1261        // SAFETY: This memory address was just read from the current
1262        // store. Therefore, it is valid in the current store.
1263        let mem = unsafe { store_inner.memories.get(mem_addr) };
1264
1265        let idx = calculate_mem_address(&memarg, relative_address)?;
1266        mem.mem.store(idx, data_to_store)?;
1267
1268        trace!("Instruction: f32.store [{relative_address} {data_to_store}] -> []");
1269        Ok(ControlFlow::Continue(()))
1270    }
1271}
1272
1273define_instruction_fn! {
1274    f64_store,
1275    fuel_check = flat(opcode::F64_STORE),
1276    |Args {
1277         store_inner,
1278         modules,
1279         resumable,
1280         wasm,
1281         current_module,
1282         ..
1283     }| {
1284        let memarg = MemArg::read(wasm).unwrap_validated();
1285
1286        let data_to_store: F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1287        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1288
1289        // SAFETY: The current module address must come from the current
1290        // store, because it is the only parameter to this function that
1291        // can contain module addresses. All stores guarantee all
1292        // addresses in them to be valid within themselves.
1293        let module = unsafe { modules.get(*current_module) };
1294
1295        // SAFETY: Validation guarantees at least one memory to exist.
1296        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1297        // SAFETY: This memory address was just read from the current
1298        // store. Therefore, it is valid in the current store.
1299        let mem = unsafe { store_inner.memories.get(mem_addr) };
1300
1301        let idx = calculate_mem_address(&memarg, relative_address)?;
1302        mem.mem.store(idx, data_to_store)?;
1303
1304        trace!("Instruction: f64.store [{relative_address} {data_to_store}] -> []");
1305        Ok(ControlFlow::Continue(()))
1306    }
1307}
1308
1309define_instruction_fn! {
1310    v128_store,
1311    fuel_check = flat_fd(opcode::fd_extensions::V128_STORE),
1312    |Args {
1313         wasm,
1314         resumable,
1315         modules,
1316         current_module,
1317         store_inner,
1318         ..
1319     }| {
1320        let memarg = MemArg::read(wasm).unwrap_validated();
1321        // SAFETY: The current module address must come from the current
1322        // store, because it is the only parameter to this function that
1323        // can contain module addresses. All stores guarantee all
1324        // addresses in them to be valid within themselves.
1325        let module = unsafe { modules.get(*current_module) };
1326
1327        // SAFETY: Validation guarantees at least one memory to
1328        // exist.
1329        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1330        // SAFETY: This memory address was just read from the
1331        // current store. Therefore, it is valid in the current
1332        // store.
1333        let memory = unsafe { store_inner.memories.get(mem_addr) };
1334
1335        let data: [u8; 16] = resumable.stack.pop_value().try_into().unwrap_validated();
1336        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1337        let idx = calculate_mem_address(&memarg, relative_address)?;
1338
1339        memory.mem.store(idx, u128::from_le_bytes(data))?;
1340        Ok(ControlFlow::Continue(()))
1341    }
1342}
1343
1344// t.storeN
1345define_instruction_fn! {
1346    i32_store8,
1347    fuel_check = flat(opcode::I32_STORE8),
1348    |Args {
1349         store_inner,
1350         modules,
1351         resumable,
1352         wasm,
1353         current_module,
1354         ..
1355     }| {
1356        let memarg = MemArg::read(wasm).unwrap_validated();
1357
1358        let data_to_store: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1359        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1360
1361        let wrapped_data = data_to_store as i8;
1362
1363        // SAFETY: The current module address must come from the current
1364        // store, because it is the only parameter to this function that
1365        // can contain module addresses. All stores guarantee all
1366        // addresses in them to be valid within themselves.
1367        let module = unsafe { modules.get(*current_module) };
1368
1369        // SAFETY: Validation guarantees at least one memory to exist.
1370        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1371        // SAFETY: This memory address was just read from the current
1372        // store. Therefore, it is valid in the current store.
1373        let mem = unsafe { store_inner.memories.get(mem_addr) };
1374
1375        let idx = calculate_mem_address(&memarg, relative_address)?;
1376        mem.mem.store(idx, wrapped_data)?;
1377
1378        trace!("Instruction: i32.store8 [{relative_address} {wrapped_data}] -> []");
1379        Ok(ControlFlow::Continue(()))
1380    }
1381}
1382
1383define_instruction_fn! {
1384    i32_store16,
1385    fuel_check = flat(opcode::I32_STORE16),
1386    |Args {
1387         store_inner,
1388         modules,
1389         resumable,
1390         wasm,
1391         current_module,
1392         ..
1393     }| {
1394        let memarg = MemArg::read(wasm).unwrap_validated();
1395
1396        let data_to_store: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1397        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1398
1399        let wrapped_data = data_to_store as i16;
1400
1401        // SAFETY: The current module address must come from the current
1402        // store, because it is the only parameter to this function that
1403        // can contain module addresses. All stores guarantee all
1404        // addresses in them to be valid within themselves.
1405        let module = unsafe { modules.get(*current_module) };
1406
1407        // SAFETY: Validation guarantees at least one memory to exist.
1408        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1409        // SAFETY: This memory address was just read from the current
1410        // store. Therefore, it is valid in the current store.
1411        let mem = unsafe { store_inner.memories.get(mem_addr) };
1412
1413        let idx = calculate_mem_address(&memarg, relative_address)?;
1414        mem.mem.store(idx, wrapped_data)?;
1415
1416        trace!("Instruction: i32.store16 [{relative_address} {data_to_store}] -> []");
1417        Ok(ControlFlow::Continue(()))
1418    }
1419}
1420
1421define_instruction_fn! {
1422    i64_store8,
1423    fuel_check = flat(opcode::I64_STORE8),
1424    |Args {
1425         store_inner,
1426         modules,
1427         resumable,
1428         wasm,
1429         current_module,
1430         ..
1431     }| {
1432        let memarg = MemArg::read(wasm).unwrap_validated();
1433
1434        let data_to_store: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1435        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1436
1437        let wrapped_data = data_to_store as i8;
1438
1439        // SAFETY: The current module address must come from the current
1440        // store, because it is the only parameter to this function that
1441        // can contain module addresses. All stores guarantee all
1442        // addresses in them to be valid within themselves.
1443        let module = unsafe { modules.get(*current_module) };
1444
1445        // SAFETY: Validation guarantees at least one memory to exist.
1446        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1447        // SAFETY: This memory address was just read from the current
1448        // store. Therefore, it is valid in the current store.
1449        let mem = unsafe { store_inner.memories.get(mem_addr) };
1450
1451        let idx = calculate_mem_address(&memarg, relative_address)?;
1452        mem.mem.store(idx, wrapped_data)?;
1453
1454        trace!("Instruction: i64.store8 [{relative_address} {data_to_store}] -> []");
1455        Ok(ControlFlow::Continue(()))
1456    }
1457}
1458
1459define_instruction_fn! {
1460    i64_store16,
1461    fuel_check = flat(opcode::I64_STORE16),
1462    |Args {
1463         store_inner,
1464         modules,
1465         resumable,
1466         wasm,
1467         current_module,
1468         ..
1469     }| {
1470        let memarg = MemArg::read(wasm).unwrap_validated();
1471
1472        let data_to_store: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1473        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1474
1475        let wrapped_data = data_to_store as i16;
1476
1477        // SAFETY: The current module address must come from the current
1478        // store, because it is the only parameter to this function that
1479        // can contain module addresses. All stores guarantee all
1480        // addresses in them to be valid within themselves.
1481        let module = unsafe { modules.get(*current_module) };
1482
1483        // SAFETY: Validation guarantees at least one memory to exist.
1484        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1485        // SAFETY: This memory address was just read from the current
1486        // store. Therefore, it is valid in the current store.
1487        let mem = unsafe { store_inner.memories.get(mem_addr) };
1488
1489        let idx = calculate_mem_address(&memarg, relative_address)?;
1490        mem.mem.store(idx, wrapped_data)?;
1491
1492        trace!("Instruction: i64.store16 [{relative_address} {data_to_store}] -> []");
1493        Ok(ControlFlow::Continue(()))
1494    }
1495}
1496
1497define_instruction_fn! {
1498    i64_store32,
1499    fuel_check = flat(opcode::I64_STORE32),
1500    |Args {
1501         store_inner,
1502         modules,
1503         resumable,
1504         wasm,
1505         current_module,
1506         ..
1507     }| {
1508        let memarg = MemArg::read(wasm).unwrap_validated();
1509
1510        let data_to_store: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1511        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1512
1513        let wrapped_data = data_to_store as i32;
1514
1515        // SAFETY: The current module address must come from the current
1516        // store, because it is the only parameter to this function that
1517        // can contain module addresses. All stores guarantee all
1518        // addresses in them to be valid within themselves.
1519        let module = unsafe { modules.get(*current_module) };
1520
1521        // SAFETY: Validation guarantees at least one memory to exist.
1522        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1523        // SAFETY: This memory address was just read from the current
1524        // store. Therefore, it is valid in the current store.
1525        let mem = unsafe { store_inner.memories.get(mem_addr) };
1526
1527        let idx = calculate_mem_address(&memarg, relative_address)?;
1528        mem.mem.store(idx, wrapped_data)?;
1529
1530        trace!("Instruction: i64.store32 [{relative_address} {data_to_store}] -> []");
1531        Ok(ControlFlow::Continue(()))
1532    }
1533}
1534
1535// v128.storeN_lane
1536define_instruction_fn! {
1537    v128_store8_lane,
1538    fuel_check = flat_fd(opcode::fd_extensions::V128_STORE8_LANE),
1539    |Args {
1540         wasm,
1541         resumable,
1542         modules,
1543         current_module,
1544         store_inner,
1545         ..
1546     }| {
1547        let data: [u8; 16] = resumable.stack.pop_value().try_into().unwrap_validated();
1548        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1549        let memarg = MemArg::read(wasm).unwrap_validated();
1550        // SAFETY: The current module address must come from the current
1551        // store, because it is the only parameter to this function that
1552        // can contain module addresses. All stores guarantee all
1553        // addresses in them to be valid within themselves.
1554        let module = unsafe { modules.get(*current_module) };
1555
1556        // SAFETY: Validation guarantees at least one memory to
1557        // exist.
1558        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1559        // SAFETY: This memory address was just read from the
1560        // current store. Therefore, it is valid in the current
1561        // store.
1562        let memory = unsafe { store_inner.memories.get(mem_addr) };
1563        let idx = calculate_mem_address(&memarg, relative_address)?;
1564        let lane_idx = usize::from(wasm.read_u8().unwrap_validated());
1565
1566        let lane = *to_lanes::<1, 16, u8>(data).get(lane_idx).unwrap_validated();
1567
1568        memory.mem.store::<1, u8>(idx, lane)?;
1569        Ok(ControlFlow::Continue(()))
1570    }
1571}
1572define_instruction_fn! {
1573    v128_store16_lane,
1574    fuel_check = flat_fd(opcode::fd_extensions::V128_STORE16_LANE),
1575    |Args {
1576         wasm,
1577         resumable,
1578         modules,
1579         current_module,
1580         store_inner,
1581         ..
1582     }| {
1583        let data: [u8; 16] = resumable.stack.pop_value().try_into().unwrap_validated();
1584        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1585        let memarg = MemArg::read(wasm).unwrap_validated();
1586        // SAFETY: The current module address must come from the current
1587        // store, because it is the only parameter to this function that
1588        // can contain module addresses. All stores guarantee all
1589        // addresses in them to be valid within themselves.
1590        let module = unsafe { modules.get(*current_module) };
1591
1592        // SAFETY: Validation guarantees at least one memory to
1593        // exist.
1594        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1595        // SAFETY: This memory address was just read from the
1596        // current store. Therefore, it is valid in the current
1597        // store.
1598        let memory = unsafe { store_inner.memories.get(mem_addr) };
1599        let idx = calculate_mem_address(&memarg, relative_address)?;
1600        let lane_idx = usize::from(wasm.read_u8().unwrap_validated());
1601
1602        let lane = *to_lanes::<2, 8, u16>(data).get(lane_idx).unwrap_validated();
1603
1604        memory.mem.store::<2, u16>(idx, lane)?;
1605        Ok(ControlFlow::Continue(()))
1606    }
1607}
1608define_instruction_fn! {
1609    v128_store32_lane,
1610    fuel_check = flat_fd(opcode::fd_extensions::V128_STORE32_LANE),
1611    |Args {
1612         wasm,
1613         resumable,
1614         modules,
1615         current_module,
1616         store_inner,
1617         ..
1618     }| {
1619        let data: [u8; 16] = resumable.stack.pop_value().try_into().unwrap_validated();
1620        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1621        let memarg = MemArg::read(wasm).unwrap_validated();
1622        // SAFETY: The current module address must come from the current
1623        // store, because it is the only parameter to this function that
1624        // can contain module addresses. All stores guarantee all
1625        // addresses in them to be valid within themselves.
1626        let module = unsafe { modules.get(*current_module) };
1627
1628        // SAFETY: Validation guarantees at least one memory to
1629        // exist.
1630        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1631        // SAFETY: This memory address was just read from the
1632        // current store. Therefore, it is valid in the current
1633        // store.
1634        let memory = unsafe { store_inner.memories.get(mem_addr) };
1635        let idx = calculate_mem_address(&memarg, relative_address)?;
1636        let lane_idx = usize::from(wasm.read_u8().unwrap_validated());
1637
1638        let lane = *to_lanes::<4, 4, u32>(data).get(lane_idx).unwrap_validated();
1639
1640        memory.mem.store::<4, u32>(idx, lane)?;
1641        Ok(ControlFlow::Continue(()))
1642    }
1643}
1644define_instruction_fn! {
1645    v128_store64_lane,
1646    fuel_check = flat_fd(opcode::fd_extensions::V128_STORE64_LANE),
1647    |Args {
1648         wasm,
1649         resumable,
1650         modules,
1651         current_module,
1652         store_inner,
1653         ..
1654     }| {
1655        let data: [u8; 16] = resumable.stack.pop_value().try_into().unwrap_validated();
1656        let relative_address: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1657        let memarg = MemArg::read(wasm).unwrap_validated();
1658        // SAFETY: The current module address must come from the current
1659        // store, because it is the only parameter to this function that
1660        // can contain module addresses. All stores guarantee all
1661        // addresses in them to be valid within themselves.
1662        let module = unsafe { modules.get(*current_module) };
1663
1664        // SAFETY: Validation guarantees at least one memory to
1665        // exist.
1666        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1667        // SAFETY: This memory address was just read from the
1668        // current store. Therefore, it is valid in the current
1669        // store.
1670        let memory = unsafe { store_inner.memories.get(mem_addr) };
1671        let idx = calculate_mem_address(&memarg, relative_address)?;
1672        let lane_idx = usize::from(wasm.read_u8().unwrap_validated());
1673
1674        let lane = *to_lanes::<8, 2, u64>(data).get(lane_idx).unwrap_validated();
1675
1676        memory.mem.store::<8, u64>(idx, lane)?;
1677        Ok(ControlFlow::Continue(()))
1678    }
1679}
1680
1681// memory.size
1682define_instruction_fn! {
1683    memory_size,
1684    fuel_check = flat(opcode::MEMORY_SIZE),
1685    |Args {
1686         store_inner,
1687         modules,
1688         resumable,
1689         wasm,
1690         current_module,
1691         ..
1692     }| {
1693        // Note: This zero byte is reserved for the multiple memories
1694        // proposal.
1695        let _zero = wasm.read_u8().unwrap_validated();
1696        // SAFETY: The current module address must come from the current
1697        // store, because it is the only parameter to this function that
1698        // can contain module addresses. All stores guarantee all
1699        // addresses in them to be valid within themselves.
1700        let module = unsafe { modules.get(*current_module) };
1701
1702        // SAFETY: Validation guarantees at least one memory to exist.
1703        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1704        // SAFETY: This memory address was just read from the current
1705        // store. Therefore, it is valid in the current store.
1706        let mem = unsafe { store_inner.memories.get(mem_addr) };
1707        let size = mem.size() as u32;
1708        resumable.stack.push_value(Value::I32(size))?;
1709        trace!("Instruction: memory.size [] -> [{}]", size);
1710        Ok(ControlFlow::Continue(()))
1711    }
1712}
1713
1714// memory.grow
1715define_instruction_fn! {
1716    memory_grow,
1717    fuel_check = omit,
1718    |Args {
1719         store_inner,
1720         modules,
1721         resumable,
1722         wasm,
1723         current_module,
1724         ..
1725     }| {
1726        // Note: This zero byte is reserved for the multiple memories
1727        // proposal.
1728        let _zero = wasm.read_u8().unwrap_validated();
1729        // SAFETY: The current module address must come from the current
1730        // store, because it is the only parameter to this function that
1731        // can contain module addresses. All stores guarantee all
1732        // addresses in them to be valid within themselves.
1733        let module = unsafe { modules.get(*current_module) };
1734
1735        // SAFETY: Validation guarantees at least one memory to exist.
1736        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1737        // SAFETY: This memory address was just read from the current
1738        // store. Therefore, it is valid in the current store.
1739        let mem = unsafe { store_inner.memories.get_mut(mem_addr) };
1740
1741        let sz: u32 = mem.size() as u32;
1742
1743        let n: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1744        // decrement fuel, but push n back if it fails
1745        let cost = T::get_flat_cost(opcode::MEMORY_GROW)
1746            + u64::from(n) * T::get_cost_per_element(opcode::MEMORY_GROW);
1747        if let Some(fuel) = &mut resumable.maybe_fuel {
1748            if *fuel >= cost {
1749                *fuel -= cost;
1750            } else {
1751                resumable.stack.push_value(Value::I32(n)).unwrap_validated(); // we are pushing back what was just popped, this can't panic.
1752
1753                return Ok(ControlFlow::Break(InterpreterLoopOutcome::OutOfFuel {
1754                    required_fuel: NonZeroU64::new(cost - *fuel).expect(
1755                        "the last check guarantees that the current fuel is smaller than cost",
1756                    ),
1757                }));
1758            }
1759        }
1760
1761        // TODO this instruction is non-deterministic w.r.t. spec, and can fail if the embedder wills it.
1762        // for now we execute it always according to the following match expr.
1763        // if the grow operation fails, err := Value::I32(2^32-1) is pushed to the resumable.stack per spec
1764        let pushed_value = match mem.grow(n) {
1765            Ok(_) => sz,
1766            Err(_) => u32::MAX,
1767        };
1768        resumable.stack.push_value(Value::I32(pushed_value))?;
1769        trace!("Instruction: memory.grow [{}] -> [{}]", n, pushed_value);
1770        Ok(ControlFlow::Continue(()))
1771    }
1772}
1773
1774// memory.fill
1775// See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-fill
1776define_instruction_fn! {
1777    memory_fill,
1778    fuel_check = omit,
1779    |Args {
1780         resumable,
1781         wasm,
1782         store_inner,
1783         modules,
1784         current_module,
1785         ..
1786     }| {
1787        //  mappings:
1788        //      n => number of bytes to update
1789        //      val => the value to set each byte to (must be < 256)
1790        //      d => the pointer to the region to update
1791
1792        // Note: This zero byte is reserved for the multiple
1793        // memories proposal.
1794        let _zero = wasm.read_u8().unwrap_validated();
1795
1796        // SAFETY: The current module address must come from the current
1797        // store, because it is the only parameter to this function that
1798        // can contain module addresses. All stores guarantee all
1799        // addresses in them to be valid within themselves.
1800        let module = unsafe { modules.get(*current_module) };
1801
1802        // SAFETY: Validation guarantees at least one memory to exist.
1803        let mem_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1804        // SAFETY: This memory address was just read from the
1805        // current store. Therefore, it is valid in the current
1806        // store.
1807        let mem = unsafe { store_inner.memories.get(mem_addr) };
1808
1809        let n: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1810        // decrement fuel, but push n back if it fails
1811        let cost = T::get_fc_extension_flat_cost(opcode::fc_extensions::MEMORY_FILL)
1812            + u64::from(n)
1813                * T::get_fc_extension_cost_per_element(opcode::fc_extensions::MEMORY_FILL);
1814        if let Some(fuel) = &mut resumable.maybe_fuel {
1815            if *fuel >= cost {
1816                *fuel -= cost;
1817            } else {
1818                resumable.stack.push_value(Value::I32(n)).unwrap_validated(); // we are pushing back what was just popped, this can't panic.
1819                return Ok(ControlFlow::Break(InterpreterLoopOutcome::OutOfFuel {
1820                    required_fuel: NonZeroU64::new(cost - *fuel).expect(
1821                        "the last check guarantees that the current fuel is smaller than cost",
1822                    ),
1823                }));
1824            }
1825        }
1826
1827        let val: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1828
1829        if !(0..=255).contains(&val) {
1830            warn!("Value for memory.fill does not fit in a byte ({val})");
1831        }
1832
1833        let d: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1834
1835        mem.mem
1836            .fill(d.cast_unsigned().into_usize(), val as u8, n.into_usize())?;
1837
1838        trace!("Instruction: memory.fill");
1839        Ok(ControlFlow::Continue(()))
1840    }
1841}
1842
1843// memory.copy
1844// See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-copy
1845define_instruction_fn! {
1846    memory_copy,
1847    fuel_check = omit,
1848    |Args {
1849         resumable,
1850         wasm,
1851         store_inner,
1852         modules,
1853         current_module,
1854         ..
1855     }| {
1856        //  mappings:
1857        //      n => number of bytes to copy
1858        //      s => source address to copy from
1859        //      d => destination address to copy to
1860        // Note: These zero bytes are reserved for the multiple
1861        // memories proposal.
1862        let _zero = wasm.read_u8().unwrap_validated();
1863        let _zero = wasm.read_u8().unwrap_validated();
1864
1865        // SAFETY: The current module address must come from the current
1866        // store, because it is the only parameter to this function that
1867        // can contain module addresses. All stores guarantee all
1868        // addresses in them to be valid within themselves.
1869        let module = unsafe { modules.get(*current_module) };
1870
1871        // SAFETY: Validation guarantees at least one memory to
1872        // exist.
1873        let src_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1874        // SAFETY: Validation guarantees at least one memory to
1875        // exist.
1876        let dst_addr = *unsafe { module.mem_addrs.get(MemIdx::new(0)) };
1877
1878        let n: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1879        // decrement fuel, but push n back if it fails
1880        let cost = T::get_fc_extension_flat_cost(opcode::fc_extensions::MEMORY_COPY)
1881            + u64::from(n)
1882                * T::get_fc_extension_cost_per_element(opcode::fc_extensions::MEMORY_COPY);
1883        if let Some(fuel) = &mut resumable.maybe_fuel {
1884            if *fuel >= cost {
1885                *fuel -= cost;
1886            } else {
1887                resumable.stack.push_value(Value::I32(n)).unwrap_validated(); // we are pushing back what was just popped, this can't panic.
1888                return Ok(ControlFlow::Break(InterpreterLoopOutcome::OutOfFuel {
1889                    required_fuel: NonZeroU64::new(cost - *fuel).expect(
1890                        "the last check guarantees that the current fuel is smaller than cost",
1891                    ),
1892                }));
1893            }
1894        }
1895
1896        let s: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1897        let d: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1898
1899        // SAFETY: This source memory address was just read from
1900        // the current store. Therefore, it must also be valid
1901        // in the current store.
1902        let src_mem = unsafe { store_inner.memories.get(src_addr) };
1903        // SAFETY: This destination memory address was just read
1904        // from the current store. Therefore, it must also be
1905        // valid in the current store.
1906        let dest_mem = unsafe { store_inner.memories.get(dst_addr) };
1907
1908        dest_mem.mem.copy(
1909            d.cast_unsigned().into_usize(),
1910            &src_mem.mem,
1911            s.cast_unsigned().into_usize(),
1912            n.into_usize(),
1913        )?;
1914        trace!("Instruction: memory.copy");
1915        Ok(ControlFlow::Continue(()))
1916    }
1917}
1918
1919// memory.init
1920// See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-init-x
1921// Copy a region from a data segment into memory
1922define_instruction_fn! {
1923    memory_init_fn,
1924    fuel_check = omit,
1925    |Args {
1926         wasm,
1927         resumable,
1928         modules,
1929         current_module,
1930         store_inner,
1931         ..
1932     }| {
1933        //  mappings:
1934        //      n => number of bytes to copy
1935        //      s =            }> starting pointer in the data segment
1936        //      d => destination address to copy to
1937        // SAFETY: Validation guarantees there to be a valid
1938        // data index next.
1939        let data_idx = unsafe { DataIdx::read_unchecked(wasm) };
1940
1941        // Note: This zero byte is reserved for the multiple memories
1942        // proposal.
1943        let _zero = wasm.read_u8().unwrap_validated();
1944
1945        let n: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1946        // decrement fuel, but push n back if it fails
1947        let cost = T::get_fc_extension_flat_cost(opcode::fc_extensions::MEMORY_INIT)
1948            + u64::from(n)
1949                * T::get_fc_extension_cost_per_element(opcode::fc_extensions::MEMORY_INIT);
1950        if let Some(fuel) = &mut resumable.maybe_fuel {
1951            if *fuel >= cost {
1952                *fuel -= cost;
1953            } else {
1954                resumable.stack.push_value(Value::I32(n)).unwrap_validated(); // we are pushing back what was just popped, this can't panic.
1955                return Ok(ControlFlow::Break(InterpreterLoopOutcome::OutOfFuel {
1956                    required_fuel: NonZeroU64::new(cost - *fuel).expect(
1957                        "the last check guarantees that the current fuel is smaller than cost",
1958                    ),
1959                }));
1960            }
1961        }
1962
1963        let s: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1964        let d: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1965
1966        // SAFETY: All requirements are met:
1967        // 1. The current module address must come from the
1968        //    current store, because it is the only parameter to
1969        //    this function that can contain module addresses. All
1970        //    stores guarantee all addresses in them to be valid
1971        //    within themselves.
1972        // 2. Validation guarantees at least one memory to exist.
1973        // 3./5. The memory and data addresses are valid for a
1974        //       similar reason that the module address is valid:
1975        //       they are stored in the current module instance,
1976        //       which is also part of the current store.
1977        // 4. Validation gurantees this data index to be valid
1978        //    for the current module instance.
1979        unsafe {
1980            memory_init(
1981                modules,
1982                &mut store_inner.memories,
1983                &store_inner.data,
1984                *current_module,
1985                data_idx,
1986                MemIdx::new(0),
1987                n,
1988                s,
1989                d,
1990            )?
1991        };
1992        Ok(ControlFlow::Continue(()))
1993    }
1994}
1995
1996// data.drop
1997define_instruction_fn! {
1998    data_drop_fn,
1999    fuel_check = flat_fc(opcode::fc_extensions::DATA_DROP),
2000    |Args {
2001         wasm,
2002         modules,
2003         current_module,
2004         store_inner,
2005         ..
2006     }| {
2007        // SAFETY: Validation guarantees there to be a valid
2008        // data index next.
2009        let data_idx = unsafe { DataIdx::read_unchecked(wasm) };
2010        // SAFETY: All requirements are met:
2011        // 1. The current module address must come from the
2012        //    current store, because it is the only parameter to
2013        //    this function that can contain module addresses. All
2014        //    stores guarantee all addresses in them to be valid
2015        //    within themselves.
2016        // 2. Validation guarantees the data index to be valid
2017        //    for the current module instance.
2018        // 3. The data address is valid for a similar reason that
2019        //    the module address is valid: it is stored in the
2020        //    current module instance, which is also part of the
2021        //    current store.
2022        unsafe { data_drop(modules, &mut store_inner.data, *current_module, data_idx) };
2023        Ok(ControlFlow::Continue(()))
2024    }
2025}