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