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