Skip to main content

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