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