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