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::{
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
20fn 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 (a, b) if a.is_vector() && b.is_vector() => true,
33 (a, b) => a == b,
34 })
35}
36
37fn 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
46fn 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
59pub 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 let arg = |index: usize| -> DataValue {
83 let value_ref = inst_context.args()[index];
84 state.current_frame().get(value_ref).clone()
85 };
86
87 let args = || -> SmallVec<[DataValue; 1]> { state.collect_values(inst_context.args()) };
89
90 let args_range = |indexes: RangeFrom<usize>| -> Result<SmallVec<[DataValue; 1]>, StepError> {
92 Ok(SmallVec::<[DataValue; 1]>::from(&args()[indexes]))
93 };
94
95 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 InstructionData::BinaryImm8 { imm, .. } | InstructionData::TernaryImm8 { imm, .. } => {
146 DataValue::from(imm as i8) }
148 InstructionData::UnaryIeee16 { imm, .. } => DataValue::from(imm),
150 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 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 let imm_as_ctrl_ty = || -> Result<DataValue, ValueError> {
169 DataValue::convert(imm(), ValueConversionKind::Exact(ctrl_ty))
170 };
171
172 let assign = |value: DataValue| ControlFlow::Assign(smallvec![value]);
174
175 let assign_multiple = |values: &[DataValue]| ControlFlow::Assign(SmallVec::from(values));
177
178 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 let assign_or_memtrap = |res| match res {
207 Ok(v) => assign(v),
208 Err(e) => ControlFlow::Trap(memerror_to_trap(e)),
209 };
210
211 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 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 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 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 let choose = |condition: bool, left: DataValue, right: DataValue| -> ControlFlow {
260 assign(if condition { left } else { right })
261 };
262
263 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 #[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 let trap_code = || -> TrapCode { inst.trap_code().unwrap() };
288
289 let trap_when = |condition: bool, trap: CraneliftTrap| -> ControlFlow {
291 if condition {
292 ControlFlow::Trap(trap)
293 } else {
294 ControlFlow::Continue
295 }
296 };
297
298 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 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 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 if validate_signature_params(&signature.returns[..], &res[..]) {
334 ControlFlow::Assign(res)
335 } else {
336 ControlFlow::Trap(CraneliftTrap::BadSignature)
337 }
338 }
339 })
340 };
341
342 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 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 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 if let Some(sum) = DataValueExt::uadd_checked(arg(0), arg(1))? {
775 assign(sum)
776 } else {
777 ControlFlow::Trap(CraneliftTrap::User(trap_code()))
778 }
779 }
780 Opcode::SsubOverflowBin => {
781 let (mut sub, mut carry) = arg(0).ssub_overflow(arg(1))?;
782
783 if DataValueExt::into_bool(arg(2))? {
784 let (sub2, carry2) = sub.ssub_overflow(DataValueExt::int(1, ctrl_ty)?)?;
785 carry |= carry2;
786 sub = sub2;
787 }
788
789 assign_multiple(&[sub, DataValueExt::bool(carry, false, types::I8)?])
790 }
791 Opcode::UsubOverflowBin => {
792 let (mut sub, mut carry) = arg(0).usub_overflow(arg(1))?;
793
794 if DataValueExt::into_bool(arg(2))? {
795 let (sub2, carry2) = sub.usub_overflow(DataValueExt::int(1, ctrl_ty)?)?;
796 carry |= carry2;
797 sub = sub2;
798 }
799
800 assign_multiple(&[sub, DataValueExt::bool(carry, false, types::I8)?])
801 }
802 Opcode::Band => binary(DataValueExt::and, arg(0), arg(1))?,
803 Opcode::Bor => binary(DataValueExt::or, arg(0), arg(1))?,
804 Opcode::Bxor => binary(DataValueExt::xor, arg(0), arg(1))?,
805 Opcode::Bnot => unary(DataValueExt::not, arg(0))?,
806 Opcode::BandNot => binary(DataValueExt::and, arg(0), DataValueExt::not(arg(1))?)?,
807 Opcode::BorNot => binary(DataValueExt::or, arg(0), DataValueExt::not(arg(1))?)?,
808 Opcode::BxorNot => binary(DataValueExt::xor, arg(0), DataValueExt::not(arg(1))?)?,
809 Opcode::BandImm => binary(DataValueExt::and, arg(0), imm_as_ctrl_ty()?)?,
810 Opcode::BorImm => binary(DataValueExt::or, arg(0), imm_as_ctrl_ty()?)?,
811 Opcode::BxorImm => binary(DataValueExt::xor, arg(0), imm_as_ctrl_ty()?)?,
812 Opcode::Rotl => binary(DataValueExt::rotl, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
813 Opcode::Rotr => binary(DataValueExt::rotr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
814 Opcode::RotlImm => binary(DataValueExt::rotl, arg(0), shift_amt(ctrl_ty, imm())?)?,
815 Opcode::RotrImm => binary(DataValueExt::rotr, arg(0), shift_amt(ctrl_ty, imm())?)?,
816 Opcode::Ishl => binary(DataValueExt::shl, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
817 Opcode::Ushr => binary(DataValueExt::ushr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
818 Opcode::Sshr => binary(DataValueExt::sshr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
819 Opcode::IshlImm => binary(DataValueExt::shl, arg(0), shift_amt(ctrl_ty, imm())?)?,
820 Opcode::UshrImm => binary(DataValueExt::ushr, arg(0), shift_amt(ctrl_ty, imm())?)?,
821 Opcode::SshrImm => binary(DataValueExt::sshr, arg(0), shift_amt(ctrl_ty, imm())?)?,
822 Opcode::Bitrev => unary(DataValueExt::reverse_bits, arg(0))?,
823 Opcode::Bswap => unary(DataValueExt::swap_bytes, arg(0))?,
824 Opcode::Clz => unary(DataValueExt::leading_zeros, arg(0))?,
825 Opcode::Cls => {
826 let count = if arg(0) < DataValueExt::int(0, ctrl_ty)? {
827 arg(0).leading_ones()?
828 } else {
829 arg(0).leading_zeros()?
830 };
831 assign(DataValueExt::sub(count, DataValueExt::int(1, ctrl_ty)?)?)
832 }
833 Opcode::Ctz => unary(DataValueExt::trailing_zeros, arg(0))?,
834 Opcode::Popcnt => {
835 let count = if arg(0).ty().is_int() {
836 arg(0).count_ones()?
837 } else {
838 let lanes = extractlanes(&arg(0), ctrl_ty)?
839 .into_iter()
840 .map(|lane| lane.count_ones())
841 .collect::<ValueResult<SimdVec<DataValue>>>()?;
842 vectorizelanes(&lanes, ctrl_ty)?
843 };
844 assign(count)
845 }
846
847 Opcode::Fcmp => {
848 let arg0 = extractlanes(&arg(0), ctrl_ty)?;
849 let arg1 = extractlanes(&arg(1), ctrl_ty)?;
850
851 assign(vectorizelanes(
852 &(arg0
853 .into_iter()
854 .zip(arg1.into_iter())
855 .map(|(x, y)| {
856 DataValue::bool(
857 fcmp(inst.fp_cond_code().unwrap(), &x, &y).unwrap(),
858 ctrl_ty.is_vector(),
859 ctrl_ty.lane_type().as_truthy(),
860 )
861 })
862 .collect::<ValueResult<SimdVec<DataValue>>>()?),
863 ctrl_ty,
864 )?)
865 }
866 Opcode::Fadd => binary(DataValueExt::add, arg(0), arg(1))?,
867 Opcode::Fsub => binary(DataValueExt::sub, arg(0), arg(1))?,
868 Opcode::Fmul => binary(DataValueExt::mul, arg(0), arg(1))?,
869 Opcode::Fdiv => binary(DataValueExt::sdiv, arg(0), arg(1))?,
870 Opcode::Sqrt => unary(DataValueExt::sqrt, arg(0))?,
871 Opcode::Fma => {
872 let arg0 = extractlanes(&arg(0), ctrl_ty)?;
873 let arg1 = extractlanes(&arg(1), ctrl_ty)?;
874 let arg2 = extractlanes(&arg(2), ctrl_ty)?;
875
876 assign(vectorizelanes(
877 &(arg0
878 .into_iter()
879 .zip(arg1.into_iter())
880 .zip(arg2.into_iter())
881 .map(|((x, y), z)| DataValueExt::fma(x, y, z))
882 .collect::<ValueResult<SimdVec<DataValue>>>()?),
883 ctrl_ty,
884 )?)
885 }
886 Opcode::Fneg => unary(DataValueExt::neg, arg(0))?,
887 Opcode::Fabs => unary(DataValueExt::abs, arg(0))?,
888 Opcode::Fcopysign => binary(DataValueExt::copysign, arg(0), arg(1))?,
889 Opcode::Fmin => {
890 let scalar_min = |a: DataValue, b: DataValue| -> ValueResult<DataValue> {
891 Ok(match (a, b) {
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 };
899
900 if ctrl_ty.is_vector() {
901 let arg0 = extractlanes(&arg(0), ctrl_ty)?;
902 let arg1 = extractlanes(&arg(1), ctrl_ty)?;
903
904 assign(vectorizelanes(
905 &(arg0
906 .into_iter()
907 .zip(arg1.into_iter())
908 .map(|(a, b)| scalar_min(a, b))
909 .collect::<ValueResult<SimdVec<DataValue>>>()?),
910 ctrl_ty,
911 )?)
912 } else {
913 assign(scalar_min(arg(0), arg(1))?)
914 }
915 }
916 Opcode::Fmax => {
917 let scalar_max = |a: DataValue, b: DataValue| -> ValueResult<DataValue> {
918 Ok(match (a, b) {
919 (a, _) if a.is_nan()? => a,
920 (_, b) if b.is_nan()? => b,
921 (a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => b,
922 (a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => a,
923 (a, b) => a.smax(b)?,
924 })
925 };
926
927 if ctrl_ty.is_vector() {
928 let arg0 = extractlanes(&arg(0), ctrl_ty)?;
929 let arg1 = extractlanes(&arg(1), ctrl_ty)?;
930
931 assign(vectorizelanes(
932 &(arg0
933 .into_iter()
934 .zip(arg1.into_iter())
935 .map(|(a, b)| scalar_max(a, b))
936 .collect::<ValueResult<SimdVec<DataValue>>>()?),
937 ctrl_ty,
938 )?)
939 } else {
940 assign(scalar_max(arg(0), arg(1))?)
941 }
942 }
943 Opcode::Ceil => unary(DataValueExt::ceil, arg(0))?,
944 Opcode::Floor => unary(DataValueExt::floor, arg(0))?,
945 Opcode::Trunc => unary(DataValueExt::trunc, arg(0))?,
946 Opcode::Nearest => unary(DataValueExt::nearest, arg(0))?,
947 Opcode::Bitcast | Opcode::ScalarToVector => {
948 let input_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
949 let lanes = &if input_ty.is_vector() {
950 assert_eq!(
951 inst.memflags()
952 .expect("byte order flag to be set")
953 .endianness(Endianness::Little),
954 Endianness::Little,
955 "Only little endian bitcasts on vectors are supported"
956 );
957 extractlanes(&arg(0), ctrl_ty)?
958 } else {
959 extractlanes(&arg(0), input_ty)?
960 .into_iter()
961 .map(|x| DataValue::convert(x, ValueConversionKind::Exact(ctrl_ty.lane_type())))
962 .collect::<ValueResult<SimdVec<DataValue>>>()?
963 };
964 assign(match inst.opcode() {
965 Opcode::Bitcast => vectorizelanes(lanes, ctrl_ty)?,
966 Opcode::ScalarToVector => vectorizelanes_all(lanes, ctrl_ty)?,
967 _ => unreachable!(),
968 })
969 }
970 Opcode::Ireduce => assign(DataValueExt::convert(
971 arg(0),
972 ValueConversionKind::Truncate(ctrl_ty),
973 )?),
974 Opcode::Snarrow | Opcode::Unarrow | Opcode::Uunarrow => {
975 let arg0 = extractlanes(&arg(0), ctrl_ty)?;
976 let arg1 = extractlanes(&arg(1), ctrl_ty)?;
977 let new_type = ctrl_ty.split_lanes().unwrap();
978 let (min, max) = new_type.bounds(inst.opcode() == Opcode::Snarrow);
979 let min: DataValue = DataValueExt::int(min as i128, ctrl_ty.lane_type())?;
980 let max: DataValue = DataValueExt::int(max as i128, ctrl_ty.lane_type())?;
981 let narrow = |mut lane: DataValue| -> ValueResult<DataValue> {
982 if inst.opcode() == Opcode::Uunarrow {
983 lane = DataValueExt::umax(lane, min.clone())?;
984 lane = DataValueExt::umin(lane, max.clone())?;
985 } else {
986 lane = DataValueExt::smax(lane, min.clone())?;
987 lane = DataValueExt::smin(lane, max.clone())?;
988 }
989 lane = lane.convert(ValueConversionKind::Truncate(new_type.lane_type()))?;
990 Ok(lane)
991 };
992 let new_vec = arg0
993 .into_iter()
994 .chain(arg1)
995 .map(|lane| narrow(lane))
996 .collect::<ValueResult<Vec<_>>>()?;
997 assign(vectorizelanes(&new_vec, new_type)?)
998 }
999 Opcode::Bmask => assign({
1000 let bool = arg(0);
1001 let bool_ty = ctrl_ty.as_truthy_pedantic();
1002 let lanes = extractlanes(&bool, bool_ty)?
1003 .into_iter()
1004 .map(|lane| lane.convert(ValueConversionKind::Mask(ctrl_ty.lane_type())))
1005 .collect::<ValueResult<SimdVec<DataValue>>>()?;
1006 vectorizelanes(&lanes, ctrl_ty)?
1007 }),
1008 Opcode::Sextend => assign(DataValueExt::convert(
1009 arg(0),
1010 ValueConversionKind::SignExtend(ctrl_ty),
1011 )?),
1012 Opcode::Uextend => assign(DataValueExt::convert(
1013 arg(0),
1014 ValueConversionKind::ZeroExtend(ctrl_ty),
1015 )?),
1016 Opcode::Fpromote => assign(DataValueExt::convert(
1017 arg(0),
1018 ValueConversionKind::Exact(ctrl_ty),
1019 )?),
1020 Opcode::Fdemote => assign(DataValueExt::convert(
1021 arg(0),
1022 ValueConversionKind::RoundNearestEven(ctrl_ty),
1023 )?),
1024 Opcode::Shuffle => {
1025 let mask = imm().into_array()?;
1026 let a = DataValueExt::into_array(&arg(0))?;
1027 let b = DataValueExt::into_array(&arg(1))?;
1028 let mut new = [0u8; 16];
1029 for i in 0..mask.len() {
1030 if (mask[i] as usize) < a.len() {
1031 new[i] = a[mask[i] as usize];
1032 } else if (mask[i] as usize - a.len()) < b.len() {
1033 new[i] = b[mask[i] as usize - a.len()];
1034 } }
1036 assign(DataValueExt::vector(new, types::I8X16)?)
1037 }
1038 Opcode::Swizzle => {
1039 let x = DataValueExt::into_array(&arg(0))?;
1040 let s = DataValueExt::into_array(&arg(1))?;
1041 let mut new = [0u8; 16];
1042 for i in 0..new.len() {
1043 if (s[i] as usize) < new.len() {
1044 new[i] = x[s[i] as usize];
1045 } }
1047 assign(DataValueExt::vector(new, types::I8X16)?)
1048 }
1049 Opcode::Splat => assign(splat(ctrl_ty, arg(0))?),
1050 Opcode::Insertlane => {
1051 let idx = imm().into_int_unsigned()? as usize;
1052 let mut vector = extractlanes(&arg(0), ctrl_ty)?;
1053 vector[idx] = arg(1);
1054 assign(vectorizelanes(&vector, ctrl_ty)?)
1055 }
1056 Opcode::Extractlane => {
1057 let idx = imm().into_int_unsigned()? as usize;
1058 let lanes = extractlanes(&arg(0), ctrl_ty)?;
1059 assign(lanes[idx].clone())
1060 }
1061 Opcode::VhighBits => {
1062 let vector_type = inst_context
1065 .type_of(inst_context.args()[0])
1066 .unwrap()
1067 .as_int();
1068 let a = extractlanes(&arg(0), vector_type)?;
1069 let mut result: u128 = 0;
1070 for (i, val) in a.into_iter().enumerate() {
1071 let val = val.reverse_bits()?.into_int_unsigned()?; result |= (val & 1) << i;
1073 }
1074 assign(DataValueExt::int(result as i128, ctrl_ty)?)
1075 }
1076 Opcode::VanyTrue => {
1077 let simd_ty = ctrl_ty.as_int();
1078 let lane_ty = simd_ty.lane_type();
1079 let init = DataValue::bool(false, true, lane_ty)?;
1080 let any = fold_vector(arg(0), simd_ty, init.clone(), |acc, lane| acc.or(lane))?;
1081 assign(DataValue::bool(any != init, false, types::I8)?)
1082 }
1083 Opcode::VallTrue => assign(DataValue::bool(
1084 !(arg(0)
1085 .iter_lanes(ctrl_ty.as_int())?
1086 .try_fold(false, |acc, lane| {
1087 Ok::<bool, ValueError>(acc | lane.is_zero()?)
1088 })?),
1089 false,
1090 types::I8,
1091 )?),
1092 Opcode::SwidenLow | Opcode::SwidenHigh | Opcode::UwidenLow | Opcode::UwidenHigh => {
1093 let new_type = ctrl_ty.merge_lanes().unwrap();
1094 let conv_type = match inst.opcode() {
1095 Opcode::SwidenLow | Opcode::SwidenHigh => {
1096 ValueConversionKind::SignExtend(new_type.lane_type())
1097 }
1098 Opcode::UwidenLow | Opcode::UwidenHigh => {
1099 ValueConversionKind::ZeroExtend(new_type.lane_type())
1100 }
1101 _ => unreachable!(),
1102 };
1103 let vec_iter = extractlanes(&arg(0), ctrl_ty)?.into_iter();
1104 let new_vec = match inst.opcode() {
1105 Opcode::SwidenLow | Opcode::UwidenLow => vec_iter
1106 .take(new_type.lane_count() as usize)
1107 .map(|lane| lane.convert(conv_type.clone()))
1108 .collect::<ValueResult<Vec<_>>>()?,
1109 Opcode::SwidenHigh | Opcode::UwidenHigh => vec_iter
1110 .skip(new_type.lane_count() as usize)
1111 .map(|lane| lane.convert(conv_type.clone()))
1112 .collect::<ValueResult<Vec<_>>>()?,
1113 _ => unreachable!(),
1114 };
1115 assign(vectorizelanes(&new_vec, new_type)?)
1116 }
1117 Opcode::FcvtToUint | Opcode::FcvtToSint => {
1118 if arg(0).is_nan()? {
1120 return Ok(ControlFlow::Trap(CraneliftTrap::User(
1121 TrapCode::BAD_CONVERSION_TO_INTEGER,
1122 )));
1123 }
1124 let x = arg(0).into_float()? as i128;
1125 let is_signed = inst.opcode() == Opcode::FcvtToSint;
1126 let (min, max) = ctrl_ty.bounds(is_signed);
1127 let overflow = if is_signed {
1128 x < (min as i128) || x > (max as i128)
1129 } else {
1130 x < 0 || (x as u128) > max
1131 };
1132 if overflow {
1134 return Ok(ControlFlow::Trap(CraneliftTrap::User(
1135 TrapCode::INTEGER_OVERFLOW,
1136 )));
1137 }
1138 assign(DataValueExt::int(x, ctrl_ty)?)
1140 }
1141 Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => {
1142 let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
1143 let cvt = |x: DataValue| -> ValueResult<DataValue> {
1144 if x.is_nan()? {
1146 DataValue::int(0, ctrl_ty.lane_type())
1147 } else {
1148 let is_signed = inst.opcode() == Opcode::FcvtToSintSat;
1149 let (min, max) = ctrl_ty.bounds(is_signed);
1150 let x = x.into_float()? as i128;
1151 let x = if is_signed {
1152 let x = i128::max(x, min as i128);
1153 let x = i128::min(x, max as i128);
1154 x
1155 } else {
1156 let x = if x < 0 { 0 } else { x };
1157 let x = u128::min(x as u128, max);
1158 x as i128
1159 };
1160
1161 DataValue::int(x, ctrl_ty.lane_type())
1162 }
1163 };
1164
1165 let x = extractlanes(&arg(0), in_ty)?;
1166
1167 assign(vectorizelanes(
1168 &x.into_iter()
1169 .map(cvt)
1170 .collect::<ValueResult<SimdVec<DataValue>>>()?,
1171 ctrl_ty,
1172 )?)
1173 }
1174 Opcode::FcvtFromUint | Opcode::FcvtFromSint => {
1175 let x = extractlanes(
1176 &arg(0),
1177 inst_context.type_of(inst_context.args()[0]).unwrap(),
1178 )?;
1179 let bits = |x: DataValue| -> ValueResult<u64> {
1180 Ok(match ctrl_ty.lane_type() {
1181 types::F32 => (if inst.opcode() == Opcode::FcvtFromUint {
1182 x.into_int_unsigned()? as f32
1183 } else {
1184 x.into_int_signed()? as f32
1185 })
1186 .to_bits() as u64,
1187 types::F64 => (if inst.opcode() == Opcode::FcvtFromUint {
1188 x.into_int_unsigned()? as f64
1189 } else {
1190 x.into_int_signed()? as f64
1191 })
1192 .to_bits(),
1193 _ => unimplemented!("unexpected conversion to {:?}", ctrl_ty.lane_type()),
1194 })
1195 };
1196 assign(vectorizelanes(
1197 &x.into_iter()
1198 .map(|x| DataValue::float(bits(x)?, ctrl_ty.lane_type()))
1199 .collect::<ValueResult<SimdVec<DataValue>>>()?,
1200 ctrl_ty,
1201 )?)
1202 }
1203 Opcode::FvpromoteLow => {
1204 let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
1205 assert_eq!(in_ty, types::F32X4);
1206 let out_ty = types::F64X2;
1207 let x = extractlanes(&arg(0), in_ty)?;
1208 assign(vectorizelanes(
1209 &x[..(out_ty.lane_count() as usize)]
1210 .into_iter()
1211 .map(|x| {
1212 DataValue::convert(
1213 x.to_owned(),
1214 ValueConversionKind::Exact(out_ty.lane_type()),
1215 )
1216 })
1217 .collect::<ValueResult<SimdVec<DataValue>>>()?,
1218 out_ty,
1219 )?)
1220 }
1221 Opcode::Fvdemote => {
1222 let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
1223 assert_eq!(in_ty, types::F64X2);
1224 let out_ty = types::F32X4;
1225 let x = extractlanes(&arg(0), in_ty)?;
1226 let x = &mut x
1227 .into_iter()
1228 .map(|x| {
1229 DataValue::convert(x, ValueConversionKind::RoundNearestEven(out_ty.lane_type()))
1230 })
1231 .collect::<ValueResult<SimdVec<DataValue>>>()?;
1232 for _ in 0..(out_ty.lane_count() as usize - x.len()) {
1234 x.push(DataValue::float(0, out_ty.lane_type())?);
1235 }
1236 assign(vectorizelanes(x, out_ty)?)
1237 }
1238 Opcode::Isplit => assign_multiple(&[
1239 DataValueExt::convert(arg(0), ValueConversionKind::Truncate(types::I64))?,
1240 DataValueExt::convert(arg(0), ValueConversionKind::ExtractUpper(types::I64))?,
1241 ]),
1242 Opcode::Iconcat => assign(DataValueExt::concat(arg(0), arg(1))?),
1243 Opcode::AtomicRmw => {
1244 let op = inst.atomic_rmw_op().unwrap();
1245 let val = arg(1);
1246 let addr = arg(0).into_int_unsigned()? as u64;
1247 let mem_flags = inst.memflags().expect("instruction to have memory flags");
1248 let loaded = Address::try_from(addr)
1249 .and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));
1250 let prev_val = match loaded {
1251 Ok(v) => v,
1252 Err(e) => return Ok(ControlFlow::Trap(memerror_to_trap(e))),
1253 };
1254 let prev_val_to_assign = prev_val.clone();
1255 let replace = match op {
1256 AtomicRmwOp::Xchg => Ok(val),
1257 AtomicRmwOp::Add => DataValueExt::add(prev_val, val),
1258 AtomicRmwOp::Sub => DataValueExt::sub(prev_val, val),
1259 AtomicRmwOp::And => DataValueExt::and(prev_val, val),
1260 AtomicRmwOp::Or => DataValueExt::or(prev_val, val),
1261 AtomicRmwOp::Xor => DataValueExt::xor(prev_val, val),
1262 AtomicRmwOp::Nand => DataValueExt::and(prev_val, val).and_then(DataValue::not),
1263 AtomicRmwOp::Smax => DataValueExt::smax(prev_val, val),
1264 AtomicRmwOp::Smin => DataValueExt::smin(prev_val, val),
1265 AtomicRmwOp::Umax => DataValueExt::umax(val, prev_val),
1266 AtomicRmwOp::Umin => DataValueExt::umin(val, prev_val),
1267 }?;
1268 let stored = Address::try_from(addr)
1269 .and_then(|addr| state.checked_store(addr, replace, mem_flags));
1270 assign_or_memtrap(stored.map(|_| prev_val_to_assign))
1271 }
1272 Opcode::AtomicCas => {
1273 let addr = arg(0).into_int_unsigned()? as u64;
1274 let mem_flags = inst.memflags().expect("instruction to have memory flags");
1275 let loaded = Address::try_from(addr)
1276 .and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));
1277 let loaded_val = match loaded {
1278 Ok(v) => v,
1279 Err(e) => return Ok(ControlFlow::Trap(memerror_to_trap(e))),
1280 };
1281 let expected_val = arg(1);
1282 let val_to_assign = if loaded_val == expected_val {
1283 let val_to_store = arg(2);
1284 Address::try_from(addr)
1285 .and_then(|addr| state.checked_store(addr, val_to_store, mem_flags))
1286 .map(|_| loaded_val)
1287 } else {
1288 Ok(loaded_val)
1289 };
1290 assign_or_memtrap(val_to_assign)
1291 }
1292 Opcode::AtomicLoad => {
1293 let load_ty = inst_context.controlling_type().unwrap();
1294 let addr = arg(0).into_int_unsigned()? as u64;
1295 let mem_flags = inst.memflags().expect("instruction to have memory flags");
1296 assign_or_memtrap(
1298 Address::try_from(addr)
1299 .and_then(|addr| state.checked_load(addr, load_ty, mem_flags)),
1300 )
1301 }
1302 Opcode::AtomicStore => {
1303 let val = arg(0);
1304 let addr = arg(1).into_int_unsigned()? as u64;
1305 let mem_flags = inst.memflags().expect("instruction to have memory flags");
1306 continue_or_memtrap(
1308 Address::try_from(addr).and_then(|addr| state.checked_store(addr, val, mem_flags)),
1309 )
1310 }
1311 Opcode::Fence => {
1312 ControlFlow::Continue
1315 }
1316 Opcode::SqmulRoundSat => {
1317 let lane_type = ctrl_ty.lane_type();
1318 let double_width = ctrl_ty.double_width().unwrap().lane_type();
1319 let arg0 = extractlanes(&arg(0), ctrl_ty)?;
1320 let arg1 = extractlanes(&arg(1), ctrl_ty)?;
1321 let (min, max) = lane_type.bounds(true);
1322 let min: DataValue = DataValueExt::int(min as i128, double_width)?;
1323 let max: DataValue = DataValueExt::int(max as i128, double_width)?;
1324 let new_vec = arg0
1325 .into_iter()
1326 .zip(arg1.into_iter())
1327 .map(|(x, y)| {
1328 let x = x.into_int_signed()?;
1329 let y = y.into_int_signed()?;
1330 let z: DataValue = DataValueExt::int(
1332 (x * y + (1 << (lane_type.bits() - 2))) >> (lane_type.bits() - 1),
1333 double_width,
1334 )?;
1335 let z = DataValueExt::smin(z, max.clone())?;
1337 let z = DataValueExt::smax(z, min.clone())?;
1338 let z = z.convert(ValueConversionKind::Truncate(lane_type))?;
1339 Ok(z)
1340 })
1341 .collect::<ValueResult<SimdVec<_>>>()?;
1342 assign(vectorizelanes(&new_vec, ctrl_ty)?)
1343 }
1344 Opcode::IaddPairwise => {
1345 assign(binary_pairwise(arg(0), arg(1), ctrl_ty, DataValueExt::add)?)
1346 }
1347 Opcode::ExtractVector => {
1348 unimplemented!("ExtractVector not supported");
1349 }
1350 Opcode::GetFramePointer => unimplemented!("GetFramePointer"),
1351 Opcode::GetStackPointer => unimplemented!("GetStackPointer"),
1352 Opcode::GetReturnAddress => unimplemented!("GetReturnAddress"),
1353 Opcode::X86Pshufb => unimplemented!("X86Pshufb"),
1354 Opcode::Blendv => unimplemented!("Blendv"),
1355 Opcode::X86Pmulhrsw => unimplemented!("X86Pmulhrsw"),
1356 Opcode::X86Pmaddubsw => unimplemented!("X86Pmaddubsw"),
1357 Opcode::X86Cvtt2dq => unimplemented!("X86Cvtt2dq"),
1358 Opcode::StackSwitch => unimplemented!("StackSwitch"),
1359
1360 Opcode::TryCall => unimplemented!("TryCall"),
1361 Opcode::TryCallIndirect => unimplemented!("TryCallIndirect"),
1362
1363 Opcode::GetExceptionHandlerAddress => unimplemented!("GetExceptionHandlerAddress"),
1364
1365 Opcode::SequencePoint => unimplemented!("SequencePoint"),
1366 })
1367}
1368
1369#[derive(Error, Debug)]
1370pub enum StepError {
1371 #[error("unable to retrieve value from SSA reference: {0}")]
1372 UnknownValue(ValueRef),
1373 #[error("unable to find the following function: {0}")]
1374 UnknownFunction(FuncRef),
1375 #[error("cannot step with these values")]
1376 ValueError(#[from] ValueError),
1377 #[error("failed to access memory")]
1378 MemoryError(#[from] MemoryError),
1379}
1380
1381#[derive(Debug, PartialEq)]
1384pub enum ControlFlow<'a> {
1385 Assign(SmallVec<[DataValue; 1]>),
1388 Continue,
1391 ContinueAt(Block, SmallVec<[DataValue; 1]>),
1396 Call(&'a Function, SmallVec<[DataValue; 1]>),
1398 ReturnCall(&'a Function, SmallVec<[DataValue; 1]>),
1400 Return(SmallVec<[DataValue; 1]>),
1402 Trap(CraneliftTrap),
1405}
1406
1407#[derive(Error, Debug, PartialEq, Eq, Hash)]
1408pub enum CraneliftTrap {
1409 #[error("user code: {0}")]
1410 User(TrapCode),
1411 #[error("bad signature")]
1412 BadSignature,
1413 #[error("unreachable code has been reached")]
1414 UnreachableCodeReached,
1415 #[error("heap is misaligned")]
1416 HeapMisaligned,
1417 #[error("user debug")]
1418 Debug,
1419}
1420
1421fn icmp(
1423 ctrl_ty: types::Type,
1424 code: IntCC,
1425 left: &DataValue,
1426 right: &DataValue,
1427) -> ValueResult<DataValue> {
1428 let cmp = |bool_ty: types::Type,
1429 code: IntCC,
1430 left: &DataValue,
1431 right: &DataValue|
1432 -> ValueResult<DataValue> {
1433 Ok(DataValueExt::bool(
1434 match code {
1435 IntCC::Equal => left == right,
1436 IntCC::NotEqual => left != right,
1437 IntCC::SignedGreaterThan => left > right,
1438 IntCC::SignedGreaterThanOrEqual => left >= right,
1439 IntCC::SignedLessThan => left < right,
1440 IntCC::SignedLessThanOrEqual => left <= right,
1441 IntCC::UnsignedGreaterThan => {
1442 left.clone().into_int_unsigned()? > right.clone().into_int_unsigned()?
1443 }
1444 IntCC::UnsignedGreaterThanOrEqual => {
1445 left.clone().into_int_unsigned()? >= right.clone().into_int_unsigned()?
1446 }
1447 IntCC::UnsignedLessThan => {
1448 left.clone().into_int_unsigned()? < right.clone().into_int_unsigned()?
1449 }
1450 IntCC::UnsignedLessThanOrEqual => {
1451 left.clone().into_int_unsigned()? <= right.clone().into_int_unsigned()?
1452 }
1453 },
1454 ctrl_ty.is_vector(),
1455 bool_ty,
1456 )?)
1457 };
1458
1459 let dst_ty = ctrl_ty.as_truthy();
1460 let left = extractlanes(left, ctrl_ty)?;
1461 let right = extractlanes(right, ctrl_ty)?;
1462
1463 let res = left
1464 .into_iter()
1465 .zip(right.into_iter())
1466 .map(|(l, r)| cmp(dst_ty.lane_type(), code, &l, &r))
1467 .collect::<ValueResult<SimdVec<DataValue>>>()?;
1468
1469 Ok(vectorizelanes(&res, dst_ty)?)
1470}
1471
1472fn fcmp(code: FloatCC, left: &DataValue, right: &DataValue) -> ValueResult<bool> {
1474 Ok(match code {
1475 FloatCC::Ordered => left == right || left < right || left > right,
1476 FloatCC::Unordered => DataValueExt::uno(left, right)?,
1477 FloatCC::Equal => left == right,
1478 FloatCC::NotEqual => left < right || left > right || DataValueExt::uno(left, right)?,
1479 FloatCC::OrderedNotEqual => left < right || left > right,
1480 FloatCC::UnorderedOrEqual => left == right || DataValueExt::uno(left, right)?,
1481 FloatCC::LessThan => left < right,
1482 FloatCC::LessThanOrEqual => left <= right,
1483 FloatCC::GreaterThan => left > right,
1484 FloatCC::GreaterThanOrEqual => left >= right,
1485 FloatCC::UnorderedOrLessThan => DataValueExt::uno(left, right)? || left < right,
1486 FloatCC::UnorderedOrLessThanOrEqual => DataValueExt::uno(left, right)? || left <= right,
1487 FloatCC::UnorderedOrGreaterThan => DataValueExt::uno(left, right)? || left > right,
1488 FloatCC::UnorderedOrGreaterThanOrEqual => DataValueExt::uno(left, right)? || left >= right,
1489 })
1490}
1491
1492pub type SimdVec<DataValue> = SmallVec<[DataValue; 4]>;
1493
1494pub(crate) fn extractlanes(
1497 x: &DataValue,
1498 vector_type: types::Type,
1499) -> ValueResult<SimdVec<DataValue>> {
1500 let lane_type = vector_type.lane_type();
1501 let mut lanes = SimdVec::new();
1502 if !x.ty().is_vector() {
1504 lanes.push(x.clone());
1505 return Ok(lanes);
1506 }
1507
1508 let iterations = match lane_type {
1509 types::I8 => 1,
1510 types::I16 | types::F16 => 2,
1511 types::I32 | types::F32 => 4,
1512 types::I64 | types::F64 => 8,
1513 _ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),
1514 };
1515
1516 let x = x.into_array()?;
1517 for i in 0..vector_type.lane_count() {
1518 let mut lane: i128 = 0;
1519 for j in 0..iterations {
1520 lane += (x[((i * iterations) + j) as usize] as i128) << (8 * j);
1521 }
1522
1523 let lane_val: DataValue = if lane_type.is_float() {
1524 DataValueExt::float(lane as u64, lane_type)?
1525 } else {
1526 DataValueExt::int(lane, lane_type)?
1527 };
1528 lanes.push(lane_val);
1529 }
1530 return Ok(lanes);
1531}
1532
1533fn vectorizelanes(x: &[DataValue], vector_type: types::Type) -> ValueResult<DataValue> {
1536 if x.len() == 1 {
1538 Ok(x[0].clone())
1539 } else {
1540 vectorizelanes_all(x, vector_type)
1541 }
1542}
1543
1544fn vectorizelanes_all(x: &[DataValue], vector_type: types::Type) -> ValueResult<DataValue> {
1546 let lane_type = vector_type.lane_type();
1547 let iterations = match lane_type {
1548 types::I8 => 1,
1549 types::I16 | types::F16 => 2,
1550 types::I32 | types::F32 => 4,
1551 types::I64 | types::F64 => 8,
1552 _ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),
1553 };
1554 let mut result: [u8; 16] = [0; 16];
1555 for (i, val) in x.iter().enumerate() {
1556 let lane_val: i128 = val
1557 .clone()
1558 .convert(ValueConversionKind::Exact(lane_type.as_int()))?
1559 .into_int_unsigned()? as i128;
1560
1561 for j in 0..iterations {
1562 result[(i * iterations) + j] = (lane_val >> (8 * j)) as u8;
1563 }
1564 }
1565 DataValueExt::vector(result, vector_type)
1566}
1567
1568fn fold_vector<F>(v: DataValue, ty: types::Type, init: DataValue, op: F) -> ValueResult<DataValue>
1570where
1571 F: FnMut(DataValue, DataValue) -> ValueResult<DataValue>,
1572{
1573 extractlanes(&v, ty)?.into_iter().try_fold(init, op)
1574}
1575
1576fn unary_arith<F>(x: DataValue, vector_type: types::Type, op: F) -> ValueResult<DataValue>
1578where
1579 F: Fn(DataValue) -> ValueResult<DataValue>,
1580{
1581 let arg = extractlanes(&x, vector_type)?;
1582
1583 let result = arg
1584 .into_iter()
1585 .map(|arg| Ok(op(arg)?))
1586 .collect::<ValueResult<SimdVec<DataValue>>>()?;
1587
1588 vectorizelanes(&result, vector_type)
1589}
1590
1591fn binary_arith<F>(
1593 x: DataValue,
1594 y: DataValue,
1595 vector_type: types::Type,
1596 op: F,
1597) -> ValueResult<DataValue>
1598where
1599 F: Fn(DataValue, DataValue) -> ValueResult<DataValue>,
1600{
1601 let arg0 = extractlanes(&x, vector_type)?;
1602 let arg1 = extractlanes(&y, vector_type)?;
1603
1604 let result = arg0
1605 .into_iter()
1606 .zip(arg1)
1607 .map(|(lhs, rhs)| Ok(op(lhs, rhs)?))
1608 .collect::<ValueResult<SimdVec<DataValue>>>()?;
1609
1610 vectorizelanes(&result, vector_type)
1611}
1612
1613fn binary_pairwise<F>(
1617 x: DataValue,
1618 y: DataValue,
1619 vector_type: types::Type,
1620 op: F,
1621) -> ValueResult<DataValue>
1622where
1623 F: Fn(DataValue, DataValue) -> ValueResult<DataValue>,
1624{
1625 let arg0 = extractlanes(&x, vector_type)?;
1626 let arg1 = extractlanes(&y, vector_type)?;
1627
1628 let result = arg0
1629 .chunks(2)
1630 .chain(arg1.chunks(2))
1631 .map(|pair| op(pair[0].clone(), pair[1].clone()))
1632 .collect::<ValueResult<SimdVec<DataValue>>>()?;
1633
1634 vectorizelanes(&result, vector_type)
1635}
1636
1637fn bitselect(c: DataValue, x: DataValue, y: DataValue) -> ValueResult<DataValue> {
1638 let mask_x = DataValueExt::and(c.clone(), x)?;
1639 let mask_y = DataValueExt::and(DataValueExt::not(c)?, y)?;
1640 DataValueExt::or(mask_x, mask_y)
1641}
1642
1643fn splat(ty: Type, val: DataValue) -> ValueResult<DataValue> {
1644 let mut new_vector = SimdVec::new();
1645 for _ in 0..ty.lane_count() {
1646 new_vector.push(val.clone());
1647 }
1648 vectorizelanes(&new_vector, ty)
1649}
1650
1651fn shift_amt(ty: Type, val: DataValue) -> ValueResult<DataValue> {
1654 splat(ty, val.convert(ValueConversionKind::Exact(ty.lane_type()))?)
1655}