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