1 //! Cranelift IR interpreter.
2 //!
3 //! This module partially contains the logic for interpreting Cranelift IR.
4 
5 use crate::address::{Address, AddressFunctionEntry, AddressRegion, AddressSize};
6 use crate::environment::{FuncIndex, FunctionStore};
7 use crate::frame::Frame;
8 use crate::instruction::DfgInstructionContext;
9 use crate::state::{InterpreterFunctionRef, MemoryError, State};
10 use crate::step::{ControlFlow, CraneliftTrap, StepError, step};
11 use crate::value::{DataValueExt, ValueError};
12 use cranelift_codegen::data_value::DataValue;
13 use cranelift_codegen::ir::{
14     ArgumentPurpose, Block, Endianness, ExternalName, FuncRef, Function, GlobalValue,
15     GlobalValueData, LibCall, MemFlags, StackSlot, Type,
16 };
17 use log::trace;
18 use smallvec::SmallVec;
19 use std::fmt::Debug;
20 use std::iter;
21 use thiserror::Error;
22 
23 /// The Cranelift interpreter; this contains some high-level functions to control the interpreter's
24 /// flow. The interpreter state is defined separately (see [InterpreterState]) as the execution
25 /// semantics for each Cranelift instruction (see [step]).
26 pub struct Interpreter<'a> {
27     state: InterpreterState<'a>,
28     fuel: Option<u64>,
29 }
30 
31 impl<'a> Interpreter<'a> {
new(state: InterpreterState<'a>) -> Self32     pub fn new(state: InterpreterState<'a>) -> Self {
33         Self { state, fuel: None }
34     }
35 
36     /// The `fuel` mechanism sets a number of instructions that
37     /// the interpreter can execute before stopping. If this
38     /// value is `None` (the default), no limit is imposed.
with_fuel(self, fuel: Option<u64>) -> Self39     pub fn with_fuel(self, fuel: Option<u64>) -> Self {
40         Self { fuel, ..self }
41     }
42 
43     /// Call a function by name; this is a helpful proxy for [Interpreter::call_by_index].
call_by_name( &mut self, func_name: &str, arguments: &[DataValue], ) -> Result<ControlFlow<'a>, InterpreterError>44     pub fn call_by_name(
45         &mut self,
46         func_name: &str,
47         arguments: &[DataValue],
48     ) -> Result<ControlFlow<'a>, InterpreterError> {
49         let index = self
50             .state
51             .functions
52             .index_of(func_name)
53             .ok_or_else(|| InterpreterError::UnknownFunctionName(func_name.to_string()))?;
54         self.call_by_index(index, arguments)
55     }
56 
57     /// Call a function by its index in the [FunctionStore]; this is a proxy for
58     /// `Interpreter::call`.
call_by_index( &mut self, index: FuncIndex, arguments: &[DataValue], ) -> Result<ControlFlow<'a>, InterpreterError>59     pub fn call_by_index(
60         &mut self,
61         index: FuncIndex,
62         arguments: &[DataValue],
63     ) -> Result<ControlFlow<'a>, InterpreterError> {
64         match self.state.functions.get_by_index(index) {
65             None => Err(InterpreterError::UnknownFunctionIndex(index)),
66             Some(func) => self.call(func, arguments),
67         }
68     }
69 
70     /// Interpret a call to a [Function] given its [DataValue] arguments.
call( &mut self, function: &'a Function, arguments: &[DataValue], ) -> Result<ControlFlow<'a>, InterpreterError>71     fn call(
72         &mut self,
73         function: &'a Function,
74         arguments: &[DataValue],
75     ) -> Result<ControlFlow<'a>, InterpreterError> {
76         trace!("Call: {}({:?})", function.name, arguments);
77         let first_block = function
78             .layout
79             .blocks()
80             .next()
81             .expect("to have a first block");
82         let parameters = function.dfg.block_params(first_block);
83         self.state.push_frame(function);
84         self.state
85             .current_frame_mut()
86             .set_all(parameters, arguments.to_vec());
87 
88         self.block(first_block)
89     }
90 
91     /// Interpret a [Block] in a [Function]. This drives the interpretation over sequences of
92     /// instructions, which may continue in other blocks, until the function returns.
block(&mut self, block: Block) -> Result<ControlFlow<'a>, InterpreterError>93     fn block(&mut self, block: Block) -> Result<ControlFlow<'a>, InterpreterError> {
94         trace!("Block: {block}");
95         let function = self.state.current_frame_mut().function();
96         let layout = &function.layout;
97         let mut maybe_inst = layout.first_inst(block);
98         while let Some(inst) = maybe_inst {
99             if self.consume_fuel() == FuelResult::Stop {
100                 return Err(InterpreterError::FuelExhausted);
101             }
102 
103             let inst_context = DfgInstructionContext::new(inst, &function.dfg);
104             match step(&mut self.state, inst_context)? {
105                 ControlFlow::Assign(values) => {
106                     self.state
107                         .current_frame_mut()
108                         .set_all(function.dfg.inst_results(inst), values.to_vec());
109                     maybe_inst = layout.next_inst(inst)
110                 }
111                 ControlFlow::Continue => maybe_inst = layout.next_inst(inst),
112                 ControlFlow::ContinueAt(block, block_arguments) => {
113                     trace!("Block: {block}");
114                     self.state
115                         .current_frame_mut()
116                         .set_all(function.dfg.block_params(block), block_arguments.to_vec());
117                     maybe_inst = layout.first_inst(block)
118                 }
119                 ControlFlow::Call(called_function, arguments) => {
120                     match self.call(called_function, &arguments)? {
121                         ControlFlow::Return(rets) => {
122                             self.state
123                                 .current_frame_mut()
124                                 .set_all(function.dfg.inst_results(inst), rets.to_vec());
125                             maybe_inst = layout.next_inst(inst)
126                         }
127                         ControlFlow::Trap(trap) => return Ok(ControlFlow::Trap(trap)),
128                         cf => {
129                             panic!("invalid control flow after call: {cf:?}")
130                         }
131                     }
132                 }
133                 ControlFlow::ReturnCall(callee, args) => {
134                     self.state.pop_frame();
135 
136                     return match self.call(callee, &args)? {
137                         ControlFlow::Return(rets) => Ok(ControlFlow::Return(rets)),
138                         ControlFlow::Trap(trap) => Ok(ControlFlow::Trap(trap)),
139                         cf => {
140                             panic!("invalid control flow after return_call: {cf:?}")
141                         }
142                     };
143                 }
144                 ControlFlow::Return(returned_values) => {
145                     self.state.pop_frame();
146                     return Ok(ControlFlow::Return(returned_values));
147                 }
148                 ControlFlow::Trap(trap) => return Ok(ControlFlow::Trap(trap)),
149             }
150         }
151         Err(InterpreterError::Unreachable)
152     }
153 
consume_fuel(&mut self) -> FuelResult154     fn consume_fuel(&mut self) -> FuelResult {
155         match self.fuel {
156             Some(0) => FuelResult::Stop,
157             Some(ref mut n) => {
158                 *n -= 1;
159                 FuelResult::Continue
160             }
161 
162             // We do not have fuel enabled, so unconditionally continue
163             None => FuelResult::Continue,
164         }
165     }
166 }
167 
168 #[derive(Debug, PartialEq, Clone)]
169 /// The result of consuming fuel. Signals if the caller should stop or continue.
170 pub enum FuelResult {
171     /// We still have `fuel` available and should continue execution.
172     Continue,
173     /// The available `fuel` has been exhausted, we should stop now.
174     Stop,
175 }
176 
177 /// The ways interpretation can fail.
178 #[derive(Error, Debug)]
179 pub enum InterpreterError {
180     #[error("failed to interpret instruction")]
181     StepError(#[from] StepError),
182     #[error("reached an unreachable statement")]
183     Unreachable,
184     #[error("unknown function index (has it been added to the function store?): {0}")]
185     UnknownFunctionIndex(FuncIndex),
186     #[error("unknown function with name (has it been added to the function store?): {0}")]
187     UnknownFunctionName(String),
188     #[error("value error")]
189     ValueError(#[from] ValueError),
190     #[error("fuel exhausted")]
191     FuelExhausted,
192 }
193 
194 pub type LibCallValues = SmallVec<[DataValue; 1]>;
195 pub type LibCallHandler = fn(LibCall, LibCallValues) -> Result<LibCallValues, CraneliftTrap>;
196 
197 /// Maintains the [Interpreter]'s state, implementing the [State] trait.
198 pub struct InterpreterState<'a> {
199     pub functions: FunctionStore<'a>,
200     pub libcall_handler: LibCallHandler,
201     pub frame_stack: Vec<Frame<'a>>,
202     /// Number of bytes from the bottom of the stack where the current frame's stack space is
203     pub frame_offset: usize,
204     pub stack: Vec<u8>,
205     pub pinned_reg: DataValue,
206     pub native_endianness: Endianness,
207 }
208 
209 impl Default for InterpreterState<'_> {
default() -> Self210     fn default() -> Self {
211         let native_endianness = if cfg!(target_endian = "little") {
212             Endianness::Little
213         } else {
214             Endianness::Big
215         };
216         Self {
217             functions: FunctionStore::default(),
218             libcall_handler: |_, _| Err(CraneliftTrap::UnreachableCodeReached),
219             frame_stack: vec![],
220             frame_offset: 0,
221             stack: Vec::with_capacity(1024),
222             pinned_reg: DataValue::I64(0),
223             native_endianness,
224         }
225     }
226 }
227 
228 impl<'a> InterpreterState<'a> {
with_function_store(self, functions: FunctionStore<'a>) -> Self229     pub fn with_function_store(self, functions: FunctionStore<'a>) -> Self {
230         Self { functions, ..self }
231     }
232 
233     /// Registers a libcall handler
with_libcall_handler(mut self, handler: LibCallHandler) -> Self234     pub fn with_libcall_handler(mut self, handler: LibCallHandler) -> Self {
235         self.libcall_handler = handler;
236         self
237     }
238 }
239 
240 impl<'a> State<'a> for InterpreterState<'a> {
get_function(&self, func_ref: FuncRef) -> Option<&'a Function>241     fn get_function(&self, func_ref: FuncRef) -> Option<&'a Function> {
242         self.functions
243             .get_from_func_ref(func_ref, self.frame_stack.last().unwrap().function())
244     }
get_current_function(&self) -> &'a Function245     fn get_current_function(&self) -> &'a Function {
246         self.current_frame().function()
247     }
248 
get_libcall_handler(&self) -> LibCallHandler249     fn get_libcall_handler(&self) -> LibCallHandler {
250         self.libcall_handler
251     }
252 
push_frame(&mut self, function: &'a Function)253     fn push_frame(&mut self, function: &'a Function) {
254         if let Some(frame) = self.frame_stack.iter().last() {
255             self.frame_offset += frame.function().fixed_stack_size() as usize;
256         }
257 
258         // Grow the stack by the space necessary for this frame
259         self.stack
260             .extend(iter::repeat(0).take(function.fixed_stack_size() as usize));
261 
262         self.frame_stack.push(Frame::new(function));
263     }
pop_frame(&mut self)264     fn pop_frame(&mut self) {
265         if let Some(frame) = self.frame_stack.pop() {
266             // Shorten the stack after exiting the frame
267             self.stack
268                 .truncate(self.stack.len() - frame.function().fixed_stack_size() as usize);
269 
270             // Reset frame_offset to the start of this function
271             if let Some(frame) = self.frame_stack.iter().last() {
272                 self.frame_offset -= frame.function().fixed_stack_size() as usize;
273             }
274         }
275     }
276 
current_frame_mut(&mut self) -> &mut Frame<'a>277     fn current_frame_mut(&mut self) -> &mut Frame<'a> {
278         let num_frames = self.frame_stack.len();
279         match num_frames {
280             0 => panic!("unable to retrieve the current frame because no frames were pushed"),
281             _ => &mut self.frame_stack[num_frames - 1],
282         }
283     }
284 
current_frame(&self) -> &Frame<'a>285     fn current_frame(&self) -> &Frame<'a> {
286         let num_frames = self.frame_stack.len();
287         match num_frames {
288             0 => panic!("unable to retrieve the current frame because no frames were pushed"),
289             _ => &self.frame_stack[num_frames - 1],
290         }
291     }
292 
stack_address( &self, size: AddressSize, slot: StackSlot, offset: u64, ) -> Result<Address, MemoryError>293     fn stack_address(
294         &self,
295         size: AddressSize,
296         slot: StackSlot,
297         offset: u64,
298     ) -> Result<Address, MemoryError> {
299         let stack_slots = &self.get_current_function().sized_stack_slots;
300         let stack_slot = &stack_slots[slot];
301 
302         // offset must be `0 <= Offset < sizeof(SS)`
303         if offset >= stack_slot.size as u64 {
304             return Err(MemoryError::InvalidOffset {
305                 offset,
306                 max: stack_slot.size as u64,
307             });
308         }
309 
310         // Calculate the offset from the current frame to the requested stack slot
311         let slot_offset: u64 = stack_slots
312             .keys()
313             .filter(|k| k < &slot)
314             .map(|k| stack_slots[k].size as u64)
315             .sum();
316 
317         let final_offset = self.frame_offset as u64 + slot_offset + offset;
318         Address::from_parts(size, AddressRegion::Stack, 0, final_offset)
319     }
320 
checked_load( &self, addr: Address, ty: Type, mem_flags: MemFlags, ) -> Result<DataValue, MemoryError>321     fn checked_load(
322         &self,
323         addr: Address,
324         ty: Type,
325         mem_flags: MemFlags,
326     ) -> Result<DataValue, MemoryError> {
327         let load_size = ty.bytes() as usize;
328         let addr_start = addr.offset as usize;
329         let addr_end = addr_start + load_size;
330 
331         let src = match addr.region {
332             AddressRegion::Stack => {
333                 if addr_end > self.stack.len() {
334                     return Err(MemoryError::OutOfBoundsLoad {
335                         addr,
336                         load_size,
337                         mem_flags,
338                     });
339                 }
340 
341                 &self.stack[addr_start..addr_end]
342             }
343             _ => unimplemented!(),
344         };
345 
346         // Aligned flag is set and address is not aligned for the given type
347         if mem_flags.aligned() && addr_start % load_size != 0 {
348             return Err(MemoryError::MisalignedLoad { addr, load_size });
349         }
350 
351         Ok(match mem_flags.endianness(self.native_endianness) {
352             Endianness::Big => DataValue::read_from_slice_be(src, ty),
353             Endianness::Little => DataValue::read_from_slice_le(src, ty),
354         })
355     }
356 
checked_store( &mut self, addr: Address, v: DataValue, mem_flags: MemFlags, ) -> Result<(), MemoryError>357     fn checked_store(
358         &mut self,
359         addr: Address,
360         v: DataValue,
361         mem_flags: MemFlags,
362     ) -> Result<(), MemoryError> {
363         let store_size = v.ty().bytes() as usize;
364         let addr_start = addr.offset as usize;
365         let addr_end = addr_start + store_size;
366 
367         let dst = match addr.region {
368             AddressRegion::Stack => {
369                 if addr_end > self.stack.len() {
370                     return Err(MemoryError::OutOfBoundsStore {
371                         addr,
372                         store_size,
373                         mem_flags,
374                     });
375                 }
376 
377                 &mut self.stack[addr_start..addr_end]
378             }
379             _ => unimplemented!(),
380         };
381 
382         // Aligned flag is set and address is not aligned for the given type
383         if mem_flags.aligned() && addr_start % store_size != 0 {
384             return Err(MemoryError::MisalignedStore { addr, store_size });
385         }
386 
387         Ok(match mem_flags.endianness(self.native_endianness) {
388             Endianness::Big => v.write_to_slice_be(dst),
389             Endianness::Little => v.write_to_slice_le(dst),
390         })
391     }
392 
function_address( &self, size: AddressSize, name: &ExternalName, ) -> Result<Address, MemoryError>393     fn function_address(
394         &self,
395         size: AddressSize,
396         name: &ExternalName,
397     ) -> Result<Address, MemoryError> {
398         let curr_func = self.get_current_function();
399         let (entry, index) = match name {
400             ExternalName::User(username) => {
401                 let ext_name = &curr_func.params.user_named_funcs()[*username];
402 
403                 // TODO: This is not optimal since we are looking up by string name
404                 let index = self.functions.index_of(&ext_name.to_string()).unwrap();
405 
406                 (AddressFunctionEntry::UserFunction, index.as_u32())
407             }
408 
409             ExternalName::TestCase(testname) => {
410                 // TODO: This is not optimal since we are looking up by string name
411                 let index = self.functions.index_of(&testname.to_string()).unwrap();
412 
413                 (AddressFunctionEntry::UserFunction, index.as_u32())
414             }
415             ExternalName::LibCall(libcall) => {
416                 // We don't properly have a "libcall" store, but we can use `LibCall::all()`
417                 // and index into that.
418                 let index = LibCall::all_libcalls()
419                     .iter()
420                     .position(|lc| lc == libcall)
421                     .unwrap();
422 
423                 (AddressFunctionEntry::LibCall, index as u32)
424             }
425             _ => unimplemented!("function_address: {:?}", name),
426         };
427 
428         Address::from_parts(size, AddressRegion::Function, entry as u64, index as u64)
429     }
430 
get_function_from_address(&self, address: Address) -> Option<InterpreterFunctionRef<'a>>431     fn get_function_from_address(&self, address: Address) -> Option<InterpreterFunctionRef<'a>> {
432         let index = address.offset as u32;
433         if address.region != AddressRegion::Function {
434             return None;
435         }
436 
437         match AddressFunctionEntry::from(address.entry) {
438             AddressFunctionEntry::UserFunction => self
439                 .functions
440                 .get_by_index(FuncIndex::from_u32(index))
441                 .map(InterpreterFunctionRef::from),
442 
443             AddressFunctionEntry::LibCall => LibCall::all_libcalls()
444                 .get(index as usize)
445                 .copied()
446                 .map(InterpreterFunctionRef::from),
447         }
448     }
449 
450     /// Non-Recursively resolves a global value until its address is found
resolve_global_value(&self, gv: GlobalValue) -> Result<DataValue, MemoryError>451     fn resolve_global_value(&self, gv: GlobalValue) -> Result<DataValue, MemoryError> {
452         // Resolving a Global Value is a "pointer" chasing operation that lends itself to
453         // using a recursive solution. However, resolving this in a recursive manner
454         // is a bad idea because its very easy to add a bunch of global values and
455         // blow up the call stack.
456         //
457         // Adding to the challenges of this, is that the operations possible with GlobalValues
458         // mean that we cannot use a simple loop to resolve each global value, we must keep
459         // a pending list of operations.
460 
461         // These are the possible actions that we can perform
462         #[derive(Debug)]
463         enum ResolveAction {
464             Resolve(GlobalValue),
465             /// Perform an add on the current address
466             Add(DataValue),
467             /// Load From the current address and replace it with the loaded value
468             Load {
469                 /// Offset added to the base pointer before doing the load.
470                 offset: i32,
471 
472                 /// Type of the loaded value.
473                 global_type: Type,
474             },
475         }
476 
477         let func = self.get_current_function();
478 
479         // We start with a sentinel value that will fail if we try to load / add to it
480         // without resolving the base GV First.
481         let mut current_val = DataValue::I8(0);
482         let mut action_stack = vec![ResolveAction::Resolve(gv)];
483 
484         loop {
485             match action_stack.pop() {
486                 Some(ResolveAction::Resolve(gv)) => match func.global_values[gv] {
487                     GlobalValueData::VMContext => {
488                         // Fetch the VMContext value from the values of the first block in the function
489                         let index = func
490                             .signature
491                             .params
492                             .iter()
493                             .enumerate()
494                             .find(|(_, p)| p.purpose == ArgumentPurpose::VMContext)
495                             .map(|(i, _)| i)
496                             // This should be validated by the verifier
497                             .expect("No VMCtx argument was found, but one is referenced");
498 
499                         let first_block =
500                             func.layout.blocks().next().expect("to have a first block");
501                         let vmctx_value = func.dfg.block_params(first_block)[index];
502                         current_val = self.current_frame().get(vmctx_value).clone();
503                     }
504                     GlobalValueData::Load {
505                         base,
506                         offset,
507                         global_type,
508                         ..
509                     } => {
510                         action_stack.push(ResolveAction::Load {
511                             offset: offset.into(),
512                             global_type,
513                         });
514                         action_stack.push(ResolveAction::Resolve(base));
515                     }
516                     GlobalValueData::IAddImm {
517                         base,
518                         offset,
519                         global_type,
520                     } => {
521                         let offset: i64 = offset.into();
522                         let dv = DataValue::int(offset as i128, global_type)
523                             .map_err(|_| MemoryError::InvalidAddressType(global_type))?;
524                         action_stack.push(ResolveAction::Add(dv));
525                         action_stack.push(ResolveAction::Resolve(base));
526                     }
527                     GlobalValueData::Symbol { .. } => unimplemented!(),
528                     GlobalValueData::DynScaleTargetConst { .. } => unimplemented!(),
529                 },
530                 Some(ResolveAction::Add(dv)) => {
531                     current_val = current_val
532                         .add(dv.clone())
533                         .map_err(|_| MemoryError::InvalidAddress(dv))?;
534                 }
535                 Some(ResolveAction::Load {
536                     offset,
537                     global_type,
538                 }) => {
539                     let mut addr = Address::try_from(current_val)?;
540                     let mem_flags = MemFlags::trusted();
541                     // We can forego bounds checking here since its performed in `checked_load`
542                     addr.offset += offset as u64;
543                     current_val = self.checked_load(addr, global_type, mem_flags)?;
544                 }
545 
546                 // We are done resolving this, return the current value
547                 None => return Ok(current_val),
548             }
549         }
550     }
551 
get_pinned_reg(&self) -> DataValue552     fn get_pinned_reg(&self) -> DataValue {
553         self.pinned_reg.clone()
554     }
555 
set_pinned_reg(&mut self, v: DataValue)556     fn set_pinned_reg(&mut self, v: DataValue) {
557         self.pinned_reg = v;
558     }
559 }
560 
561 #[cfg(test)]
562 mod tests {
563     use super::*;
564     use crate::step::CraneliftTrap;
565     use cranelift_codegen::ir::TrapCode;
566     use cranelift_codegen::ir::immediates::Ieee32;
567     use cranelift_reader::parse_functions;
568     use smallvec::smallvec;
569 
570     // Most interpreter tests should use the more ergonomic `test interpret` filetest but this
571     // unit test serves as a sanity check that the interpreter still works without all of the
572     // filetest infrastructure.
573     #[test]
sanity()574     fn sanity() {
575         let code = "function %test() -> i8 {
576         block0:
577             v0 = iconst.i32 1
578             v1 = iadd_imm v0, 1
579             v2 = irsub_imm v1, 44  ; 44 - 2 == 42 (see irsub_imm's semantics)
580             v3 = icmp_imm eq v2, 42
581             return v3
582         }";
583 
584         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
585         let mut env = FunctionStore::default();
586         env.add(func.name.to_string(), &func);
587         let state = InterpreterState::default().with_function_store(env);
588         let result = Interpreter::new(state).call_by_name("%test", &[]).unwrap();
589 
590         assert_eq!(result, ControlFlow::Return(smallvec![DataValue::I8(1)]));
591     }
592 
593     // We don't have a way to check for traps with the current filetest infrastructure
594     #[test]
udiv_by_zero_traps()595     fn udiv_by_zero_traps() {
596         let code = "function %test() -> i32 {
597         block0:
598             v0 = iconst.i32 1
599             v1 = udiv_imm.i32 v0, 0
600             return v1
601         }";
602 
603         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
604         let mut env = FunctionStore::default();
605         env.add(func.name.to_string(), &func);
606         let state = InterpreterState::default().with_function_store(env);
607         let trap = Interpreter::new(state).call_by_name("%test", &[]).unwrap();
608 
609         assert_eq!(
610             trap,
611             ControlFlow::Trap(CraneliftTrap::User(TrapCode::INTEGER_DIVISION_BY_ZERO))
612         );
613     }
614 
615     #[test]
sdiv_min_by_neg_one_traps_with_overflow()616     fn sdiv_min_by_neg_one_traps_with_overflow() {
617         let code = "function %test() -> i8 {
618         block0:
619             v0 = iconst.i32 -2147483648
620             v1 = sdiv_imm.i32 v0, -1
621             return v1
622         }";
623 
624         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
625         let mut env = FunctionStore::default();
626         env.add(func.name.to_string(), &func);
627         let state = InterpreterState::default().with_function_store(env);
628         let result = Interpreter::new(state).call_by_name("%test", &[]).unwrap();
629 
630         match result {
631             ControlFlow::Trap(CraneliftTrap::User(TrapCode::INTEGER_OVERFLOW)) => {}
632             _ => panic!("Unexpected ControlFlow: {result:?}"),
633         }
634     }
635 
636     // This test verifies that functions can refer to each other using the function store. A double indirection is
637     // required, which is tricky to get right: a referenced function is a FuncRef when called but a FuncIndex inside the
638     // function store. This test would preferably be a CLIF filetest but the filetest infrastructure only looks at a
639     // single function at a time--we need more than one function in the store for this test.
640     #[test]
function_references()641     fn function_references() {
642         let code = "
643         function %child(i32) -> i32 {
644         block0(v0: i32):
645             v1 = iadd_imm v0, -1
646             return v1
647         }
648 
649         function %parent(i32) -> i32 {
650             fn42 = %child(i32) -> i32
651         block0(v0: i32):
652             v1 = iadd_imm v0, 1
653             v2 = call fn42(v1)
654             return v2
655         }";
656 
657         let mut env = FunctionStore::default();
658         let funcs = parse_functions(code).unwrap().to_vec();
659         funcs.iter().for_each(|f| env.add(f.name.to_string(), f));
660 
661         let state = InterpreterState::default().with_function_store(env);
662         let result = Interpreter::new(state)
663             .call_by_name("%parent", &[DataValue::I32(0)])
664             .unwrap();
665 
666         assert_eq!(result, ControlFlow::Return(smallvec![DataValue::I32(0)]));
667     }
668 
669     #[test]
fuel()670     fn fuel() {
671         let code = "function %test() -> i8 {
672         block0:
673             v0 = iconst.i32 1
674             v1 = iadd_imm v0, 1
675             return v1
676         }";
677 
678         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
679         let mut env = FunctionStore::default();
680         env.add(func.name.to_string(), &func);
681 
682         // The default interpreter should not enable the fuel mechanism
683         let state = InterpreterState::default().with_function_store(env.clone());
684         let result = Interpreter::new(state).call_by_name("%test", &[]).unwrap();
685 
686         assert_eq!(result, ControlFlow::Return(smallvec![DataValue::I32(2)]));
687 
688         // With 2 fuel, we should execute the iconst and iadd, but not the return thus giving a
689         // fuel exhausted error
690         let state = InterpreterState::default().with_function_store(env.clone());
691         let result = Interpreter::new(state)
692             .with_fuel(Some(2))
693             .call_by_name("%test", &[]);
694         match result {
695             Err(InterpreterError::FuelExhausted) => {}
696             _ => panic!("Expected Err(FuelExhausted), but got {result:?}"),
697         }
698 
699         // With 3 fuel, we should be able to execute the return instruction, and complete the test
700         let state = InterpreterState::default().with_function_store(env.clone());
701         let result = Interpreter::new(state)
702             .with_fuel(Some(3))
703             .call_by_name("%test", &[])
704             .unwrap();
705 
706         assert_eq!(result, ControlFlow::Return(smallvec![DataValue::I32(2)]));
707     }
708 
709     // Verifies that writing to the stack on a called function does not overwrite the parents
710     // stack slots.
711     #[test]
stack_slots_multi_functions()712     fn stack_slots_multi_functions() {
713         let code = "
714         function %callee(i64, i64) -> i64 {
715             ss0 = explicit_slot 8
716             ss1 = explicit_slot 8
717 
718         block0(v0: i64, v1: i64):
719             stack_store.i64 v0, ss0
720             stack_store.i64 v1, ss1
721             v2 = stack_load.i64 ss0
722             v3 = stack_load.i64 ss1
723             v4 = iadd.i64 v2, v3
724             return v4
725         }
726 
727         function %caller(i64, i64, i64, i64) -> i64 {
728             fn0 = %callee(i64, i64) -> i64
729             ss0 = explicit_slot 8
730             ss1 = explicit_slot 8
731 
732         block0(v0: i64, v1: i64, v2: i64, v3: i64):
733             stack_store.i64 v0, ss0
734             stack_store.i64 v1, ss1
735 
736             v4 = call fn0(v2, v3)
737 
738             v5 = stack_load.i64 ss0
739             v6 = stack_load.i64 ss1
740 
741             v7 = iadd.i64 v4, v5
742             v8 = iadd.i64 v7, v6
743 
744             return v8
745         }";
746 
747         let mut env = FunctionStore::default();
748         let funcs = parse_functions(code).unwrap().to_vec();
749         funcs.iter().for_each(|f| env.add(f.name.to_string(), f));
750 
751         let state = InterpreterState::default().with_function_store(env);
752         let result = Interpreter::new(state)
753             .call_by_name(
754                 "%caller",
755                 &[
756                     DataValue::I64(3),
757                     DataValue::I64(5),
758                     DataValue::I64(7),
759                     DataValue::I64(11),
760                 ],
761             )
762             .unwrap();
763 
764         assert_eq!(result, ControlFlow::Return(smallvec![DataValue::I64(26)]))
765     }
766 
767     #[test]
out_of_slot_write_traps()768     fn out_of_slot_write_traps() {
769         let code = "
770         function %stack_write() {
771             ss0 = explicit_slot 8
772 
773         block0:
774             v0 = iconst.i64 10
775             stack_store.i64 v0, ss0+8
776             return
777         }";
778 
779         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
780         let mut env = FunctionStore::default();
781         env.add(func.name.to_string(), &func);
782         let state = InterpreterState::default().with_function_store(env);
783         let trap = Interpreter::new(state)
784             .call_by_name("%stack_write", &[])
785             .unwrap();
786 
787         assert_eq!(
788             trap,
789             ControlFlow::Trap(CraneliftTrap::User(TrapCode::HEAP_OUT_OF_BOUNDS))
790         );
791     }
792 
793     #[test]
partial_out_of_slot_write_traps()794     fn partial_out_of_slot_write_traps() {
795         let code = "
796         function %stack_write() {
797             ss0 = explicit_slot 8
798 
799         block0:
800             v0 = iconst.i64 10
801             stack_store.i64 v0, ss0+4
802             return
803         }";
804 
805         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
806         let mut env = FunctionStore::default();
807         env.add(func.name.to_string(), &func);
808         let state = InterpreterState::default().with_function_store(env);
809         let trap = Interpreter::new(state)
810             .call_by_name("%stack_write", &[])
811             .unwrap();
812 
813         assert_eq!(
814             trap,
815             ControlFlow::Trap(CraneliftTrap::User(TrapCode::HEAP_OUT_OF_BOUNDS))
816         );
817     }
818 
819     #[test]
out_of_slot_read_traps()820     fn out_of_slot_read_traps() {
821         let code = "
822         function %stack_load() {
823             ss0 = explicit_slot 8
824 
825         block0:
826             v0 = stack_load.i64 ss0+8
827             return
828         }";
829 
830         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
831         let mut env = FunctionStore::default();
832         env.add(func.name.to_string(), &func);
833         let state = InterpreterState::default().with_function_store(env);
834         let trap = Interpreter::new(state)
835             .call_by_name("%stack_load", &[])
836             .unwrap();
837 
838         assert_eq!(
839             trap,
840             ControlFlow::Trap(CraneliftTrap::User(TrapCode::HEAP_OUT_OF_BOUNDS))
841         );
842     }
843 
844     #[test]
partial_out_of_slot_read_traps()845     fn partial_out_of_slot_read_traps() {
846         let code = "
847         function %stack_load() {
848             ss0 = explicit_slot 8
849 
850         block0:
851             v0 = stack_load.i64 ss0+4
852             return
853         }";
854 
855         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
856         let mut env = FunctionStore::default();
857         env.add(func.name.to_string(), &func);
858         let state = InterpreterState::default().with_function_store(env);
859         let trap = Interpreter::new(state)
860             .call_by_name("%stack_load", &[])
861             .unwrap();
862 
863         assert_eq!(
864             trap,
865             ControlFlow::Trap(CraneliftTrap::User(TrapCode::HEAP_OUT_OF_BOUNDS))
866         );
867     }
868 
869     #[test]
partial_out_of_slot_read_by_addr_traps()870     fn partial_out_of_slot_read_by_addr_traps() {
871         let code = "
872         function %stack_load() {
873             ss0 = explicit_slot 8
874 
875         block0:
876             v0 = stack_addr.i64 ss0
877             v1 = iconst.i64 4
878             v2 = iadd.i64 v0, v1
879             v3 = load.i64 v2
880             return
881         }";
882 
883         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
884         let mut env = FunctionStore::default();
885         env.add(func.name.to_string(), &func);
886         let state = InterpreterState::default().with_function_store(env);
887         let trap = Interpreter::new(state)
888             .call_by_name("%stack_load", &[])
889             .unwrap();
890 
891         assert_eq!(
892             trap,
893             ControlFlow::Trap(CraneliftTrap::User(TrapCode::HEAP_OUT_OF_BOUNDS))
894         );
895     }
896 
897     #[test]
partial_out_of_slot_write_by_addr_traps()898     fn partial_out_of_slot_write_by_addr_traps() {
899         let code = "
900         function %stack_store() {
901             ss0 = explicit_slot 8
902 
903         block0:
904             v0 = stack_addr.i64 ss0
905             v1 = iconst.i64 4
906             v2 = iadd.i64 v0, v1
907             store.i64 v1, v2
908             return
909         }";
910 
911         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
912         let mut env = FunctionStore::default();
913         env.add(func.name.to_string(), &func);
914         let state = InterpreterState::default().with_function_store(env);
915         let trap = Interpreter::new(state)
916             .call_by_name("%stack_store", &[])
917             .unwrap();
918 
919         assert_eq!(
920             trap,
921             ControlFlow::Trap(CraneliftTrap::User(TrapCode::HEAP_OUT_OF_BOUNDS))
922         );
923     }
924 
925     #[test]
srem_trap()926     fn srem_trap() {
927         let code = "function %test() -> i64 {
928         block0:
929             v0 = iconst.i64 0x8000_0000_0000_0000
930             v1 = iconst.i64 -1
931             v2 = srem.i64 v0, v1
932             return v2
933         }";
934 
935         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
936         let mut env = FunctionStore::default();
937         env.add(func.name.to_string(), &func);
938         let state = InterpreterState::default().with_function_store(env);
939         let trap = Interpreter::new(state).call_by_name("%test", &[]).unwrap();
940 
941         assert_eq!(
942             trap,
943             ControlFlow::Trap(CraneliftTrap::User(TrapCode::INTEGER_OVERFLOW))
944         );
945     }
946 
947     #[test]
libcall()948     fn libcall() {
949         let code = "function %test() -> i64 {
950             fn0 = colocated %CeilF32 (f32) -> f32 fast
951         block0:
952             v1 = f32const 0x0.5
953             v2 = call fn0(v1)
954             return v2
955         }";
956 
957         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
958         let mut env = FunctionStore::default();
959         env.add(func.name.to_string(), &func);
960         let state = InterpreterState::default()
961             .with_function_store(env)
962             .with_libcall_handler(|libcall, args| {
963                 Ok(smallvec![match (libcall, &args[..]) {
964                     (LibCall::CeilF32, [DataValue::F32(a)]) => DataValue::F32(a.ceil()),
965                     _ => panic!("Unexpected args"),
966                 }])
967             });
968 
969         let result = Interpreter::new(state).call_by_name("%test", &[]).unwrap();
970 
971         assert_eq!(
972             result,
973             ControlFlow::Return(smallvec![DataValue::F32(Ieee32::with_float(1.0))])
974         )
975     }
976 
977     #[test]
misaligned_store_traps()978     fn misaligned_store_traps() {
979         let code = "
980         function %test() {
981             ss0 = explicit_slot 16
982 
983         block0:
984             v0 = stack_addr.i64 ss0
985             v1 = iconst.i64 1
986             store.i64 aligned v1, v0+2
987             return
988         }";
989 
990         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
991         let mut env = FunctionStore::default();
992         env.add(func.name.to_string(), &func);
993         let state = InterpreterState::default().with_function_store(env);
994         let trap = Interpreter::new(state).call_by_name("%test", &[]).unwrap();
995 
996         assert_eq!(trap, ControlFlow::Trap(CraneliftTrap::HeapMisaligned));
997     }
998 
999     #[test]
misaligned_load_traps()1000     fn misaligned_load_traps() {
1001         let code = "
1002         function %test() {
1003             ss0 = explicit_slot 16
1004 
1005         block0:
1006             v0 = stack_addr.i64 ss0
1007             v1 = iconst.i64 1
1008             store.i64 aligned v1, v0
1009             v2 = load.i64 aligned v0+2
1010             return
1011         }";
1012 
1013         let func = parse_functions(code).unwrap().into_iter().next().unwrap();
1014         let mut env = FunctionStore::default();
1015         env.add(func.name.to_string(), &func);
1016         let state = InterpreterState::default().with_function_store(env);
1017         let trap = Interpreter::new(state).call_by_name("%test", &[]).unwrap();
1018 
1019         assert_eq!(trap, ControlFlow::Trap(CraneliftTrap::HeapMisaligned));
1020     }
1021 
1022     // When a trap occurs in a function called by another function, the trap was not being propagated
1023     // correctly. Instead the interpterer panicked with a invalid control flow state.
1024     // See this issue for more details: https://github.com/bytecodealliance/wasmtime/issues/6155
1025     #[test]
trap_across_call_propagates_correctly()1026     fn trap_across_call_propagates_correctly() {
1027         let code = "
1028         function %u2() -> f32 system_v {
1029             ss0 = explicit_slot 69
1030             ss1 = explicit_slot 69
1031             ss2 = explicit_slot 69
1032 
1033         block0:
1034             v0 = f32const -0x1.434342p-60
1035             v1 = stack_addr.i64 ss2+24
1036             store notrap aligned v0, v1
1037             return v0
1038         }
1039 
1040         function %u1() -> f32 system_v {
1041             sig0 = () -> f32 system_v
1042             fn0 = colocated %u2 sig0
1043 
1044         block0:
1045             v57 = call fn0()
1046             return v57
1047         }";
1048 
1049         let mut env = FunctionStore::default();
1050 
1051         let funcs = parse_functions(code).unwrap();
1052         for func in &funcs {
1053             env.add(func.name.to_string(), func);
1054         }
1055 
1056         let state = InterpreterState::default().with_function_store(env);
1057         let trap = Interpreter::new(state).call_by_name("%u1", &[]).unwrap();
1058 
1059         // Ensure that the correct trap was propagated.
1060         assert_eq!(trap, ControlFlow::Trap(CraneliftTrap::HeapMisaligned));
1061     }
1062 }
1063