cranelift_interpreter/
step.rs

1//! The [step] function interprets a single Cranelift instruction given its [State] and
2//! [InstructionContext].
3use crate::address::{Address, AddressSize};
4use crate::instruction::InstructionContext;
5use crate::state::{InterpreterFunctionRef, MemoryError, State};
6use crate::value::{DataValueExt, ValueConversionKind, ValueError, ValueResult};
7use cranelift_codegen::data_value::DataValue;
8use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
9use cranelift_codegen::ir::{
10    types, AbiParam, AtomicRmwOp, Block, BlockCall, Endianness, ExternalName, FuncRef, Function,
11    InstructionData, MemFlags, Opcode, TrapCode, Type, Value as ValueRef,
12};
13use log::trace;
14use smallvec::{smallvec, SmallVec};
15use std::fmt::Debug;
16use std::ops::RangeFrom;
17use thiserror::Error;
18
19/// Ensures that all types in args are the same as expected by the signature
20fn validate_signature_params(sig: &[AbiParam], args: &[DataValue]) -> bool {
21    args.iter()
22        .map(|r| r.ty())
23        .zip(sig.iter().map(|r| r.value_type))
24        .all(|(a, b)| match (a, b) {
25            // For these two cases we don't have precise type information for `a`.
26            // We don't distinguish between different bool types, or different vector types
27            // The actual error is in `Value::ty` that returns default types for some values
28            // but we don't have enough information there either.
29            //
30            // Ideally the user has run the verifier and caught this properly...
31            (a, b) if a.is_vector() && b.is_vector() => true,
32            (a, b) => a == b,
33        })
34}
35
36// Helper for summing a sequence of values.
37fn sum_unsigned(head: DataValue, tail: SmallVec<[DataValue; 1]>) -> ValueResult<u128> {
38    let mut acc = head;
39    for t in tail {
40        acc = DataValueExt::add(acc, t)?;
41    }
42    acc.into_int_unsigned()
43}
44
45/// Interpret a single Cranelift instruction. Note that program traps and interpreter errors are
46/// distinct: a program trap results in `Ok(Flow::Trap(...))` whereas an interpretation error (e.g.
47/// the types of two values are incompatible) results in `Err(...)`.
48#[allow(unused_variables)]
49pub fn step<'a, I>(state: &mut dyn State<'a>, inst_context: I) -> Result<ControlFlow<'a>, StepError>
50where
51    I: InstructionContext,
52{
53    let inst = inst_context.data();
54    let ctrl_ty = inst_context.controlling_type().unwrap();
55    trace!(
56        "Step: {}{}",
57        inst.opcode(),
58        if ctrl_ty.is_invalid() {
59            String::new()
60        } else {
61            format!(".{ctrl_ty}")
62        }
63    );
64
65    // The following closures make the `step` implementation much easier to express. Note that they
66    // frequently close over the `state` or `inst_context` for brevity.
67
68    // Retrieve the current value for an instruction argument.
69    let arg = |index: usize| -> DataValue {
70        let value_ref = inst_context.args()[index];
71        state.current_frame().get(value_ref).clone()
72    };
73
74    // Retrieve the current values for all of an instruction's arguments.
75    let args = || -> SmallVec<[DataValue; 1]> { state.collect_values(inst_context.args()) };
76
77    // Retrieve the current values for a range of an instruction's arguments.
78    let args_range = |indexes: RangeFrom<usize>| -> Result<SmallVec<[DataValue; 1]>, StepError> {
79        Ok(SmallVec::<[DataValue; 1]>::from(&args()[indexes]))
80    };
81
82    // Retrieve the immediate value for an instruction, expecting it to exist.
83    let imm = || -> DataValue {
84        DataValue::from(match inst {
85            InstructionData::UnaryConst {
86                constant_handle,
87                opcode,
88            } => {
89                let buffer = state
90                    .get_current_function()
91                    .dfg
92                    .constants
93                    .get(constant_handle);
94                match (ctrl_ty.bytes(), opcode) {
95                    (_, Opcode::F128const) => DataValue::F128(buffer.try_into().expect("a 16-byte data buffer")),
96                    (16, Opcode::Vconst) => DataValue::V128(buffer.as_slice().try_into().expect("a 16-byte data buffer")),
97                    (8, Opcode::Vconst) => DataValue::V64(buffer.as_slice().try_into().expect("an 8-byte data buffer")),
98                    (length, opcode) => panic!("unexpected UnaryConst controlling type size {length} for opcode {opcode:?}"),
99                }
100            }
101            InstructionData::Shuffle { imm, .. } => {
102                let mask = state
103                    .get_current_function()
104                    .dfg
105                    .immediates
106                    .get(imm)
107                    .unwrap()
108                    .as_slice();
109                match mask.len() {
110                    16 => DataValue::V128(mask.try_into().expect("a 16-byte vector mask")),
111                    8 => DataValue::V64(mask.try_into().expect("an 8-byte vector mask")),
112                    length => panic!("unexpected Shuffle mask length {}", mask.len()),
113                }
114            }
115            // 8-bit.
116            InstructionData::BinaryImm8 { imm, .. } | InstructionData::TernaryImm8 { imm, .. } => {
117                DataValue::from(imm as i8) // Note the switch from unsigned to signed.
118            }
119            // 16-bit
120            InstructionData::UnaryIeee16 { imm, .. } => DataValue::from(imm),
121            // 32-bit
122            InstructionData::UnaryIeee32 { imm, .. } => DataValue::from(imm),
123            InstructionData::Load { offset, .. }
124            | InstructionData::Store { offset, .. }
125            | InstructionData::StackLoad { offset, .. }
126            | InstructionData::StackStore { offset, .. } => DataValue::from(offset),
127            // 64-bit.
128            InstructionData::UnaryImm { imm, .. }
129            | InstructionData::BinaryImm64 { imm, .. }
130            | InstructionData::IntCompareImm { imm, .. } => DataValue::from(imm.bits()),
131            InstructionData::UnaryIeee64 { imm, .. } => DataValue::from(imm),
132            _ => unreachable!(),
133        })
134    };
135
136    // Retrieve the immediate value for an instruction and convert it to the controlling type of the
137    // instruction. For example, since `InstructionData` stores all integer immediates in a 64-bit
138    // size, this will attempt to convert `iconst.i8 ...` to an 8-bit size.
139    let imm_as_ctrl_ty = || -> Result<DataValue, ValueError> {
140        DataValue::convert(imm(), ValueConversionKind::Exact(ctrl_ty))
141    };
142
143    // Indicate that the result of a step is to assign a single value to an instruction's results.
144    let assign = |value: DataValue| ControlFlow::Assign(smallvec![value]);
145
146    // Indicate that the result of a step is to assign multiple values to an instruction's results.
147    let assign_multiple = |values: &[DataValue]| ControlFlow::Assign(SmallVec::from(values));
148
149    // Similar to `assign` but converts some errors into traps
150    let assign_or_trap = |value: ValueResult<DataValue>| match value {
151        Ok(v) => Ok(assign(v)),
152        Err(ValueError::IntegerDivisionByZero) => Ok(ControlFlow::Trap(CraneliftTrap::User(
153            TrapCode::INTEGER_DIVISION_BY_ZERO,
154        ))),
155        Err(ValueError::IntegerOverflow) => Ok(ControlFlow::Trap(CraneliftTrap::User(
156            TrapCode::INTEGER_OVERFLOW,
157        ))),
158        Err(e) => Err(e),
159    };
160
161    let memerror_to_trap = |e: MemoryError| match e {
162        MemoryError::InvalidAddress(_)
163        | MemoryError::InvalidAddressType(_)
164        | MemoryError::InvalidOffset { .. }
165        | MemoryError::InvalidEntry { .. } => CraneliftTrap::User(TrapCode::HEAP_OUT_OF_BOUNDS),
166        MemoryError::OutOfBoundsStore { mem_flags, .. }
167        | MemoryError::OutOfBoundsLoad { mem_flags, .. } => CraneliftTrap::User(
168            mem_flags
169                .trap_code()
170                .expect("op with notrap flag should not trap"),
171        ),
172        MemoryError::MisalignedLoad { .. } => CraneliftTrap::HeapMisaligned,
173        MemoryError::MisalignedStore { .. } => CraneliftTrap::HeapMisaligned,
174    };
175
176    // Assigns or traps depending on the value of the result
177    let assign_or_memtrap = |res| match res {
178        Ok(v) => assign(v),
179        Err(e) => ControlFlow::Trap(memerror_to_trap(e)),
180    };
181
182    // Continues or traps depending on the value of the result
183    let continue_or_memtrap = |res| match res {
184        Ok(_) => ControlFlow::Continue,
185        Err(e) => ControlFlow::Trap(memerror_to_trap(e)),
186    };
187
188    let calculate_addr =
189        |addr_ty: Type, imm: DataValue, args: SmallVec<[DataValue; 1]>| -> ValueResult<u64> {
190            let imm = imm.convert(ValueConversionKind::ZeroExtend(addr_ty))?;
191            let args = args
192                .into_iter()
193                .map(|v| v.convert(ValueConversionKind::ZeroExtend(addr_ty)))
194                .collect::<ValueResult<SmallVec<[DataValue; 1]>>>()?;
195
196            Ok(sum_unsigned(imm, args)? as u64)
197        };
198
199    // Interpret a unary instruction with the given `op`, assigning the resulting value to the
200    // instruction's results.
201    let unary =
202        |op: fn(DataValue) -> ValueResult<DataValue>, arg: DataValue| -> ValueResult<ControlFlow> {
203            let ctrl_ty = inst_context.controlling_type().unwrap();
204            let res = unary_arith(arg, ctrl_ty, op)?;
205            Ok(assign(res))
206        };
207
208    // Interpret a binary instruction with the given `op`, assigning the resulting value to the
209    // instruction's results.
210    let binary = |op: fn(DataValue, DataValue) -> ValueResult<DataValue>,
211                  left: DataValue,
212                  right: DataValue|
213     -> ValueResult<ControlFlow> {
214        let ctrl_ty = inst_context.controlling_type().unwrap();
215        let res = binary_arith(left, right, ctrl_ty, op)?;
216        Ok(assign(res))
217    };
218
219    // Similar to `binary` but converts select `ValueError`'s into trap `ControlFlow`'s
220    let binary_can_trap = |op: fn(DataValue, DataValue) -> ValueResult<DataValue>,
221                           left: DataValue,
222                           right: DataValue|
223     -> ValueResult<ControlFlow> {
224        let ctrl_ty = inst_context.controlling_type().unwrap();
225        let res = binary_arith(left, right, ctrl_ty, op);
226        assign_or_trap(res)
227    };
228
229    // Choose whether to assign `left` or `right` to the instruction's result based on a `condition`.
230    let choose = |condition: bool, left: DataValue, right: DataValue| -> ControlFlow {
231        assign(if condition { left } else { right })
232    };
233
234    // Retrieve an instruction's branch destination; expects the instruction to be a branch.
235
236    let continue_at = |block: BlockCall| {
237        let branch_args =
238            state.collect_values(block.args_slice(&state.get_current_function().dfg.value_lists));
239        Ok(ControlFlow::ContinueAt(
240            block.block(&state.get_current_function().dfg.value_lists),
241            branch_args,
242        ))
243    };
244
245    // Based on `condition`, indicate where to continue the control flow.
246    let branch_when = |condition: bool, block| -> Result<ControlFlow, StepError> {
247        if condition {
248            continue_at(block)
249        } else {
250            Ok(ControlFlow::Continue)
251        }
252    };
253
254    // Retrieve an instruction's trap code; expects the instruction to be a trap.
255    let trap_code = || -> TrapCode { inst.trap_code().unwrap() };
256
257    // Based on `condition`, either trap or not.
258    let trap_when = |condition: bool, trap: CraneliftTrap| -> ControlFlow {
259        if condition {
260            ControlFlow::Trap(trap)
261        } else {
262            ControlFlow::Continue
263        }
264    };
265
266    // Calls a function reference with the given arguments.
267    let call_func =
268        |func_ref: InterpreterFunctionRef<'a>,
269         args: SmallVec<[DataValue; 1]>,
270         make_ctrl_flow: fn(&'a Function, SmallVec<[DataValue; 1]>) -> ControlFlow<'a>|
271         -> Result<ControlFlow<'a>, StepError> {
272            let signature = func_ref.signature();
273
274            // Check the types of the arguments. This is usually done by the verifier, but nothing
275            // guarantees that the user has ran that.
276            let args_match = validate_signature_params(&signature.params[..], &args[..]);
277            if !args_match {
278                return Ok(ControlFlow::Trap(CraneliftTrap::BadSignature));
279            }
280
281            Ok(match func_ref {
282                InterpreterFunctionRef::Function(func) => make_ctrl_flow(func, args),
283                InterpreterFunctionRef::LibCall(libcall) => {
284                    debug_assert!(
285                        !matches!(
286                            inst.opcode(),
287                            Opcode::ReturnCall | Opcode::ReturnCallIndirect,
288                        ),
289                        "Cannot tail call to libcalls"
290                    );
291                    let libcall_handler = state.get_libcall_handler();
292
293                    // We don't transfer control to a libcall, we just execute it and return the results
294                    let res = libcall_handler(libcall, args);
295                    let res = match res {
296                        Err(trap) => return Ok(ControlFlow::Trap(trap)),
297                        Ok(rets) => rets,
298                    };
299
300                    // Check that what the handler returned is what we expect.
301                    if validate_signature_params(&signature.returns[..], &res[..]) {
302                        ControlFlow::Assign(res)
303                    } else {
304                        ControlFlow::Trap(CraneliftTrap::BadSignature)
305                    }
306                }
307            })
308        };
309
310    // Interpret a Cranelift instruction.
311    Ok(match inst.opcode() {
312        Opcode::Jump => {
313            if let InstructionData::Jump { destination, .. } = inst {
314                continue_at(destination)?
315            } else {
316                unreachable!()
317            }
318        }
319        Opcode::Brif => {
320            if let InstructionData::Brif {
321                arg,
322                blocks: [block_then, block_else],
323                ..
324            } = inst
325            {
326                let arg = state.current_frame().get(arg).clone();
327
328                let condition = arg.convert(ValueConversionKind::ToBoolean)?.into_bool()?;
329
330                if condition {
331                    continue_at(block_then)?
332                } else {
333                    continue_at(block_else)?
334                }
335            } else {
336                unreachable!()
337            }
338        }
339        Opcode::BrTable => {
340            if let InstructionData::BranchTable { table, .. } = inst {
341                let jt_data = &state.get_current_function().stencil.dfg.jump_tables[table];
342
343                // Convert to usize to remove negative indexes from the following operations
344                let jump_target = usize::try_from(arg(0).into_int_unsigned()?)
345                    .ok()
346                    .and_then(|i| jt_data.as_slice().get(i))
347                    .copied()
348                    .unwrap_or(jt_data.default_block());
349
350                continue_at(jump_target)?
351            } else {
352                unreachable!()
353            }
354        }
355        Opcode::Trap => ControlFlow::Trap(CraneliftTrap::User(trap_code())),
356        Opcode::Debugtrap => ControlFlow::Trap(CraneliftTrap::Debug),
357        Opcode::Trapz => trap_when(!arg(0).into_bool()?, CraneliftTrap::User(trap_code())),
358        Opcode::Trapnz => trap_when(arg(0).into_bool()?, CraneliftTrap::User(trap_code())),
359        Opcode::Return => ControlFlow::Return(args()),
360        Opcode::Call | Opcode::ReturnCall => {
361            let func_ref = if let InstructionData::Call { func_ref, .. } = inst {
362                func_ref
363            } else {
364                unreachable!()
365            };
366
367            let curr_func = state.get_current_function();
368            let ext_data = curr_func
369                .dfg
370                .ext_funcs
371                .get(func_ref)
372                .ok_or(StepError::UnknownFunction(func_ref))?;
373
374            let args = args();
375            let func = match ext_data.name {
376                // These functions should be registered in the regular function store
377                ExternalName::User(_) | ExternalName::TestCase(_) => {
378                    let function = state
379                        .get_function(func_ref)
380                        .ok_or(StepError::UnknownFunction(func_ref))?;
381                    InterpreterFunctionRef::Function(function)
382                }
383                ExternalName::LibCall(libcall) => InterpreterFunctionRef::LibCall(libcall),
384                ExternalName::KnownSymbol(_) => unimplemented!(),
385            };
386
387            let make_control_flow = match inst.opcode() {
388                Opcode::Call => ControlFlow::Call,
389                Opcode::ReturnCall => ControlFlow::ReturnCall,
390                _ => unreachable!(),
391            };
392
393            call_func(func, args, make_control_flow)?
394        }
395        Opcode::CallIndirect | Opcode::ReturnCallIndirect => {
396            let args = args();
397            let addr_dv = DataValue::I64(arg(0).into_int_unsigned()? as i64);
398            let addr = Address::try_from(addr_dv.clone()).map_err(StepError::MemoryError)?;
399
400            let func = state
401                .get_function_from_address(addr)
402                .ok_or_else(|| StepError::MemoryError(MemoryError::InvalidAddress(addr_dv)))?;
403
404            let call_args: SmallVec<[DataValue; 1]> = SmallVec::from(&args[1..]);
405
406            let make_control_flow = match inst.opcode() {
407                Opcode::CallIndirect => ControlFlow::Call,
408                Opcode::ReturnCallIndirect => ControlFlow::ReturnCall,
409                _ => unreachable!(),
410            };
411
412            call_func(func, call_args, make_control_flow)?
413        }
414        Opcode::FuncAddr => {
415            let func_ref = if let InstructionData::FuncAddr { func_ref, .. } = inst {
416                func_ref
417            } else {
418                unreachable!()
419            };
420
421            let ext_data = state
422                .get_current_function()
423                .dfg
424                .ext_funcs
425                .get(func_ref)
426                .ok_or(StepError::UnknownFunction(func_ref))?;
427
428            let addr_ty = inst_context.controlling_type().unwrap();
429            assign_or_memtrap({
430                AddressSize::try_from(addr_ty).and_then(|addr_size| {
431                    let addr = state.function_address(addr_size, &ext_data.name)?;
432                    let dv = DataValue::try_from(addr)?;
433                    Ok(dv.into())
434                })
435            })
436        }
437        Opcode::Load
438        | Opcode::Uload8
439        | Opcode::Sload8
440        | Opcode::Uload16
441        | Opcode::Sload16
442        | Opcode::Uload32
443        | Opcode::Sload32
444        | Opcode::Uload8x8
445        | Opcode::Sload8x8
446        | Opcode::Uload16x4
447        | Opcode::Sload16x4
448        | Opcode::Uload32x2
449        | Opcode::Sload32x2 => {
450            let ctrl_ty = inst_context.controlling_type().unwrap();
451            let (load_ty, kind) = match inst.opcode() {
452                Opcode::Load => (ctrl_ty, None),
453                Opcode::Uload8 => (types::I8, Some(ValueConversionKind::ZeroExtend(ctrl_ty))),
454                Opcode::Sload8 => (types::I8, Some(ValueConversionKind::SignExtend(ctrl_ty))),
455                Opcode::Uload16 => (types::I16, Some(ValueConversionKind::ZeroExtend(ctrl_ty))),
456                Opcode::Sload16 => (types::I16, Some(ValueConversionKind::SignExtend(ctrl_ty))),
457                Opcode::Uload32 => (types::I32, Some(ValueConversionKind::ZeroExtend(ctrl_ty))),
458                Opcode::Sload32 => (types::I32, Some(ValueConversionKind::SignExtend(ctrl_ty))),
459                Opcode::Uload8x8
460                | Opcode::Sload8x8
461                | Opcode::Uload16x4
462                | Opcode::Sload16x4
463                | Opcode::Uload32x2
464                | Opcode::Sload32x2 => unimplemented!(),
465                _ => unreachable!(),
466            };
467
468            let addr_value = calculate_addr(types::I64, imm(), args())?;
469            let mem_flags = inst.memflags().expect("instruction to have memory flags");
470            let loaded = assign_or_memtrap(
471                Address::try_from(addr_value)
472                    .and_then(|addr| state.checked_load(addr, load_ty, mem_flags)),
473            );
474
475            match (loaded, kind) {
476                (ControlFlow::Assign(ret), Some(c)) => ControlFlow::Assign(
477                    ret.into_iter()
478                        .map(|loaded| loaded.convert(c.clone()))
479                        .collect::<ValueResult<SmallVec<[DataValue; 1]>>>()?,
480                ),
481                (cf, _) => cf,
482            }
483        }
484        Opcode::Store | Opcode::Istore8 | Opcode::Istore16 | Opcode::Istore32 => {
485            let kind = match inst.opcode() {
486                Opcode::Store => None,
487                Opcode::Istore8 => Some(ValueConversionKind::Truncate(types::I8)),
488                Opcode::Istore16 => Some(ValueConversionKind::Truncate(types::I16)),
489                Opcode::Istore32 => Some(ValueConversionKind::Truncate(types::I32)),
490                _ => unreachable!(),
491            };
492
493            let addr_value = calculate_addr(types::I64, imm(), args_range(1..)?)?;
494            let mem_flags = inst.memflags().expect("instruction to have memory flags");
495            let reduced = if let Some(c) = kind {
496                arg(0).convert(c)?
497            } else {
498                arg(0)
499            };
500            continue_or_memtrap(
501                Address::try_from(addr_value)
502                    .and_then(|addr| state.checked_store(addr, reduced, mem_flags)),
503            )
504        }
505        Opcode::StackLoad => {
506            let load_ty = inst_context.controlling_type().unwrap();
507            let slot = inst.stack_slot().unwrap();
508            let offset = sum_unsigned(imm(), args())? as u64;
509            let mem_flags = MemFlags::new();
510            assign_or_memtrap({
511                state
512                    .stack_address(AddressSize::_64, slot, offset)
513                    .and_then(|addr| state.checked_load(addr, load_ty, mem_flags))
514            })
515        }
516        Opcode::StackStore => {
517            let arg = arg(0);
518            let slot = inst.stack_slot().unwrap();
519            let offset = sum_unsigned(imm(), args_range(1..)?)? as u64;
520            let mem_flags = MemFlags::new();
521            continue_or_memtrap({
522                state
523                    .stack_address(AddressSize::_64, slot, offset)
524                    .and_then(|addr| state.checked_store(addr, arg, mem_flags))
525            })
526        }
527        Opcode::StackAddr => {
528            let load_ty = inst_context.controlling_type().unwrap();
529            let slot = inst.stack_slot().unwrap();
530            let offset = sum_unsigned(imm(), args())? as u64;
531            assign_or_memtrap({
532                AddressSize::try_from(load_ty).and_then(|addr_size| {
533                    let addr = state.stack_address(addr_size, slot, offset)?;
534                    let dv = DataValue::try_from(addr)?;
535                    Ok(dv.into())
536                })
537            })
538        }
539        Opcode::DynamicStackAddr => unimplemented!("DynamicStackSlot"),
540        Opcode::DynamicStackLoad => unimplemented!("DynamicStackLoad"),
541        Opcode::DynamicStackStore => unimplemented!("DynamicStackStore"),
542        Opcode::GlobalValue | Opcode::SymbolValue | Opcode::TlsValue => {
543            if let InstructionData::UnaryGlobalValue { global_value, .. } = inst {
544                assign_or_memtrap(state.resolve_global_value(global_value))
545            } else {
546                unreachable!()
547            }
548        }
549        Opcode::GetPinnedReg => assign(state.get_pinned_reg()),
550        Opcode::SetPinnedReg => {
551            let arg0 = arg(0);
552            state.set_pinned_reg(arg0);
553            ControlFlow::Continue
554        }
555        Opcode::Iconst => assign(DataValueExt::int(imm().into_int_signed()?, ctrl_ty)?),
556        Opcode::F16const => assign(imm()),
557        Opcode::F32const => assign(imm()),
558        Opcode::F64const => assign(imm()),
559        Opcode::F128const => assign(imm()),
560        Opcode::Vconst => assign(imm()),
561        Opcode::Nop => ControlFlow::Continue,
562        Opcode::Select | Opcode::SelectSpectreGuard => choose(arg(0).into_bool()?, arg(1), arg(2)),
563        Opcode::Bitselect => assign(bitselect(arg(0), arg(1), arg(2))?),
564        Opcode::Icmp => assign(icmp(ctrl_ty, inst.cond_code().unwrap(), &arg(0), &arg(1))?),
565        Opcode::IcmpImm => assign(icmp(
566            ctrl_ty,
567            inst.cond_code().unwrap(),
568            &arg(0),
569            &imm_as_ctrl_ty()?,
570        )?),
571        Opcode::Smin => {
572            if ctrl_ty.is_vector() {
573                let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(1), &arg(0))?;
574                assign(bitselect(icmp, arg(0), arg(1))?)
575            } else {
576                assign(arg(0).smin(arg(1))?)
577            }
578        }
579        Opcode::Umin => {
580            if ctrl_ty.is_vector() {
581                let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(1), &arg(0))?;
582                assign(bitselect(icmp, arg(0), arg(1))?)
583            } else {
584                assign(arg(0).umin(arg(1))?)
585            }
586        }
587        Opcode::Smax => {
588            if ctrl_ty.is_vector() {
589                let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(0), &arg(1))?;
590                assign(bitselect(icmp, arg(0), arg(1))?)
591            } else {
592                assign(arg(0).smax(arg(1))?)
593            }
594        }
595        Opcode::Umax => {
596            if ctrl_ty.is_vector() {
597                let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(0), &arg(1))?;
598                assign(bitselect(icmp, arg(0), arg(1))?)
599            } else {
600                assign(arg(0).umax(arg(1))?)
601            }
602        }
603        Opcode::AvgRound => {
604            let sum = DataValueExt::add(arg(0), arg(1))?;
605            let one = DataValueExt::int(1, arg(0).ty())?;
606            let inc = DataValueExt::add(sum, one)?;
607            let two = DataValueExt::int(2, arg(0).ty())?;
608            binary(DataValueExt::udiv, inc, two)?
609        }
610        Opcode::Iadd => binary(DataValueExt::add, arg(0), arg(1))?,
611        Opcode::UaddSat => assign(binary_arith(
612            arg(0),
613            arg(1),
614            ctrl_ty,
615            DataValueExt::uadd_sat,
616        )?),
617        Opcode::SaddSat => assign(binary_arith(
618            arg(0),
619            arg(1),
620            ctrl_ty,
621            DataValueExt::sadd_sat,
622        )?),
623        Opcode::Isub => binary(DataValueExt::sub, arg(0), arg(1))?,
624        Opcode::UsubSat => assign(binary_arith(
625            arg(0),
626            arg(1),
627            ctrl_ty,
628            DataValueExt::usub_sat,
629        )?),
630        Opcode::SsubSat => assign(binary_arith(
631            arg(0),
632            arg(1),
633            ctrl_ty,
634            DataValueExt::ssub_sat,
635        )?),
636        Opcode::Ineg => binary(DataValueExt::sub, DataValueExt::int(0, ctrl_ty)?, arg(0))?,
637        Opcode::Iabs => {
638            let (min_val, _) = ctrl_ty.lane_type().bounds(true);
639            let min_val: DataValue = DataValueExt::int(min_val as i128, ctrl_ty.lane_type())?;
640            let arg0 = extractlanes(&arg(0), ctrl_ty)?;
641            let new_vec = arg0
642                .into_iter()
643                .map(|lane| {
644                    if lane == min_val {
645                        Ok(min_val.clone())
646                    } else {
647                        DataValueExt::int(lane.into_int_signed()?.abs(), ctrl_ty.lane_type())
648                    }
649                })
650                .collect::<ValueResult<SimdVec<DataValue>>>()?;
651            assign(vectorizelanes(&new_vec, ctrl_ty)?)
652        }
653        Opcode::Imul => binary(DataValueExt::mul, arg(0), arg(1))?,
654        Opcode::Umulhi | Opcode::Smulhi => {
655            let double_length = match ctrl_ty.lane_bits() {
656                8 => types::I16,
657                16 => types::I32,
658                32 => types::I64,
659                64 => types::I128,
660                _ => unimplemented!("Unsupported integer length {}", ctrl_ty.bits()),
661            };
662            let conv_type = if inst.opcode() == Opcode::Umulhi {
663                ValueConversionKind::ZeroExtend(double_length)
664            } else {
665                ValueConversionKind::SignExtend(double_length)
666            };
667            let arg0 = extractlanes(&arg(0), ctrl_ty)?;
668            let arg1 = extractlanes(&arg(1), ctrl_ty)?;
669
670            let res = arg0
671                .into_iter()
672                .zip(arg1)
673                .map(|(x, y)| {
674                    let x = x.convert(conv_type.clone())?;
675                    let y = y.convert(conv_type.clone())?;
676
677                    Ok(DataValueExt::mul(x, y)?
678                        .convert(ValueConversionKind::ExtractUpper(ctrl_ty.lane_type()))?)
679                })
680                .collect::<ValueResult<SimdVec<DataValue>>>()?;
681
682            assign(vectorizelanes(&res, ctrl_ty)?)
683        }
684        Opcode::Udiv => binary_can_trap(DataValueExt::udiv, arg(0), arg(1))?,
685        Opcode::Sdiv => binary_can_trap(DataValueExt::sdiv, arg(0), arg(1))?,
686        Opcode::Urem => binary_can_trap(DataValueExt::urem, arg(0), arg(1))?,
687        Opcode::Srem => binary_can_trap(DataValueExt::srem, arg(0), arg(1))?,
688        Opcode::IaddImm => binary(DataValueExt::add, arg(0), imm_as_ctrl_ty()?)?,
689        Opcode::ImulImm => binary(DataValueExt::mul, arg(0), imm_as_ctrl_ty()?)?,
690        Opcode::UdivImm => binary_can_trap(DataValueExt::udiv, arg(0), imm_as_ctrl_ty()?)?,
691        Opcode::SdivImm => binary_can_trap(DataValueExt::sdiv, arg(0), imm_as_ctrl_ty()?)?,
692        Opcode::UremImm => binary_can_trap(DataValueExt::urem, arg(0), imm_as_ctrl_ty()?)?,
693        Opcode::SremImm => binary_can_trap(DataValueExt::srem, arg(0), imm_as_ctrl_ty()?)?,
694        Opcode::IrsubImm => binary(DataValueExt::sub, imm_as_ctrl_ty()?, arg(0))?,
695        Opcode::UaddOverflow => {
696            let (sum, carry) = arg(0).uadd_overflow(arg(1))?;
697            assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
698        }
699        Opcode::SaddOverflow => {
700            let (sum, carry) = arg(0).sadd_overflow(arg(1))?;
701            assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
702        }
703        Opcode::UsubOverflow => {
704            let (sum, carry) = arg(0).usub_overflow(arg(1))?;
705            assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
706        }
707        Opcode::SsubOverflow => {
708            let (sum, carry) = arg(0).ssub_overflow(arg(1))?;
709            assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
710        }
711        Opcode::UmulOverflow => {
712            let (sum, carry) = arg(0).umul_overflow(arg(1))?;
713            assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
714        }
715        Opcode::SmulOverflow => {
716            let (sum, carry) = arg(0).smul_overflow(arg(1))?;
717            assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
718        }
719        Opcode::SaddOverflowCin => {
720            let (mut sum, mut carry) = arg(0).sadd_overflow(arg(1))?;
721
722            if DataValueExt::into_bool(arg(2))? {
723                let (sum2, carry2) = sum.sadd_overflow(DataValueExt::int(1, ctrl_ty)?)?;
724                carry |= carry2;
725                sum = sum2;
726            }
727
728            assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
729        }
730        Opcode::UaddOverflowCin => {
731            let (mut sum, mut carry) = arg(0).uadd_overflow(arg(1))?;
732
733            if DataValueExt::into_bool(arg(2))? {
734                let (sum2, carry2) = sum.uadd_overflow(DataValueExt::int(1, ctrl_ty)?)?;
735                carry |= carry2;
736                sum = sum2;
737            }
738
739            assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
740        }
741        Opcode::UaddOverflowTrap => {
742            let sum = DataValueExt::add(arg(0), arg(1))?;
743            let carry = sum < arg(0) && sum < arg(1);
744            if carry {
745                ControlFlow::Trap(CraneliftTrap::User(trap_code()))
746            } else {
747                assign(sum)
748            }
749        }
750        Opcode::SsubOverflowBin => {
751            let (mut sub, mut carry) = arg(0).ssub_overflow(arg(1))?;
752
753            if DataValueExt::into_bool(arg(2))? {
754                let (sub2, carry2) = sub.ssub_overflow(DataValueExt::int(1, ctrl_ty)?)?;
755                carry |= carry2;
756                sub = sub2;
757            }
758
759            assign_multiple(&[sub, DataValueExt::bool(carry, false, types::I8)?])
760        }
761        Opcode::UsubOverflowBin => {
762            let (mut sub, mut carry) = arg(0).usub_overflow(arg(1))?;
763
764            if DataValueExt::into_bool(arg(2))? {
765                let (sub2, carry2) = sub.usub_overflow(DataValueExt::int(1, ctrl_ty)?)?;
766                carry |= carry2;
767                sub = sub2;
768            }
769
770            assign_multiple(&[sub, DataValueExt::bool(carry, false, types::I8)?])
771        }
772        Opcode::Band => binary(DataValueExt::and, arg(0), arg(1))?,
773        Opcode::Bor => binary(DataValueExt::or, arg(0), arg(1))?,
774        Opcode::Bxor => binary(DataValueExt::xor, arg(0), arg(1))?,
775        Opcode::Bnot => unary(DataValueExt::not, arg(0))?,
776        Opcode::BandNot => binary(DataValueExt::and, arg(0), DataValueExt::not(arg(1))?)?,
777        Opcode::BorNot => binary(DataValueExt::or, arg(0), DataValueExt::not(arg(1))?)?,
778        Opcode::BxorNot => binary(DataValueExt::xor, arg(0), DataValueExt::not(arg(1))?)?,
779        Opcode::BandImm => binary(DataValueExt::and, arg(0), imm_as_ctrl_ty()?)?,
780        Opcode::BorImm => binary(DataValueExt::or, arg(0), imm_as_ctrl_ty()?)?,
781        Opcode::BxorImm => binary(DataValueExt::xor, arg(0), imm_as_ctrl_ty()?)?,
782        Opcode::Rotl => binary(DataValueExt::rotl, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
783        Opcode::Rotr => binary(DataValueExt::rotr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
784        Opcode::RotlImm => binary(DataValueExt::rotl, arg(0), shift_amt(ctrl_ty, imm())?)?,
785        Opcode::RotrImm => binary(DataValueExt::rotr, arg(0), shift_amt(ctrl_ty, imm())?)?,
786        Opcode::Ishl => binary(DataValueExt::shl, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
787        Opcode::Ushr => binary(DataValueExt::ushr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
788        Opcode::Sshr => binary(DataValueExt::sshr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
789        Opcode::IshlImm => binary(DataValueExt::shl, arg(0), shift_amt(ctrl_ty, imm())?)?,
790        Opcode::UshrImm => binary(DataValueExt::ushr, arg(0), shift_amt(ctrl_ty, imm())?)?,
791        Opcode::SshrImm => binary(DataValueExt::sshr, arg(0), shift_amt(ctrl_ty, imm())?)?,
792        Opcode::Bitrev => unary(DataValueExt::reverse_bits, arg(0))?,
793        Opcode::Bswap => unary(DataValueExt::swap_bytes, arg(0))?,
794        Opcode::Clz => unary(DataValueExt::leading_zeros, arg(0))?,
795        Opcode::Cls => {
796            let count = if arg(0) < DataValueExt::int(0, ctrl_ty)? {
797                arg(0).leading_ones()?
798            } else {
799                arg(0).leading_zeros()?
800            };
801            assign(DataValueExt::sub(count, DataValueExt::int(1, ctrl_ty)?)?)
802        }
803        Opcode::Ctz => unary(DataValueExt::trailing_zeros, arg(0))?,
804        Opcode::Popcnt => {
805            let count = if arg(0).ty().is_int() {
806                arg(0).count_ones()?
807            } else {
808                let lanes = extractlanes(&arg(0), ctrl_ty)?
809                    .into_iter()
810                    .map(|lane| lane.count_ones())
811                    .collect::<ValueResult<SimdVec<DataValue>>>()?;
812                vectorizelanes(&lanes, ctrl_ty)?
813            };
814            assign(count)
815        }
816
817        Opcode::Fcmp => {
818            let arg0 = extractlanes(&arg(0), ctrl_ty)?;
819            let arg1 = extractlanes(&arg(1), ctrl_ty)?;
820
821            assign(vectorizelanes(
822                &(arg0
823                    .into_iter()
824                    .zip(arg1.into_iter())
825                    .map(|(x, y)| {
826                        DataValue::bool(
827                            fcmp(inst.fp_cond_code().unwrap(), &x, &y).unwrap(),
828                            ctrl_ty.is_vector(),
829                            ctrl_ty.lane_type().as_truthy(),
830                        )
831                    })
832                    .collect::<ValueResult<SimdVec<DataValue>>>()?),
833                ctrl_ty,
834            )?)
835        }
836        Opcode::Fadd => binary(DataValueExt::add, arg(0), arg(1))?,
837        Opcode::Fsub => binary(DataValueExt::sub, arg(0), arg(1))?,
838        Opcode::Fmul => binary(DataValueExt::mul, arg(0), arg(1))?,
839        Opcode::Fdiv => binary(DataValueExt::sdiv, arg(0), arg(1))?,
840        Opcode::Sqrt => unary(DataValueExt::sqrt, arg(0))?,
841        Opcode::Fma => {
842            let arg0 = extractlanes(&arg(0), ctrl_ty)?;
843            let arg1 = extractlanes(&arg(1), ctrl_ty)?;
844            let arg2 = extractlanes(&arg(2), ctrl_ty)?;
845
846            assign(vectorizelanes(
847                &(arg0
848                    .into_iter()
849                    .zip(arg1.into_iter())
850                    .zip(arg2.into_iter())
851                    .map(|((x, y), z)| DataValueExt::fma(x, y, z))
852                    .collect::<ValueResult<SimdVec<DataValue>>>()?),
853                ctrl_ty,
854            )?)
855        }
856        Opcode::Fneg => unary(DataValueExt::neg, arg(0))?,
857        Opcode::Fabs => unary(DataValueExt::abs, arg(0))?,
858        Opcode::Fcopysign => binary(DataValueExt::copysign, arg(0), arg(1))?,
859        Opcode::Fmin => assign(match (arg(0), arg(1)) {
860            (a, _) if a.is_nan()? => a,
861            (_, b) if b.is_nan()? => b,
862            (a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => a,
863            (a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => b,
864            (a, b) => a.smin(b)?,
865        }),
866        Opcode::Fmax => assign(match (arg(0), arg(1)) {
867            (a, _) if a.is_nan()? => a,
868            (_, b) if b.is_nan()? => b,
869            (a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => b,
870            (a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => a,
871            (a, b) => a.smax(b)?,
872        }),
873        Opcode::Ceil => unary(DataValueExt::ceil, arg(0))?,
874        Opcode::Floor => unary(DataValueExt::floor, arg(0))?,
875        Opcode::Trunc => unary(DataValueExt::trunc, arg(0))?,
876        Opcode::Nearest => unary(DataValueExt::nearest, arg(0))?,
877        Opcode::Bitcast | Opcode::ScalarToVector => {
878            let input_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
879            let lanes = &if input_ty.is_vector() {
880                assert_eq!(
881                    inst.memflags()
882                        .expect("byte order flag to be set")
883                        .endianness(Endianness::Little),
884                    Endianness::Little,
885                    "Only little endian bitcasts on vectors are supported"
886                );
887                extractlanes(&arg(0), ctrl_ty)?
888            } else {
889                extractlanes(&arg(0), input_ty)?
890                    .into_iter()
891                    .map(|x| DataValue::convert(x, ValueConversionKind::Exact(ctrl_ty.lane_type())))
892                    .collect::<ValueResult<SimdVec<DataValue>>>()?
893            };
894            assign(match inst.opcode() {
895                Opcode::Bitcast => vectorizelanes(lanes, ctrl_ty)?,
896                Opcode::ScalarToVector => vectorizelanes_all(lanes, ctrl_ty)?,
897                _ => unreachable!(),
898            })
899        }
900        Opcode::Ireduce => assign(DataValueExt::convert(
901            arg(0),
902            ValueConversionKind::Truncate(ctrl_ty),
903        )?),
904        Opcode::Snarrow | Opcode::Unarrow | Opcode::Uunarrow => {
905            let arg0 = extractlanes(&arg(0), ctrl_ty)?;
906            let arg1 = extractlanes(&arg(1), ctrl_ty)?;
907            let new_type = ctrl_ty.split_lanes().unwrap();
908            let (min, max) = new_type.bounds(inst.opcode() == Opcode::Snarrow);
909            let min: DataValue = DataValueExt::int(min as i128, ctrl_ty.lane_type())?;
910            let max: DataValue = DataValueExt::int(max as i128, ctrl_ty.lane_type())?;
911            let narrow = |mut lane: DataValue| -> ValueResult<DataValue> {
912                if inst.opcode() == Opcode::Uunarrow {
913                    lane = DataValueExt::umax(lane, min.clone())?;
914                    lane = DataValueExt::umin(lane, max.clone())?;
915                } else {
916                    lane = DataValueExt::smax(lane, min.clone())?;
917                    lane = DataValueExt::smin(lane, max.clone())?;
918                }
919                lane = lane.convert(ValueConversionKind::Truncate(new_type.lane_type()))?;
920                Ok(lane)
921            };
922            let new_vec = arg0
923                .into_iter()
924                .chain(arg1)
925                .map(|lane| narrow(lane))
926                .collect::<ValueResult<Vec<_>>>()?;
927            assign(vectorizelanes(&new_vec, new_type)?)
928        }
929        Opcode::Bmask => assign({
930            let bool = arg(0);
931            let bool_ty = ctrl_ty.as_truthy_pedantic();
932            let lanes = extractlanes(&bool, bool_ty)?
933                .into_iter()
934                .map(|lane| lane.convert(ValueConversionKind::Mask(ctrl_ty.lane_type())))
935                .collect::<ValueResult<SimdVec<DataValue>>>()?;
936            vectorizelanes(&lanes, ctrl_ty)?
937        }),
938        Opcode::Sextend => assign(DataValueExt::convert(
939            arg(0),
940            ValueConversionKind::SignExtend(ctrl_ty),
941        )?),
942        Opcode::Uextend => assign(DataValueExt::convert(
943            arg(0),
944            ValueConversionKind::ZeroExtend(ctrl_ty),
945        )?),
946        Opcode::Fpromote => assign(DataValueExt::convert(
947            arg(0),
948            ValueConversionKind::Exact(ctrl_ty),
949        )?),
950        Opcode::Fdemote => assign(DataValueExt::convert(
951            arg(0),
952            ValueConversionKind::RoundNearestEven(ctrl_ty),
953        )?),
954        Opcode::Shuffle => {
955            let mask = imm().into_array()?;
956            let a = DataValueExt::into_array(&arg(0))?;
957            let b = DataValueExt::into_array(&arg(1))?;
958            let mut new = [0u8; 16];
959            for i in 0..mask.len() {
960                if (mask[i] as usize) < a.len() {
961                    new[i] = a[mask[i] as usize];
962                } else if (mask[i] as usize - a.len()) < b.len() {
963                    new[i] = b[mask[i] as usize - a.len()];
964                } // else leave as 0.
965            }
966            assign(DataValueExt::vector(new, types::I8X16)?)
967        }
968        Opcode::Swizzle => {
969            let x = DataValueExt::into_array(&arg(0))?;
970            let s = DataValueExt::into_array(&arg(1))?;
971            let mut new = [0u8; 16];
972            for i in 0..new.len() {
973                if (s[i] as usize) < new.len() {
974                    new[i] = x[s[i] as usize];
975                } // else leave as 0
976            }
977            assign(DataValueExt::vector(new, types::I8X16)?)
978        }
979        Opcode::Splat => assign(splat(ctrl_ty, arg(0))?),
980        Opcode::Insertlane => {
981            let idx = imm().into_int_unsigned()? as usize;
982            let mut vector = extractlanes(&arg(0), ctrl_ty)?;
983            vector[idx] = arg(1);
984            assign(vectorizelanes(&vector, ctrl_ty)?)
985        }
986        Opcode::Extractlane => {
987            let idx = imm().into_int_unsigned()? as usize;
988            let lanes = extractlanes(&arg(0), ctrl_ty)?;
989            assign(lanes[idx].clone())
990        }
991        Opcode::VhighBits => {
992            // `ctrl_ty` controls the return type for this, so the input type
993            // must be retrieved via `inst_context`.
994            let vector_type = inst_context
995                .type_of(inst_context.args()[0])
996                .unwrap()
997                .as_int();
998            let a = extractlanes(&arg(0), vector_type)?;
999            let mut result: u128 = 0;
1000            for (i, val) in a.into_iter().enumerate() {
1001                let val = val.reverse_bits()?.into_int_unsigned()?; // MSB -> LSB
1002                result |= (val & 1) << i;
1003            }
1004            assign(DataValueExt::int(result as i128, ctrl_ty)?)
1005        }
1006        Opcode::VanyTrue => {
1007            let simd_ty = ctrl_ty.as_int();
1008            let lane_ty = simd_ty.lane_type();
1009            let init = DataValue::bool(false, true, lane_ty)?;
1010            let any = fold_vector(arg(0), simd_ty, init.clone(), |acc, lane| acc.or(lane))?;
1011            assign(DataValue::bool(any != init, false, types::I8)?)
1012        }
1013        Opcode::VallTrue => assign(DataValue::bool(
1014            !(arg(0)
1015                .iter_lanes(ctrl_ty.as_int())?
1016                .try_fold(false, |acc, lane| {
1017                    Ok::<bool, ValueError>(acc | lane.is_zero()?)
1018                })?),
1019            false,
1020            types::I8,
1021        )?),
1022        Opcode::SwidenLow | Opcode::SwidenHigh | Opcode::UwidenLow | Opcode::UwidenHigh => {
1023            let new_type = ctrl_ty.merge_lanes().unwrap();
1024            let conv_type = match inst.opcode() {
1025                Opcode::SwidenLow | Opcode::SwidenHigh => {
1026                    ValueConversionKind::SignExtend(new_type.lane_type())
1027                }
1028                Opcode::UwidenLow | Opcode::UwidenHigh => {
1029                    ValueConversionKind::ZeroExtend(new_type.lane_type())
1030                }
1031                _ => unreachable!(),
1032            };
1033            let vec_iter = extractlanes(&arg(0), ctrl_ty)?.into_iter();
1034            let new_vec = match inst.opcode() {
1035                Opcode::SwidenLow | Opcode::UwidenLow => vec_iter
1036                    .take(new_type.lane_count() as usize)
1037                    .map(|lane| lane.convert(conv_type.clone()))
1038                    .collect::<ValueResult<Vec<_>>>()?,
1039                Opcode::SwidenHigh | Opcode::UwidenHigh => vec_iter
1040                    .skip(new_type.lane_count() as usize)
1041                    .map(|lane| lane.convert(conv_type.clone()))
1042                    .collect::<ValueResult<Vec<_>>>()?,
1043                _ => unreachable!(),
1044            };
1045            assign(vectorizelanes(&new_vec, new_type)?)
1046        }
1047        Opcode::FcvtToUint | Opcode::FcvtToSint => {
1048            // NaN check
1049            if arg(0).is_nan()? {
1050                return Ok(ControlFlow::Trap(CraneliftTrap::User(
1051                    TrapCode::BAD_CONVERSION_TO_INTEGER,
1052                )));
1053            }
1054            let x = arg(0).into_float()? as i128;
1055            let is_signed = inst.opcode() == Opcode::FcvtToSint;
1056            let (min, max) = ctrl_ty.bounds(is_signed);
1057            let overflow = if is_signed {
1058                x < (min as i128) || x > (max as i128)
1059            } else {
1060                x < 0 || (x as u128) > max
1061            };
1062            // bounds check
1063            if overflow {
1064                return Ok(ControlFlow::Trap(CraneliftTrap::User(
1065                    TrapCode::INTEGER_OVERFLOW,
1066                )));
1067            }
1068            // perform the conversion.
1069            assign(DataValueExt::int(x, ctrl_ty)?)
1070        }
1071        Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => {
1072            let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
1073            let cvt = |x: DataValue| -> ValueResult<DataValue> {
1074                // NaN check
1075                if x.is_nan()? {
1076                    DataValue::int(0, ctrl_ty.lane_type())
1077                } else {
1078                    let is_signed = inst.opcode() == Opcode::FcvtToSintSat;
1079                    let (min, max) = ctrl_ty.bounds(is_signed);
1080                    let x = x.into_float()? as i128;
1081                    let x = if is_signed {
1082                        let x = i128::max(x, min as i128);
1083                        let x = i128::min(x, max as i128);
1084                        x
1085                    } else {
1086                        let x = if x < 0 { 0 } else { x };
1087                        let x = u128::min(x as u128, max);
1088                        x as i128
1089                    };
1090
1091                    DataValue::int(x, ctrl_ty.lane_type())
1092                }
1093            };
1094
1095            let x = extractlanes(&arg(0), in_ty)?;
1096
1097            assign(vectorizelanes(
1098                &x.into_iter()
1099                    .map(cvt)
1100                    .collect::<ValueResult<SimdVec<DataValue>>>()?,
1101                ctrl_ty,
1102            )?)
1103        }
1104        Opcode::FcvtFromUint | Opcode::FcvtFromSint => {
1105            let x = extractlanes(
1106                &arg(0),
1107                inst_context.type_of(inst_context.args()[0]).unwrap(),
1108            )?;
1109            let bits = |x: DataValue| -> ValueResult<u64> {
1110                Ok(match ctrl_ty.lane_type() {
1111                    types::F32 => (if inst.opcode() == Opcode::FcvtFromUint {
1112                        x.into_int_unsigned()? as f32
1113                    } else {
1114                        x.into_int_signed()? as f32
1115                    })
1116                    .to_bits() as u64,
1117                    types::F64 => (if inst.opcode() == Opcode::FcvtFromUint {
1118                        x.into_int_unsigned()? as f64
1119                    } else {
1120                        x.into_int_signed()? as f64
1121                    })
1122                    .to_bits(),
1123                    _ => unimplemented!("unexpected conversion to {:?}", ctrl_ty.lane_type()),
1124                })
1125            };
1126            assign(vectorizelanes(
1127                &x.into_iter()
1128                    .map(|x| DataValue::float(bits(x)?, ctrl_ty.lane_type()))
1129                    .collect::<ValueResult<SimdVec<DataValue>>>()?,
1130                ctrl_ty,
1131            )?)
1132        }
1133        Opcode::FvpromoteLow => {
1134            let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
1135            assert_eq!(in_ty, types::F32X4);
1136            let out_ty = types::F64X2;
1137            let x = extractlanes(&arg(0), in_ty)?;
1138            assign(vectorizelanes(
1139                &x[..(out_ty.lane_count() as usize)]
1140                    .into_iter()
1141                    .map(|x| {
1142                        DataValue::convert(
1143                            x.to_owned(),
1144                            ValueConversionKind::Exact(out_ty.lane_type()),
1145                        )
1146                    })
1147                    .collect::<ValueResult<SimdVec<DataValue>>>()?,
1148                out_ty,
1149            )?)
1150        }
1151        Opcode::Fvdemote => {
1152            let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
1153            assert_eq!(in_ty, types::F64X2);
1154            let out_ty = types::F32X4;
1155            let x = extractlanes(&arg(0), in_ty)?;
1156            let x = &mut x
1157                .into_iter()
1158                .map(|x| {
1159                    DataValue::convert(x, ValueConversionKind::RoundNearestEven(out_ty.lane_type()))
1160                })
1161                .collect::<ValueResult<SimdVec<DataValue>>>()?;
1162            // zero the high bits.
1163            for _ in 0..(out_ty.lane_count() as usize - x.len()) {
1164                x.push(DataValue::float(0, out_ty.lane_type())?);
1165            }
1166            assign(vectorizelanes(x, out_ty)?)
1167        }
1168        Opcode::Isplit => assign_multiple(&[
1169            DataValueExt::convert(arg(0), ValueConversionKind::Truncate(types::I64))?,
1170            DataValueExt::convert(arg(0), ValueConversionKind::ExtractUpper(types::I64))?,
1171        ]),
1172        Opcode::Iconcat => assign(DataValueExt::concat(arg(0), arg(1))?),
1173        Opcode::AtomicRmw => {
1174            let op = inst.atomic_rmw_op().unwrap();
1175            let val = arg(1);
1176            let addr = arg(0).into_int_unsigned()? as u64;
1177            let mem_flags = inst.memflags().expect("instruction to have memory flags");
1178            let loaded = Address::try_from(addr)
1179                .and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));
1180            let prev_val = match loaded {
1181                Ok(v) => v,
1182                Err(e) => return Ok(ControlFlow::Trap(memerror_to_trap(e))),
1183            };
1184            let prev_val_to_assign = prev_val.clone();
1185            let replace = match op {
1186                AtomicRmwOp::Xchg => Ok(val),
1187                AtomicRmwOp::Add => DataValueExt::add(prev_val, val),
1188                AtomicRmwOp::Sub => DataValueExt::sub(prev_val, val),
1189                AtomicRmwOp::And => DataValueExt::and(prev_val, val),
1190                AtomicRmwOp::Or => DataValueExt::or(prev_val, val),
1191                AtomicRmwOp::Xor => DataValueExt::xor(prev_val, val),
1192                AtomicRmwOp::Nand => DataValueExt::and(prev_val, val).and_then(DataValue::not),
1193                AtomicRmwOp::Smax => DataValueExt::smax(prev_val, val),
1194                AtomicRmwOp::Smin => DataValueExt::smin(prev_val, val),
1195                AtomicRmwOp::Umax => DataValueExt::umax(val, prev_val),
1196                AtomicRmwOp::Umin => DataValueExt::umin(val, prev_val),
1197            }?;
1198            let stored = Address::try_from(addr)
1199                .and_then(|addr| state.checked_store(addr, replace, mem_flags));
1200            assign_or_memtrap(stored.map(|_| prev_val_to_assign))
1201        }
1202        Opcode::AtomicCas => {
1203            let addr = arg(0).into_int_unsigned()? as u64;
1204            let mem_flags = inst.memflags().expect("instruction to have memory flags");
1205            let loaded = Address::try_from(addr)
1206                .and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));
1207            let loaded_val = match loaded {
1208                Ok(v) => v,
1209                Err(e) => return Ok(ControlFlow::Trap(memerror_to_trap(e))),
1210            };
1211            let expected_val = arg(1);
1212            let val_to_assign = if loaded_val == expected_val {
1213                let val_to_store = arg(2);
1214                Address::try_from(addr)
1215                    .and_then(|addr| state.checked_store(addr, val_to_store, mem_flags))
1216                    .map(|_| loaded_val)
1217            } else {
1218                Ok(loaded_val)
1219            };
1220            assign_or_memtrap(val_to_assign)
1221        }
1222        Opcode::AtomicLoad => {
1223            let load_ty = inst_context.controlling_type().unwrap();
1224            let addr = arg(0).into_int_unsigned()? as u64;
1225            let mem_flags = inst.memflags().expect("instruction to have memory flags");
1226            // We are doing a regular load here, this isn't actually thread safe.
1227            assign_or_memtrap(
1228                Address::try_from(addr)
1229                    .and_then(|addr| state.checked_load(addr, load_ty, mem_flags)),
1230            )
1231        }
1232        Opcode::AtomicStore => {
1233            let val = arg(0);
1234            let addr = arg(1).into_int_unsigned()? as u64;
1235            let mem_flags = inst.memflags().expect("instruction to have memory flags");
1236            // We are doing a regular store here, this isn't actually thread safe.
1237            continue_or_memtrap(
1238                Address::try_from(addr).and_then(|addr| state.checked_store(addr, val, mem_flags)),
1239            )
1240        }
1241        Opcode::Fence => {
1242            // The interpreter always runs in a single threaded context, so we don't
1243            // actually need to emit a fence here.
1244            ControlFlow::Continue
1245        }
1246        Opcode::SqmulRoundSat => {
1247            let lane_type = ctrl_ty.lane_type();
1248            let double_width = ctrl_ty.double_width().unwrap().lane_type();
1249            let arg0 = extractlanes(&arg(0), ctrl_ty)?;
1250            let arg1 = extractlanes(&arg(1), ctrl_ty)?;
1251            let (min, max) = lane_type.bounds(true);
1252            let min: DataValue = DataValueExt::int(min as i128, double_width)?;
1253            let max: DataValue = DataValueExt::int(max as i128, double_width)?;
1254            let new_vec = arg0
1255                .into_iter()
1256                .zip(arg1.into_iter())
1257                .map(|(x, y)| {
1258                    let x = x.into_int_signed()?;
1259                    let y = y.into_int_signed()?;
1260                    // temporarily double width of the value to avoid overflow.
1261                    let z: DataValue = DataValueExt::int(
1262                        (x * y + (1 << (lane_type.bits() - 2))) >> (lane_type.bits() - 1),
1263                        double_width,
1264                    )?;
1265                    // check bounds, saturate, and truncate to correct width.
1266                    let z = DataValueExt::smin(z, max.clone())?;
1267                    let z = DataValueExt::smax(z, min.clone())?;
1268                    let z = z.convert(ValueConversionKind::Truncate(lane_type))?;
1269                    Ok(z)
1270                })
1271                .collect::<ValueResult<SimdVec<_>>>()?;
1272            assign(vectorizelanes(&new_vec, ctrl_ty)?)
1273        }
1274        Opcode::IaddPairwise => {
1275            assign(binary_pairwise(arg(0), arg(1), ctrl_ty, DataValueExt::add)?)
1276        }
1277        Opcode::ExtractVector => {
1278            unimplemented!("ExtractVector not supported");
1279        }
1280        Opcode::GetFramePointer => unimplemented!("GetFramePointer"),
1281        Opcode::GetStackPointer => unimplemented!("GetStackPointer"),
1282        Opcode::GetReturnAddress => unimplemented!("GetReturnAddress"),
1283        Opcode::X86Pshufb => unimplemented!("X86Pshufb"),
1284        Opcode::X86Blendv => unimplemented!("X86Blendv"),
1285        Opcode::X86Pmulhrsw => unimplemented!("X86Pmulhrsw"),
1286        Opcode::X86Pmaddubsw => unimplemented!("X86Pmaddubsw"),
1287        Opcode::X86Cvtt2dq => unimplemented!("X86Cvtt2dq"),
1288        Opcode::StackSwitch => unimplemented!("StackSwitch"),
1289    })
1290}
1291
1292#[derive(Error, Debug)]
1293pub enum StepError {
1294    #[error("unable to retrieve value from SSA reference: {0}")]
1295    UnknownValue(ValueRef),
1296    #[error("unable to find the following function: {0}")]
1297    UnknownFunction(FuncRef),
1298    #[error("cannot step with these values")]
1299    ValueError(#[from] ValueError),
1300    #[error("failed to access memory")]
1301    MemoryError(#[from] MemoryError),
1302}
1303
1304/// Enumerate the ways in which the control flow can change based on a single step in a Cranelift
1305/// interpreter.
1306#[derive(Debug, PartialEq)]
1307pub enum ControlFlow<'a> {
1308    /// Return one or more values from an instruction to be assigned to a left-hand side, e.g.:
1309    /// in `v0 = iadd v1, v2`, the sum of `v1` and `v2` is assigned to `v0`.
1310    Assign(SmallVec<[DataValue; 1]>),
1311    /// Continue to the next available instruction, e.g.: in `nop`, we expect to resume execution
1312    /// at the instruction after it.
1313    Continue,
1314    /// Jump to another block with the given parameters, e.g.: in
1315    /// `brif v0, block42(v1, v2), block97`, if the condition is true, we continue execution at the
1316    /// first instruction of `block42` with the values in `v1` and `v2` filling in the block
1317    /// parameters.
1318    ContinueAt(Block, SmallVec<[DataValue; 1]>),
1319    /// Indicates a call the given [Function] with the supplied arguments.
1320    Call(&'a Function, SmallVec<[DataValue; 1]>),
1321    /// Indicates a tail call to the given [Function] with the supplied arguments.
1322    ReturnCall(&'a Function, SmallVec<[DataValue; 1]>),
1323    /// Return from the current function with the given parameters, e.g.: `return [v1, v2]`.
1324    Return(SmallVec<[DataValue; 1]>),
1325    /// Stop with a program-generated trap; note that these are distinct from errors that may occur
1326    /// during interpretation.
1327    Trap(CraneliftTrap),
1328}
1329
1330#[derive(Error, Debug, PartialEq, Eq, Hash)]
1331pub enum CraneliftTrap {
1332    #[error("user code: {0}")]
1333    User(TrapCode),
1334    #[error("bad signature")]
1335    BadSignature,
1336    #[error("unreachable code has been reached")]
1337    UnreachableCodeReached,
1338    #[error("heap is misaligned")]
1339    HeapMisaligned,
1340    #[error("user debug")]
1341    Debug,
1342}
1343
1344/// Compare two values using the given integer condition `code`.
1345fn icmp(
1346    ctrl_ty: types::Type,
1347    code: IntCC,
1348    left: &DataValue,
1349    right: &DataValue,
1350) -> ValueResult<DataValue> {
1351    let cmp = |bool_ty: types::Type,
1352               code: IntCC,
1353               left: &DataValue,
1354               right: &DataValue|
1355     -> ValueResult<DataValue> {
1356        Ok(DataValueExt::bool(
1357            match code {
1358                IntCC::Equal => left == right,
1359                IntCC::NotEqual => left != right,
1360                IntCC::SignedGreaterThan => left > right,
1361                IntCC::SignedGreaterThanOrEqual => left >= right,
1362                IntCC::SignedLessThan => left < right,
1363                IntCC::SignedLessThanOrEqual => left <= right,
1364                IntCC::UnsignedGreaterThan => {
1365                    left.clone().into_int_unsigned()? > right.clone().into_int_unsigned()?
1366                }
1367                IntCC::UnsignedGreaterThanOrEqual => {
1368                    left.clone().into_int_unsigned()? >= right.clone().into_int_unsigned()?
1369                }
1370                IntCC::UnsignedLessThan => {
1371                    left.clone().into_int_unsigned()? < right.clone().into_int_unsigned()?
1372                }
1373                IntCC::UnsignedLessThanOrEqual => {
1374                    left.clone().into_int_unsigned()? <= right.clone().into_int_unsigned()?
1375                }
1376            },
1377            ctrl_ty.is_vector(),
1378            bool_ty,
1379        )?)
1380    };
1381
1382    let dst_ty = ctrl_ty.as_truthy();
1383    let left = extractlanes(left, ctrl_ty)?;
1384    let right = extractlanes(right, ctrl_ty)?;
1385
1386    let res = left
1387        .into_iter()
1388        .zip(right.into_iter())
1389        .map(|(l, r)| cmp(dst_ty.lane_type(), code, &l, &r))
1390        .collect::<ValueResult<SimdVec<DataValue>>>()?;
1391
1392    Ok(vectorizelanes(&res, dst_ty)?)
1393}
1394
1395/// Compare two values using the given floating point condition `code`.
1396fn fcmp(code: FloatCC, left: &DataValue, right: &DataValue) -> ValueResult<bool> {
1397    Ok(match code {
1398        FloatCC::Ordered => left == right || left < right || left > right,
1399        FloatCC::Unordered => DataValueExt::uno(left, right)?,
1400        FloatCC::Equal => left == right,
1401        FloatCC::NotEqual => left < right || left > right || DataValueExt::uno(left, right)?,
1402        FloatCC::OrderedNotEqual => left < right || left > right,
1403        FloatCC::UnorderedOrEqual => left == right || DataValueExt::uno(left, right)?,
1404        FloatCC::LessThan => left < right,
1405        FloatCC::LessThanOrEqual => left <= right,
1406        FloatCC::GreaterThan => left > right,
1407        FloatCC::GreaterThanOrEqual => left >= right,
1408        FloatCC::UnorderedOrLessThan => DataValueExt::uno(left, right)? || left < right,
1409        FloatCC::UnorderedOrLessThanOrEqual => DataValueExt::uno(left, right)? || left <= right,
1410        FloatCC::UnorderedOrGreaterThan => DataValueExt::uno(left, right)? || left > right,
1411        FloatCC::UnorderedOrGreaterThanOrEqual => DataValueExt::uno(left, right)? || left >= right,
1412    })
1413}
1414
1415pub type SimdVec<DataValue> = SmallVec<[DataValue; 4]>;
1416
1417/// Converts a SIMD vector value into a Rust array of [Value] for processing.
1418/// If `x` is a scalar, it will be returned as a single-element array.
1419pub(crate) fn extractlanes(
1420    x: &DataValue,
1421    vector_type: types::Type,
1422) -> ValueResult<SimdVec<DataValue>> {
1423    let lane_type = vector_type.lane_type();
1424    let mut lanes = SimdVec::new();
1425    // Wrap scalar values as a single-element vector and return.
1426    if !x.ty().is_vector() {
1427        lanes.push(x.clone());
1428        return Ok(lanes);
1429    }
1430
1431    let iterations = match lane_type {
1432        types::I8 => 1,
1433        types::I16 | types::F16 => 2,
1434        types::I32 | types::F32 => 4,
1435        types::I64 | types::F64 => 8,
1436        _ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),
1437    };
1438
1439    let x = x.into_array()?;
1440    for i in 0..vector_type.lane_count() {
1441        let mut lane: i128 = 0;
1442        for j in 0..iterations {
1443            lane += (x[((i * iterations) + j) as usize] as i128) << (8 * j);
1444        }
1445
1446        let lane_val: DataValue = if lane_type.is_float() {
1447            DataValueExt::float(lane as u64, lane_type)?
1448        } else {
1449            DataValueExt::int(lane, lane_type)?
1450        };
1451        lanes.push(lane_val);
1452    }
1453    return Ok(lanes);
1454}
1455
1456/// Convert a Rust array of [Value] back into a `Value::vector`.
1457/// Supplying a single-element array will simply return its contained value.
1458fn vectorizelanes(x: &[DataValue], vector_type: types::Type) -> ValueResult<DataValue> {
1459    // If the array is only one element, return it as a scalar.
1460    if x.len() == 1 {
1461        Ok(x[0].clone())
1462    } else {
1463        vectorizelanes_all(x, vector_type)
1464    }
1465}
1466
1467/// Convert a Rust array of [Value] back into a `Value::vector`.
1468fn vectorizelanes_all(x: &[DataValue], vector_type: types::Type) -> ValueResult<DataValue> {
1469    let lane_type = vector_type.lane_type();
1470    let iterations = match lane_type {
1471        types::I8 => 1,
1472        types::I16 | types::F16 => 2,
1473        types::I32 | types::F32 => 4,
1474        types::I64 | types::F64 => 8,
1475        _ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),
1476    };
1477    let mut result: [u8; 16] = [0; 16];
1478    for (i, val) in x.iter().enumerate() {
1479        let lane_val: i128 = val
1480            .clone()
1481            .convert(ValueConversionKind::Exact(lane_type.as_int()))?
1482            .into_int_unsigned()? as i128;
1483
1484        for j in 0..iterations {
1485            result[(i * iterations) + j] = (lane_val >> (8 * j)) as u8;
1486        }
1487    }
1488    DataValueExt::vector(result, vector_type)
1489}
1490
1491/// Performs a lanewise fold on a vector type
1492fn fold_vector<F>(v: DataValue, ty: types::Type, init: DataValue, op: F) -> ValueResult<DataValue>
1493where
1494    F: FnMut(DataValue, DataValue) -> ValueResult<DataValue>,
1495{
1496    extractlanes(&v, ty)?.into_iter().try_fold(init, op)
1497}
1498
1499/// Performs the supplied unary arithmetic `op` on a Value, either Vector or Scalar.
1500fn unary_arith<F>(x: DataValue, vector_type: types::Type, op: F) -> ValueResult<DataValue>
1501where
1502    F: Fn(DataValue) -> ValueResult<DataValue>,
1503{
1504    let arg = extractlanes(&x, vector_type)?;
1505
1506    let result = arg
1507        .into_iter()
1508        .map(|arg| Ok(op(arg)?))
1509        .collect::<ValueResult<SimdVec<DataValue>>>()?;
1510
1511    vectorizelanes(&result, vector_type)
1512}
1513
1514/// Performs the supplied binary arithmetic `op` on two values, either vector or scalar.
1515fn binary_arith<F>(
1516    x: DataValue,
1517    y: DataValue,
1518    vector_type: types::Type,
1519    op: F,
1520) -> ValueResult<DataValue>
1521where
1522    F: Fn(DataValue, DataValue) -> ValueResult<DataValue>,
1523{
1524    let arg0 = extractlanes(&x, vector_type)?;
1525    let arg1 = extractlanes(&y, vector_type)?;
1526
1527    let result = arg0
1528        .into_iter()
1529        .zip(arg1)
1530        .map(|(lhs, rhs)| Ok(op(lhs, rhs)?))
1531        .collect::<ValueResult<SimdVec<DataValue>>>()?;
1532
1533    vectorizelanes(&result, vector_type)
1534}
1535
1536/// Performs the supplied pairwise arithmetic `op` on two SIMD vectors, where
1537/// pairs are formed from adjacent vector elements and the vectors are
1538/// concatenated at the end.
1539fn binary_pairwise<F>(
1540    x: DataValue,
1541    y: DataValue,
1542    vector_type: types::Type,
1543    op: F,
1544) -> ValueResult<DataValue>
1545where
1546    F: Fn(DataValue, DataValue) -> ValueResult<DataValue>,
1547{
1548    let arg0 = extractlanes(&x, vector_type)?;
1549    let arg1 = extractlanes(&y, vector_type)?;
1550
1551    let result = arg0
1552        .chunks(2)
1553        .chain(arg1.chunks(2))
1554        .map(|pair| op(pair[0].clone(), pair[1].clone()))
1555        .collect::<ValueResult<SimdVec<DataValue>>>()?;
1556
1557    vectorizelanes(&result, vector_type)
1558}
1559
1560fn bitselect(c: DataValue, x: DataValue, y: DataValue) -> ValueResult<DataValue> {
1561    let mask_x = DataValueExt::and(c.clone(), x)?;
1562    let mask_y = DataValueExt::and(DataValueExt::not(c)?, y)?;
1563    DataValueExt::or(mask_x, mask_y)
1564}
1565
1566fn splat(ty: Type, val: DataValue) -> ValueResult<DataValue> {
1567    let mut new_vector = SimdVec::new();
1568    for _ in 0..ty.lane_count() {
1569        new_vector.push(val.clone());
1570    }
1571    vectorizelanes(&new_vector, ty)
1572}
1573
1574// Prepares the shift amount for a shift/rotate operation.
1575// The shift amount must be the same type and have the same number of lanes as the vector.
1576fn shift_amt(ty: Type, val: DataValue) -> ValueResult<DataValue> {
1577    splat(ty, val.convert(ValueConversionKind::Exact(ty.lane_type()))?)
1578}