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