Path: blob/main/cranelift/interpreter/src/step.rs
1692 views
//! The [step] function interprets a single Cranelift instruction given its [State] and1//! [InstructionContext].2use crate::address::{Address, AddressSize};3use crate::frame::Frame;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::{10AbiParam, AtomicRmwOp, Block, BlockArg, BlockCall, Endianness, ExternalName, FuncRef, Function,11InstructionData, MemFlags, Opcode, TrapCode, Type, Value as ValueRef, types,12};13use log::trace;14use smallvec::{SmallVec, smallvec};15use std::fmt::Debug;16use std::ops::RangeFrom;17use thiserror::Error;1819/// Ensures that all types in args are the same as expected by the signature20fn validate_signature_params(sig: &[AbiParam], args: &[DataValue]) -> bool {21args.iter()22.map(|r| r.ty())23.zip(sig.iter().map(|r| r.value_type))24.all(|(a, b)| match (a, b) {25// For these two cases we don't have precise type information for `a`.26// We don't distinguish between different bool types, or different vector types27// The actual error is in `Value::ty` that returns default types for some values28// but we don't have enough information there either.29//30// Ideally the user has run the verifier and caught this properly...31(a, b) if a.is_vector() && b.is_vector() => true,32(a, b) => a == b,33})34}3536// Helper for summing a sequence of values.37fn sum_unsigned(head: DataValue, tail: SmallVec<[DataValue; 1]>) -> ValueResult<u128> {38let mut acc = head;39for t in tail {40acc = DataValueExt::add(acc, t)?;41}42acc.into_int_unsigned()43}4445/// Collect a list of block arguments.46fn collect_block_args(47frame: &Frame,48args: impl Iterator<Item = BlockArg>,49) -> SmallVec<[DataValue; 1]> {50args.into_iter()51.map(|n| match n {52BlockArg::Value(n) => frame.get(n).clone(),53_ => panic!("exceptions not supported"),54})55.collect()56}5758/// Interpret a single Cranelift instruction. Note that program traps and interpreter errors are59/// distinct: a program trap results in `Ok(Flow::Trap(...))` whereas an interpretation error (e.g.60/// the types of two values are incompatible) results in `Err(...)`.61pub fn step<'a, I>(state: &mut dyn State<'a>, inst_context: I) -> Result<ControlFlow<'a>, StepError>62where63I: InstructionContext,64{65let inst = inst_context.data();66let ctrl_ty = inst_context.controlling_type().unwrap();67trace!(68"Step: {}{}",69inst.opcode(),70if ctrl_ty.is_invalid() {71String::new()72} else {73format!(".{ctrl_ty}")74}75);7677// The following closures make the `step` implementation much easier to express. Note that they78// frequently close over the `state` or `inst_context` for brevity.7980// Retrieve the current value for an instruction argument.81let arg = |index: usize| -> DataValue {82let value_ref = inst_context.args()[index];83state.current_frame().get(value_ref).clone()84};8586// Retrieve the current values for all of an instruction's arguments.87let args = || -> SmallVec<[DataValue; 1]> { state.collect_values(inst_context.args()) };8889// Retrieve the current values for a range of an instruction's arguments.90let args_range = |indexes: RangeFrom<usize>| -> Result<SmallVec<[DataValue; 1]>, StepError> {91Ok(SmallVec::<[DataValue; 1]>::from(&args()[indexes]))92};9394// Retrieve the immediate value for an instruction, expecting it to exist.95let imm = || -> DataValue {96match inst {97InstructionData::UnaryConst {98constant_handle,99opcode,100} => {101let buffer = state102.get_current_function()103.dfg104.constants105.get(constant_handle);106match (ctrl_ty.bytes(), opcode) {107(_, Opcode::F128const) => {108DataValue::F128(buffer.try_into().expect("a 16-byte data buffer"))109}110(16, Opcode::Vconst) => DataValue::V128(111buffer.as_slice().try_into().expect("a 16-byte data buffer"),112),113(8, Opcode::Vconst) => {114DataValue::V64(buffer.as_slice().try_into().expect("an 8-byte data buffer"))115}116(4, Opcode::Vconst) => {117DataValue::V32(buffer.as_slice().try_into().expect("a 4-byte data buffer"))118}119(2, Opcode::Vconst) => {120DataValue::V16(buffer.as_slice().try_into().expect("a 2-byte data buffer"))121}122(length, opcode) => panic!(123"unexpected UnaryConst controlling type size {length} for opcode {opcode:?}"124),125}126}127InstructionData::Shuffle { imm, .. } => {128let mask = state129.get_current_function()130.dfg131.immediates132.get(imm)133.unwrap()134.as_slice();135match mask.len() {13616 => DataValue::V128(mask.try_into().expect("a 16-byte vector mask")),1378 => DataValue::V64(mask.try_into().expect("an 8-byte vector mask")),1384 => DataValue::V32(mask.try_into().expect("a 4-byte vector mask")),1392 => DataValue::V16(mask.try_into().expect("a 2-byte vector mask")),140length => panic!("unexpected Shuffle mask length {length}"),141}142}143// 8-bit.144InstructionData::BinaryImm8 { imm, .. } | InstructionData::TernaryImm8 { imm, .. } => {145DataValue::from(imm as i8) // Note the switch from unsigned to signed.146}147// 16-bit148InstructionData::UnaryIeee16 { imm, .. } => DataValue::from(imm),149// 32-bit150InstructionData::UnaryIeee32 { imm, .. } => DataValue::from(imm),151InstructionData::Load { offset, .. }152| InstructionData::Store { offset, .. }153| InstructionData::StackLoad { offset, .. }154| InstructionData::StackStore { offset, .. } => DataValue::from(offset),155// 64-bit.156InstructionData::UnaryImm { imm, .. }157| InstructionData::BinaryImm64 { imm, .. }158| InstructionData::IntCompareImm { imm, .. } => DataValue::from(imm.bits()),159InstructionData::UnaryIeee64 { imm, .. } => DataValue::from(imm),160_ => unreachable!(),161}162};163164// Retrieve the immediate value for an instruction and convert it to the controlling type of the165// instruction. For example, since `InstructionData` stores all integer immediates in a 64-bit166// size, this will attempt to convert `iconst.i8 ...` to an 8-bit size.167let imm_as_ctrl_ty = || -> Result<DataValue, ValueError> {168DataValue::convert(imm(), ValueConversionKind::Exact(ctrl_ty))169};170171// Indicate that the result of a step is to assign a single value to an instruction's results.172let assign = |value: DataValue| ControlFlow::Assign(smallvec![value]);173174// Indicate that the result of a step is to assign multiple values to an instruction's results.175let assign_multiple = |values: &[DataValue]| ControlFlow::Assign(SmallVec::from(values));176177// Similar to `assign` but converts some errors into traps178let assign_or_trap = |value: ValueResult<DataValue>| match value {179Ok(v) => Ok(assign(v)),180Err(ValueError::IntegerDivisionByZero) => Ok(ControlFlow::Trap(CraneliftTrap::User(181TrapCode::INTEGER_DIVISION_BY_ZERO,182))),183Err(ValueError::IntegerOverflow) => Ok(ControlFlow::Trap(CraneliftTrap::User(184TrapCode::INTEGER_OVERFLOW,185))),186Err(e) => Err(e),187};188189let memerror_to_trap = |e: MemoryError| match e {190MemoryError::InvalidAddress(_)191| MemoryError::InvalidAddressType(_)192| MemoryError::InvalidOffset { .. }193| MemoryError::InvalidEntry { .. } => CraneliftTrap::User(TrapCode::HEAP_OUT_OF_BOUNDS),194MemoryError::OutOfBoundsStore { mem_flags, .. }195| MemoryError::OutOfBoundsLoad { mem_flags, .. } => CraneliftTrap::User(196mem_flags197.trap_code()198.expect("op with notrap flag should not trap"),199),200MemoryError::MisalignedLoad { .. } => CraneliftTrap::HeapMisaligned,201MemoryError::MisalignedStore { .. } => CraneliftTrap::HeapMisaligned,202};203204// Assigns or traps depending on the value of the result205let assign_or_memtrap = |res| match res {206Ok(v) => assign(v),207Err(e) => ControlFlow::Trap(memerror_to_trap(e)),208};209210// Continues or traps depending on the value of the result211let continue_or_memtrap = |res| match res {212Ok(_) => ControlFlow::Continue,213Err(e) => ControlFlow::Trap(memerror_to_trap(e)),214};215216let calculate_addr =217|addr_ty: Type, imm: DataValue, args: SmallVec<[DataValue; 1]>| -> ValueResult<u64> {218let imm = imm.convert(ValueConversionKind::ZeroExtend(addr_ty))?;219let args = args220.into_iter()221.map(|v| v.convert(ValueConversionKind::ZeroExtend(addr_ty)))222.collect::<ValueResult<SmallVec<[DataValue; 1]>>>()?;223224Ok(sum_unsigned(imm, args)? as u64)225};226227// Interpret a unary instruction with the given `op`, assigning the resulting value to the228// instruction's results.229let unary =230|op: fn(DataValue) -> ValueResult<DataValue>, arg: DataValue| -> ValueResult<ControlFlow> {231let ctrl_ty = inst_context.controlling_type().unwrap();232let res = unary_arith(arg, ctrl_ty, op)?;233Ok(assign(res))234};235236// Interpret a binary instruction with the given `op`, assigning the resulting value to the237// instruction's results.238let binary = |op: fn(DataValue, DataValue) -> ValueResult<DataValue>,239left: DataValue,240right: DataValue|241-> ValueResult<ControlFlow> {242let ctrl_ty = inst_context.controlling_type().unwrap();243let res = binary_arith(left, right, ctrl_ty, op)?;244Ok(assign(res))245};246247// Similar to `binary` but converts select `ValueError`'s into trap `ControlFlow`'s248let binary_can_trap = |op: fn(DataValue, DataValue) -> ValueResult<DataValue>,249left: DataValue,250right: DataValue|251-> ValueResult<ControlFlow> {252let ctrl_ty = inst_context.controlling_type().unwrap();253let res = binary_arith(left, right, ctrl_ty, op);254assign_or_trap(res)255};256257// Choose whether to assign `left` or `right` to the instruction's result based on a `condition`.258let choose = |condition: bool, left: DataValue, right: DataValue| -> ControlFlow {259assign(if condition { left } else { right })260};261262// Retrieve an instruction's branch destination; expects the instruction to be a branch.263264let continue_at = |block: BlockCall| {265let branch_args = collect_block_args(266state.current_frame(),267block.args(&state.get_current_function().dfg.value_lists),268);269Ok(ControlFlow::ContinueAt(270block.block(&state.get_current_function().dfg.value_lists),271branch_args,272))273};274275// Based on `condition`, indicate where to continue the control flow.276#[expect(unused_variables, reason = "here in case it's needed in the future")]277let branch_when = |condition: bool, block| -> Result<ControlFlow, StepError> {278if condition {279continue_at(block)280} else {281Ok(ControlFlow::Continue)282}283};284285// Retrieve an instruction's trap code; expects the instruction to be a trap.286let trap_code = || -> TrapCode { inst.trap_code().unwrap() };287288// Based on `condition`, either trap or not.289let trap_when = |condition: bool, trap: CraneliftTrap| -> ControlFlow {290if condition {291ControlFlow::Trap(trap)292} else {293ControlFlow::Continue294}295};296297// Calls a function reference with the given arguments.298let call_func =299|func_ref: InterpreterFunctionRef<'a>,300args: SmallVec<[DataValue; 1]>,301make_ctrl_flow: fn(&'a Function, SmallVec<[DataValue; 1]>) -> ControlFlow<'a>|302-> Result<ControlFlow<'a>, StepError> {303let signature = func_ref.signature();304305// Check the types of the arguments. This is usually done by the verifier, but nothing306// guarantees that the user has ran that.307let args_match = validate_signature_params(&signature.params[..], &args[..]);308if !args_match {309return Ok(ControlFlow::Trap(CraneliftTrap::BadSignature));310}311312Ok(match func_ref {313InterpreterFunctionRef::Function(func) => make_ctrl_flow(func, args),314InterpreterFunctionRef::LibCall(libcall) => {315debug_assert!(316!matches!(317inst.opcode(),318Opcode::ReturnCall | Opcode::ReturnCallIndirect,319),320"Cannot tail call to libcalls"321);322let libcall_handler = state.get_libcall_handler();323324// We don't transfer control to a libcall, we just execute it and return the results325let res = libcall_handler(libcall, args);326let res = match res {327Err(trap) => return Ok(ControlFlow::Trap(trap)),328Ok(rets) => rets,329};330331// Check that what the handler returned is what we expect.332if validate_signature_params(&signature.returns[..], &res[..]) {333ControlFlow::Assign(res)334} else {335ControlFlow::Trap(CraneliftTrap::BadSignature)336}337}338})339};340341// Interpret a Cranelift instruction.342Ok(match inst.opcode() {343Opcode::Jump => {344if let InstructionData::Jump { destination, .. } = inst {345continue_at(destination)?346} else {347unreachable!()348}349}350Opcode::Brif => {351if let InstructionData::Brif {352arg,353blocks: [block_then, block_else],354..355} = inst356{357let arg = state.current_frame().get(arg).clone();358359let condition = arg.convert(ValueConversionKind::ToBoolean)?.into_bool()?;360361if condition {362continue_at(block_then)?363} else {364continue_at(block_else)?365}366} else {367unreachable!()368}369}370Opcode::BrTable => {371if let InstructionData::BranchTable { table, .. } = inst {372let jt_data = &state.get_current_function().stencil.dfg.jump_tables[table];373374// Convert to usize to remove negative indexes from the following operations375let jump_target = usize::try_from(arg(0).into_int_unsigned()?)376.ok()377.and_then(|i| jt_data.as_slice().get(i))378.copied()379.unwrap_or(jt_data.default_block());380381continue_at(jump_target)?382} else {383unreachable!()384}385}386Opcode::Trap => ControlFlow::Trap(CraneliftTrap::User(trap_code())),387Opcode::Debugtrap => ControlFlow::Trap(CraneliftTrap::Debug),388Opcode::Trapz => trap_when(!arg(0).into_bool()?, CraneliftTrap::User(trap_code())),389Opcode::Trapnz => trap_when(arg(0).into_bool()?, CraneliftTrap::User(trap_code())),390Opcode::Return => ControlFlow::Return(args()),391Opcode::Call | Opcode::ReturnCall => {392let func_ref = if let InstructionData::Call { func_ref, .. } = inst {393func_ref394} else {395unreachable!()396};397398let curr_func = state.get_current_function();399let ext_data = curr_func400.dfg401.ext_funcs402.get(func_ref)403.ok_or(StepError::UnknownFunction(func_ref))?;404405let args = args();406let func = match ext_data.name {407// These functions should be registered in the regular function store408ExternalName::User(_) | ExternalName::TestCase(_) => {409let function = state410.get_function(func_ref)411.ok_or(StepError::UnknownFunction(func_ref))?;412InterpreterFunctionRef::Function(function)413}414ExternalName::LibCall(libcall) => InterpreterFunctionRef::LibCall(libcall),415ExternalName::KnownSymbol(_) => unimplemented!(),416};417418let make_control_flow = match inst.opcode() {419Opcode::Call => ControlFlow::Call,420Opcode::ReturnCall => ControlFlow::ReturnCall,421_ => unreachable!(),422};423424call_func(func, args, make_control_flow)?425}426Opcode::CallIndirect | Opcode::ReturnCallIndirect => {427let args = args();428let addr_dv = DataValue::I64(arg(0).into_int_unsigned()? as i64);429let addr = Address::try_from(addr_dv.clone()).map_err(StepError::MemoryError)?;430431let func = state432.get_function_from_address(addr)433.ok_or_else(|| StepError::MemoryError(MemoryError::InvalidAddress(addr_dv)))?;434435let call_args: SmallVec<[DataValue; 1]> = SmallVec::from(&args[1..]);436437let make_control_flow = match inst.opcode() {438Opcode::CallIndirect => ControlFlow::Call,439Opcode::ReturnCallIndirect => ControlFlow::ReturnCall,440_ => unreachable!(),441};442443call_func(func, call_args, make_control_flow)?444}445Opcode::FuncAddr => {446let func_ref = if let InstructionData::FuncAddr { func_ref, .. } = inst {447func_ref448} else {449unreachable!()450};451452let ext_data = state453.get_current_function()454.dfg455.ext_funcs456.get(func_ref)457.ok_or(StepError::UnknownFunction(func_ref))?;458459let addr_ty = inst_context.controlling_type().unwrap();460assign_or_memtrap({461AddressSize::try_from(addr_ty).and_then(|addr_size| {462let addr = state.function_address(addr_size, &ext_data.name)?;463let dv = DataValue::try_from(addr)?;464Ok(dv)465})466})467}468Opcode::Load469| Opcode::Uload8470| Opcode::Sload8471| Opcode::Uload16472| Opcode::Sload16473| Opcode::Uload32474| Opcode::Sload32475| Opcode::Uload8x8476| Opcode::Sload8x8477| Opcode::Uload16x4478| Opcode::Sload16x4479| Opcode::Uload32x2480| Opcode::Sload32x2 => {481let ctrl_ty = inst_context.controlling_type().unwrap();482let (load_ty, kind) = match inst.opcode() {483Opcode::Load => (ctrl_ty, None),484Opcode::Uload8 => (types::I8, Some(ValueConversionKind::ZeroExtend(ctrl_ty))),485Opcode::Sload8 => (types::I8, Some(ValueConversionKind::SignExtend(ctrl_ty))),486Opcode::Uload16 => (types::I16, Some(ValueConversionKind::ZeroExtend(ctrl_ty))),487Opcode::Sload16 => (types::I16, Some(ValueConversionKind::SignExtend(ctrl_ty))),488Opcode::Uload32 => (types::I32, Some(ValueConversionKind::ZeroExtend(ctrl_ty))),489Opcode::Sload32 => (types::I32, Some(ValueConversionKind::SignExtend(ctrl_ty))),490Opcode::Uload8x8491| Opcode::Sload8x8492| Opcode::Uload16x4493| Opcode::Sload16x4494| Opcode::Uload32x2495| Opcode::Sload32x2 => unimplemented!(),496_ => unreachable!(),497};498499let addr_value = calculate_addr(types::I64, imm(), args())?;500let mem_flags = inst.memflags().expect("instruction to have memory flags");501let loaded = assign_or_memtrap(502Address::try_from(addr_value)503.and_then(|addr| state.checked_load(addr, load_ty, mem_flags)),504);505506match (loaded, kind) {507(ControlFlow::Assign(ret), Some(c)) => ControlFlow::Assign(508ret.into_iter()509.map(|loaded| loaded.convert(c.clone()))510.collect::<ValueResult<SmallVec<[DataValue; 1]>>>()?,511),512(cf, _) => cf,513}514}515Opcode::Store | Opcode::Istore8 | Opcode::Istore16 | Opcode::Istore32 => {516let kind = match inst.opcode() {517Opcode::Store => None,518Opcode::Istore8 => Some(ValueConversionKind::Truncate(types::I8)),519Opcode::Istore16 => Some(ValueConversionKind::Truncate(types::I16)),520Opcode::Istore32 => Some(ValueConversionKind::Truncate(types::I32)),521_ => unreachable!(),522};523524let addr_value = calculate_addr(types::I64, imm(), args_range(1..)?)?;525let mem_flags = inst.memflags().expect("instruction to have memory flags");526let reduced = if let Some(c) = kind {527arg(0).convert(c)?528} else {529arg(0)530};531continue_or_memtrap(532Address::try_from(addr_value)533.and_then(|addr| state.checked_store(addr, reduced, mem_flags)),534)535}536Opcode::StackLoad => {537let load_ty = inst_context.controlling_type().unwrap();538let slot = inst.stack_slot().unwrap();539let offset = sum_unsigned(imm(), args())? as u64;540let mem_flags = MemFlags::new();541assign_or_memtrap({542state543.stack_address(AddressSize::_64, slot, offset)544.and_then(|addr| state.checked_load(addr, load_ty, mem_flags))545})546}547Opcode::StackStore => {548let arg = arg(0);549let slot = inst.stack_slot().unwrap();550let offset = sum_unsigned(imm(), args_range(1..)?)? as u64;551let mem_flags = MemFlags::new();552continue_or_memtrap({553state554.stack_address(AddressSize::_64, slot, offset)555.and_then(|addr| state.checked_store(addr, arg, mem_flags))556})557}558Opcode::StackAddr => {559let load_ty = inst_context.controlling_type().unwrap();560let slot = inst.stack_slot().unwrap();561let offset = sum_unsigned(imm(), args())? as u64;562assign_or_memtrap({563AddressSize::try_from(load_ty).and_then(|addr_size| {564let addr = state.stack_address(addr_size, slot, offset)?;565let dv = DataValue::try_from(addr)?;566Ok(dv)567})568})569}570Opcode::DynamicStackAddr => unimplemented!("DynamicStackSlot"),571Opcode::DynamicStackLoad => unimplemented!("DynamicStackLoad"),572Opcode::DynamicStackStore => unimplemented!("DynamicStackStore"),573Opcode::GlobalValue | Opcode::SymbolValue | Opcode::TlsValue => {574if let InstructionData::UnaryGlobalValue { global_value, .. } = inst {575assign_or_memtrap(state.resolve_global_value(global_value))576} else {577unreachable!()578}579}580Opcode::GetPinnedReg => assign(state.get_pinned_reg()),581Opcode::SetPinnedReg => {582let arg0 = arg(0);583state.set_pinned_reg(arg0);584ControlFlow::Continue585}586Opcode::Iconst => assign(DataValueExt::int(imm().into_int_signed()?, ctrl_ty)?),587Opcode::F16const => assign(imm()),588Opcode::F32const => assign(imm()),589Opcode::F64const => assign(imm()),590Opcode::F128const => assign(imm()),591Opcode::Vconst => assign(imm()),592Opcode::Nop => ControlFlow::Continue,593Opcode::Select | Opcode::SelectSpectreGuard => choose(arg(0).into_bool()?, arg(1), arg(2)),594Opcode::Bitselect => assign(bitselect(arg(0), arg(1), arg(2))?),595Opcode::Icmp => assign(icmp(ctrl_ty, inst.cond_code().unwrap(), &arg(0), &arg(1))?),596Opcode::IcmpImm => assign(icmp(597ctrl_ty,598inst.cond_code().unwrap(),599&arg(0),600&imm_as_ctrl_ty()?,601)?),602Opcode::Smin => {603if ctrl_ty.is_vector() {604let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(1), &arg(0))?;605assign(bitselect(icmp, arg(0), arg(1))?)606} else {607assign(arg(0).smin(arg(1))?)608}609}610Opcode::Umin => {611if ctrl_ty.is_vector() {612let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(1), &arg(0))?;613assign(bitselect(icmp, arg(0), arg(1))?)614} else {615assign(arg(0).umin(arg(1))?)616}617}618Opcode::Smax => {619if ctrl_ty.is_vector() {620let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(0), &arg(1))?;621assign(bitselect(icmp, arg(0), arg(1))?)622} else {623assign(arg(0).smax(arg(1))?)624}625}626Opcode::Umax => {627if ctrl_ty.is_vector() {628let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(0), &arg(1))?;629assign(bitselect(icmp, arg(0), arg(1))?)630} else {631assign(arg(0).umax(arg(1))?)632}633}634Opcode::AvgRound => {635let sum = DataValueExt::add(arg(0), arg(1))?;636let one = DataValueExt::int(1, arg(0).ty())?;637let inc = DataValueExt::add(sum, one)?;638let two = DataValueExt::int(2, arg(0).ty())?;639binary(DataValueExt::udiv, inc, two)?640}641Opcode::Iadd => binary(DataValueExt::add, arg(0), arg(1))?,642Opcode::UaddSat => assign(binary_arith(643arg(0),644arg(1),645ctrl_ty,646DataValueExt::uadd_sat,647)?),648Opcode::SaddSat => assign(binary_arith(649arg(0),650arg(1),651ctrl_ty,652DataValueExt::sadd_sat,653)?),654Opcode::Isub => binary(DataValueExt::sub, arg(0), arg(1))?,655Opcode::UsubSat => assign(binary_arith(656arg(0),657arg(1),658ctrl_ty,659DataValueExt::usub_sat,660)?),661Opcode::SsubSat => assign(binary_arith(662arg(0),663arg(1),664ctrl_ty,665DataValueExt::ssub_sat,666)?),667Opcode::Ineg => binary(DataValueExt::sub, DataValueExt::int(0, ctrl_ty)?, arg(0))?,668Opcode::Iabs => {669let (min_val, _) = ctrl_ty.lane_type().bounds(true);670let min_val: DataValue = DataValueExt::int(min_val as i128, ctrl_ty.lane_type())?;671let arg0 = extractlanes(&arg(0), ctrl_ty)?;672let new_vec = arg0673.into_iter()674.map(|lane| {675if lane == min_val {676Ok(min_val.clone())677} else {678DataValueExt::int(lane.into_int_signed()?.abs(), ctrl_ty.lane_type())679}680})681.collect::<ValueResult<SimdVec<DataValue>>>()?;682assign(vectorizelanes(&new_vec, ctrl_ty)?)683}684Opcode::Imul => binary(DataValueExt::mul, arg(0), arg(1))?,685Opcode::Umulhi | Opcode::Smulhi => {686let double_length = match ctrl_ty.lane_bits() {6878 => types::I16,68816 => types::I32,68932 => types::I64,69064 => types::I128,691_ => unimplemented!("Unsupported integer length {}", ctrl_ty.bits()),692};693let conv_type = if inst.opcode() == Opcode::Umulhi {694ValueConversionKind::ZeroExtend(double_length)695} else {696ValueConversionKind::SignExtend(double_length)697};698let arg0 = extractlanes(&arg(0), ctrl_ty)?;699let arg1 = extractlanes(&arg(1), ctrl_ty)?;700701let res = arg0702.into_iter()703.zip(arg1)704.map(|(x, y)| {705let x = x.convert(conv_type.clone())?;706let y = y.convert(conv_type.clone())?;707708Ok(DataValueExt::mul(x, y)?709.convert(ValueConversionKind::ExtractUpper(ctrl_ty.lane_type()))?)710})711.collect::<ValueResult<SimdVec<DataValue>>>()?;712713assign(vectorizelanes(&res, ctrl_ty)?)714}715Opcode::Udiv => binary_can_trap(DataValueExt::udiv, arg(0), arg(1))?,716Opcode::Sdiv => binary_can_trap(DataValueExt::sdiv, arg(0), arg(1))?,717Opcode::Urem => binary_can_trap(DataValueExt::urem, arg(0), arg(1))?,718Opcode::Srem => binary_can_trap(DataValueExt::srem, arg(0), arg(1))?,719Opcode::IaddImm => binary(DataValueExt::add, arg(0), imm_as_ctrl_ty()?)?,720Opcode::ImulImm => binary(DataValueExt::mul, arg(0), imm_as_ctrl_ty()?)?,721Opcode::UdivImm => binary_can_trap(DataValueExt::udiv, arg(0), imm_as_ctrl_ty()?)?,722Opcode::SdivImm => binary_can_trap(DataValueExt::sdiv, arg(0), imm_as_ctrl_ty()?)?,723Opcode::UremImm => binary_can_trap(DataValueExt::urem, arg(0), imm_as_ctrl_ty()?)?,724Opcode::SremImm => binary_can_trap(DataValueExt::srem, arg(0), imm_as_ctrl_ty()?)?,725Opcode::IrsubImm => binary(DataValueExt::sub, imm_as_ctrl_ty()?, arg(0))?,726Opcode::UaddOverflow => {727let (sum, carry) = arg(0).uadd_overflow(arg(1))?;728assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])729}730Opcode::SaddOverflow => {731let (sum, carry) = arg(0).sadd_overflow(arg(1))?;732assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])733}734Opcode::UsubOverflow => {735let (sum, carry) = arg(0).usub_overflow(arg(1))?;736assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])737}738Opcode::SsubOverflow => {739let (sum, carry) = arg(0).ssub_overflow(arg(1))?;740assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])741}742Opcode::UmulOverflow => {743let (sum, carry) = arg(0).umul_overflow(arg(1))?;744assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])745}746Opcode::SmulOverflow => {747let (sum, carry) = arg(0).smul_overflow(arg(1))?;748assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])749}750Opcode::SaddOverflowCin => {751let (mut sum, mut carry) = arg(0).sadd_overflow(arg(1))?;752753if DataValueExt::into_bool(arg(2))? {754let (sum2, carry2) = sum.sadd_overflow(DataValueExt::int(1, ctrl_ty)?)?;755carry |= carry2;756sum = sum2;757}758759assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])760}761Opcode::UaddOverflowCin => {762let (mut sum, mut carry) = arg(0).uadd_overflow(arg(1))?;763764if DataValueExt::into_bool(arg(2))? {765let (sum2, carry2) = sum.uadd_overflow(DataValueExt::int(1, ctrl_ty)?)?;766carry |= carry2;767sum = sum2;768}769770assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])771}772Opcode::UaddOverflowTrap => {773if let Some(sum) = DataValueExt::uadd_checked(arg(0), arg(1))? {774assign(sum)775} else {776ControlFlow::Trap(CraneliftTrap::User(trap_code()))777}778}779Opcode::SsubOverflowBin => {780let (mut sub, mut carry) = arg(0).ssub_overflow(arg(1))?;781782if DataValueExt::into_bool(arg(2))? {783let (sub2, carry2) = sub.ssub_overflow(DataValueExt::int(1, ctrl_ty)?)?;784carry |= carry2;785sub = sub2;786}787788assign_multiple(&[sub, DataValueExt::bool(carry, false, types::I8)?])789}790Opcode::UsubOverflowBin => {791let (mut sub, mut carry) = arg(0).usub_overflow(arg(1))?;792793if DataValueExt::into_bool(arg(2))? {794let (sub2, carry2) = sub.usub_overflow(DataValueExt::int(1, ctrl_ty)?)?;795carry |= carry2;796sub = sub2;797}798799assign_multiple(&[sub, DataValueExt::bool(carry, false, types::I8)?])800}801Opcode::Band => binary(DataValueExt::and, arg(0), arg(1))?,802Opcode::Bor => binary(DataValueExt::or, arg(0), arg(1))?,803Opcode::Bxor => binary(DataValueExt::xor, arg(0), arg(1))?,804Opcode::Bnot => unary(DataValueExt::not, arg(0))?,805Opcode::BandNot => binary(DataValueExt::and, arg(0), DataValueExt::not(arg(1))?)?,806Opcode::BorNot => binary(DataValueExt::or, arg(0), DataValueExt::not(arg(1))?)?,807Opcode::BxorNot => binary(DataValueExt::xor, arg(0), DataValueExt::not(arg(1))?)?,808Opcode::BandImm => binary(DataValueExt::and, arg(0), imm_as_ctrl_ty()?)?,809Opcode::BorImm => binary(DataValueExt::or, arg(0), imm_as_ctrl_ty()?)?,810Opcode::BxorImm => binary(DataValueExt::xor, arg(0), imm_as_ctrl_ty()?)?,811Opcode::Rotl => binary(DataValueExt::rotl, arg(0), shift_amt(ctrl_ty, arg(1))?)?,812Opcode::Rotr => binary(DataValueExt::rotr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,813Opcode::RotlImm => binary(DataValueExt::rotl, arg(0), shift_amt(ctrl_ty, imm())?)?,814Opcode::RotrImm => binary(DataValueExt::rotr, arg(0), shift_amt(ctrl_ty, imm())?)?,815Opcode::Ishl => binary(DataValueExt::shl, arg(0), shift_amt(ctrl_ty, arg(1))?)?,816Opcode::Ushr => binary(DataValueExt::ushr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,817Opcode::Sshr => binary(DataValueExt::sshr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,818Opcode::IshlImm => binary(DataValueExt::shl, arg(0), shift_amt(ctrl_ty, imm())?)?,819Opcode::UshrImm => binary(DataValueExt::ushr, arg(0), shift_amt(ctrl_ty, imm())?)?,820Opcode::SshrImm => binary(DataValueExt::sshr, arg(0), shift_amt(ctrl_ty, imm())?)?,821Opcode::Bitrev => unary(DataValueExt::reverse_bits, arg(0))?,822Opcode::Bswap => unary(DataValueExt::swap_bytes, arg(0))?,823Opcode::Clz => unary(DataValueExt::leading_zeros, arg(0))?,824Opcode::Cls => {825let count = if arg(0) < DataValueExt::int(0, ctrl_ty)? {826arg(0).leading_ones()?827} else {828arg(0).leading_zeros()?829};830assign(DataValueExt::sub(count, DataValueExt::int(1, ctrl_ty)?)?)831}832Opcode::Ctz => unary(DataValueExt::trailing_zeros, arg(0))?,833Opcode::Popcnt => {834let count = if arg(0).ty().is_int() {835arg(0).count_ones()?836} else {837let lanes = extractlanes(&arg(0), ctrl_ty)?838.into_iter()839.map(|lane| lane.count_ones())840.collect::<ValueResult<SimdVec<DataValue>>>()?;841vectorizelanes(&lanes, ctrl_ty)?842};843assign(count)844}845846Opcode::Fcmp => {847let arg0 = extractlanes(&arg(0), ctrl_ty)?;848let arg1 = extractlanes(&arg(1), ctrl_ty)?;849850assign(vectorizelanes(851&(arg0852.into_iter()853.zip(arg1.into_iter())854.map(|(x, y)| {855DataValue::bool(856fcmp(inst.fp_cond_code().unwrap(), &x, &y).unwrap(),857ctrl_ty.is_vector(),858ctrl_ty.lane_type().as_truthy(),859)860})861.collect::<ValueResult<SimdVec<DataValue>>>()?),862ctrl_ty,863)?)864}865Opcode::Fadd => binary(DataValueExt::add, arg(0), arg(1))?,866Opcode::Fsub => binary(DataValueExt::sub, arg(0), arg(1))?,867Opcode::Fmul => binary(DataValueExt::mul, arg(0), arg(1))?,868Opcode::Fdiv => binary(DataValueExt::sdiv, arg(0), arg(1))?,869Opcode::Sqrt => unary(DataValueExt::sqrt, arg(0))?,870Opcode::Fma => {871let arg0 = extractlanes(&arg(0), ctrl_ty)?;872let arg1 = extractlanes(&arg(1), ctrl_ty)?;873let arg2 = extractlanes(&arg(2), ctrl_ty)?;874875assign(vectorizelanes(876&(arg0877.into_iter()878.zip(arg1.into_iter())879.zip(arg2.into_iter())880.map(|((x, y), z)| DataValueExt::fma(x, y, z))881.collect::<ValueResult<SimdVec<DataValue>>>()?),882ctrl_ty,883)?)884}885Opcode::Fneg => unary(DataValueExt::neg, arg(0))?,886Opcode::Fabs => unary(DataValueExt::abs, arg(0))?,887Opcode::Fcopysign => binary(DataValueExt::copysign, arg(0), arg(1))?,888Opcode::Fmin => assign(match (arg(0), arg(1)) {889(a, _) if a.is_nan()? => a,890(_, b) if b.is_nan()? => b,891(a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => a,892(a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => b,893(a, b) => a.smin(b)?,894}),895Opcode::Fmax => assign(match (arg(0), arg(1)) {896(a, _) if a.is_nan()? => a,897(_, b) if b.is_nan()? => b,898(a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => b,899(a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => a,900(a, b) => a.smax(b)?,901}),902Opcode::Ceil => unary(DataValueExt::ceil, arg(0))?,903Opcode::Floor => unary(DataValueExt::floor, arg(0))?,904Opcode::Trunc => unary(DataValueExt::trunc, arg(0))?,905Opcode::Nearest => unary(DataValueExt::nearest, arg(0))?,906Opcode::Bitcast | Opcode::ScalarToVector => {907let input_ty = inst_context.type_of(inst_context.args()[0]).unwrap();908let lanes = &if input_ty.is_vector() {909assert_eq!(910inst.memflags()911.expect("byte order flag to be set")912.endianness(Endianness::Little),913Endianness::Little,914"Only little endian bitcasts on vectors are supported"915);916extractlanes(&arg(0), ctrl_ty)?917} else {918extractlanes(&arg(0), input_ty)?919.into_iter()920.map(|x| DataValue::convert(x, ValueConversionKind::Exact(ctrl_ty.lane_type())))921.collect::<ValueResult<SimdVec<DataValue>>>()?922};923assign(match inst.opcode() {924Opcode::Bitcast => vectorizelanes(lanes, ctrl_ty)?,925Opcode::ScalarToVector => vectorizelanes_all(lanes, ctrl_ty)?,926_ => unreachable!(),927})928}929Opcode::Ireduce => assign(DataValueExt::convert(930arg(0),931ValueConversionKind::Truncate(ctrl_ty),932)?),933Opcode::Snarrow | Opcode::Unarrow | Opcode::Uunarrow => {934let arg0 = extractlanes(&arg(0), ctrl_ty)?;935let arg1 = extractlanes(&arg(1), ctrl_ty)?;936let new_type = ctrl_ty.split_lanes().unwrap();937let (min, max) = new_type.bounds(inst.opcode() == Opcode::Snarrow);938let min: DataValue = DataValueExt::int(min as i128, ctrl_ty.lane_type())?;939let max: DataValue = DataValueExt::int(max as i128, ctrl_ty.lane_type())?;940let narrow = |mut lane: DataValue| -> ValueResult<DataValue> {941if inst.opcode() == Opcode::Uunarrow {942lane = DataValueExt::umax(lane, min.clone())?;943lane = DataValueExt::umin(lane, max.clone())?;944} else {945lane = DataValueExt::smax(lane, min.clone())?;946lane = DataValueExt::smin(lane, max.clone())?;947}948lane = lane.convert(ValueConversionKind::Truncate(new_type.lane_type()))?;949Ok(lane)950};951let new_vec = arg0952.into_iter()953.chain(arg1)954.map(|lane| narrow(lane))955.collect::<ValueResult<Vec<_>>>()?;956assign(vectorizelanes(&new_vec, new_type)?)957}958Opcode::Bmask => assign({959let bool = arg(0);960let bool_ty = ctrl_ty.as_truthy_pedantic();961let lanes = extractlanes(&bool, bool_ty)?962.into_iter()963.map(|lane| lane.convert(ValueConversionKind::Mask(ctrl_ty.lane_type())))964.collect::<ValueResult<SimdVec<DataValue>>>()?;965vectorizelanes(&lanes, ctrl_ty)?966}),967Opcode::Sextend => assign(DataValueExt::convert(968arg(0),969ValueConversionKind::SignExtend(ctrl_ty),970)?),971Opcode::Uextend => assign(DataValueExt::convert(972arg(0),973ValueConversionKind::ZeroExtend(ctrl_ty),974)?),975Opcode::Fpromote => assign(DataValueExt::convert(976arg(0),977ValueConversionKind::Exact(ctrl_ty),978)?),979Opcode::Fdemote => assign(DataValueExt::convert(980arg(0),981ValueConversionKind::RoundNearestEven(ctrl_ty),982)?),983Opcode::Shuffle => {984let mask = imm().into_array()?;985let a = DataValueExt::into_array(&arg(0))?;986let b = DataValueExt::into_array(&arg(1))?;987let mut new = [0u8; 16];988for i in 0..mask.len() {989if (mask[i] as usize) < a.len() {990new[i] = a[mask[i] as usize];991} else if (mask[i] as usize - a.len()) < b.len() {992new[i] = b[mask[i] as usize - a.len()];993} // else leave as 0.994}995assign(DataValueExt::vector(new, types::I8X16)?)996}997Opcode::Swizzle => {998let x = DataValueExt::into_array(&arg(0))?;999let s = DataValueExt::into_array(&arg(1))?;1000let mut new = [0u8; 16];1001for i in 0..new.len() {1002if (s[i] as usize) < new.len() {1003new[i] = x[s[i] as usize];1004} // else leave as 01005}1006assign(DataValueExt::vector(new, types::I8X16)?)1007}1008Opcode::Splat => assign(splat(ctrl_ty, arg(0))?),1009Opcode::Insertlane => {1010let idx = imm().into_int_unsigned()? as usize;1011let mut vector = extractlanes(&arg(0), ctrl_ty)?;1012vector[idx] = arg(1);1013assign(vectorizelanes(&vector, ctrl_ty)?)1014}1015Opcode::Extractlane => {1016let idx = imm().into_int_unsigned()? as usize;1017let lanes = extractlanes(&arg(0), ctrl_ty)?;1018assign(lanes[idx].clone())1019}1020Opcode::VhighBits => {1021// `ctrl_ty` controls the return type for this, so the input type1022// must be retrieved via `inst_context`.1023let vector_type = inst_context1024.type_of(inst_context.args()[0])1025.unwrap()1026.as_int();1027let a = extractlanes(&arg(0), vector_type)?;1028let mut result: u128 = 0;1029for (i, val) in a.into_iter().enumerate() {1030let val = val.reverse_bits()?.into_int_unsigned()?; // MSB -> LSB1031result |= (val & 1) << i;1032}1033assign(DataValueExt::int(result as i128, ctrl_ty)?)1034}1035Opcode::VanyTrue => {1036let simd_ty = ctrl_ty.as_int();1037let lane_ty = simd_ty.lane_type();1038let init = DataValue::bool(false, true, lane_ty)?;1039let any = fold_vector(arg(0), simd_ty, init.clone(), |acc, lane| acc.or(lane))?;1040assign(DataValue::bool(any != init, false, types::I8)?)1041}1042Opcode::VallTrue => assign(DataValue::bool(1043!(arg(0)1044.iter_lanes(ctrl_ty.as_int())?1045.try_fold(false, |acc, lane| {1046Ok::<bool, ValueError>(acc | lane.is_zero()?)1047})?),1048false,1049types::I8,1050)?),1051Opcode::SwidenLow | Opcode::SwidenHigh | Opcode::UwidenLow | Opcode::UwidenHigh => {1052let new_type = ctrl_ty.merge_lanes().unwrap();1053let conv_type = match inst.opcode() {1054Opcode::SwidenLow | Opcode::SwidenHigh => {1055ValueConversionKind::SignExtend(new_type.lane_type())1056}1057Opcode::UwidenLow | Opcode::UwidenHigh => {1058ValueConversionKind::ZeroExtend(new_type.lane_type())1059}1060_ => unreachable!(),1061};1062let vec_iter = extractlanes(&arg(0), ctrl_ty)?.into_iter();1063let new_vec = match inst.opcode() {1064Opcode::SwidenLow | Opcode::UwidenLow => vec_iter1065.take(new_type.lane_count() as usize)1066.map(|lane| lane.convert(conv_type.clone()))1067.collect::<ValueResult<Vec<_>>>()?,1068Opcode::SwidenHigh | Opcode::UwidenHigh => vec_iter1069.skip(new_type.lane_count() as usize)1070.map(|lane| lane.convert(conv_type.clone()))1071.collect::<ValueResult<Vec<_>>>()?,1072_ => unreachable!(),1073};1074assign(vectorizelanes(&new_vec, new_type)?)1075}1076Opcode::FcvtToUint | Opcode::FcvtToSint => {1077// NaN check1078if arg(0).is_nan()? {1079return Ok(ControlFlow::Trap(CraneliftTrap::User(1080TrapCode::BAD_CONVERSION_TO_INTEGER,1081)));1082}1083let x = arg(0).into_float()? as i128;1084let is_signed = inst.opcode() == Opcode::FcvtToSint;1085let (min, max) = ctrl_ty.bounds(is_signed);1086let overflow = if is_signed {1087x < (min as i128) || x > (max as i128)1088} else {1089x < 0 || (x as u128) > max1090};1091// bounds check1092if overflow {1093return Ok(ControlFlow::Trap(CraneliftTrap::User(1094TrapCode::INTEGER_OVERFLOW,1095)));1096}1097// perform the conversion.1098assign(DataValueExt::int(x, ctrl_ty)?)1099}1100Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => {1101let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();1102let cvt = |x: DataValue| -> ValueResult<DataValue> {1103// NaN check1104if x.is_nan()? {1105DataValue::int(0, ctrl_ty.lane_type())1106} else {1107let is_signed = inst.opcode() == Opcode::FcvtToSintSat;1108let (min, max) = ctrl_ty.bounds(is_signed);1109let x = x.into_float()? as i128;1110let x = if is_signed {1111let x = i128::max(x, min as i128);1112let x = i128::min(x, max as i128);1113x1114} else {1115let x = if x < 0 { 0 } else { x };1116let x = u128::min(x as u128, max);1117x as i1281118};11191120DataValue::int(x, ctrl_ty.lane_type())1121}1122};11231124let x = extractlanes(&arg(0), in_ty)?;11251126assign(vectorizelanes(1127&x.into_iter()1128.map(cvt)1129.collect::<ValueResult<SimdVec<DataValue>>>()?,1130ctrl_ty,1131)?)1132}1133Opcode::FcvtFromUint | Opcode::FcvtFromSint => {1134let x = extractlanes(1135&arg(0),1136inst_context.type_of(inst_context.args()[0]).unwrap(),1137)?;1138let bits = |x: DataValue| -> ValueResult<u64> {1139Ok(match ctrl_ty.lane_type() {1140types::F32 => (if inst.opcode() == Opcode::FcvtFromUint {1141x.into_int_unsigned()? as f321142} else {1143x.into_int_signed()? as f321144})1145.to_bits() as u64,1146types::F64 => (if inst.opcode() == Opcode::FcvtFromUint {1147x.into_int_unsigned()? as f641148} else {1149x.into_int_signed()? as f641150})1151.to_bits(),1152_ => unimplemented!("unexpected conversion to {:?}", ctrl_ty.lane_type()),1153})1154};1155assign(vectorizelanes(1156&x.into_iter()1157.map(|x| DataValue::float(bits(x)?, ctrl_ty.lane_type()))1158.collect::<ValueResult<SimdVec<DataValue>>>()?,1159ctrl_ty,1160)?)1161}1162Opcode::FvpromoteLow => {1163let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();1164assert_eq!(in_ty, types::F32X4);1165let out_ty = types::F64X2;1166let x = extractlanes(&arg(0), in_ty)?;1167assign(vectorizelanes(1168&x[..(out_ty.lane_count() as usize)]1169.into_iter()1170.map(|x| {1171DataValue::convert(1172x.to_owned(),1173ValueConversionKind::Exact(out_ty.lane_type()),1174)1175})1176.collect::<ValueResult<SimdVec<DataValue>>>()?,1177out_ty,1178)?)1179}1180Opcode::Fvdemote => {1181let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();1182assert_eq!(in_ty, types::F64X2);1183let out_ty = types::F32X4;1184let x = extractlanes(&arg(0), in_ty)?;1185let x = &mut x1186.into_iter()1187.map(|x| {1188DataValue::convert(x, ValueConversionKind::RoundNearestEven(out_ty.lane_type()))1189})1190.collect::<ValueResult<SimdVec<DataValue>>>()?;1191// zero the high bits.1192for _ in 0..(out_ty.lane_count() as usize - x.len()) {1193x.push(DataValue::float(0, out_ty.lane_type())?);1194}1195assign(vectorizelanes(x, out_ty)?)1196}1197Opcode::Isplit => assign_multiple(&[1198DataValueExt::convert(arg(0), ValueConversionKind::Truncate(types::I64))?,1199DataValueExt::convert(arg(0), ValueConversionKind::ExtractUpper(types::I64))?,1200]),1201Opcode::Iconcat => assign(DataValueExt::concat(arg(0), arg(1))?),1202Opcode::AtomicRmw => {1203let op = inst.atomic_rmw_op().unwrap();1204let val = arg(1);1205let addr = arg(0).into_int_unsigned()? as u64;1206let mem_flags = inst.memflags().expect("instruction to have memory flags");1207let loaded = Address::try_from(addr)1208.and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));1209let prev_val = match loaded {1210Ok(v) => v,1211Err(e) => return Ok(ControlFlow::Trap(memerror_to_trap(e))),1212};1213let prev_val_to_assign = prev_val.clone();1214let replace = match op {1215AtomicRmwOp::Xchg => Ok(val),1216AtomicRmwOp::Add => DataValueExt::add(prev_val, val),1217AtomicRmwOp::Sub => DataValueExt::sub(prev_val, val),1218AtomicRmwOp::And => DataValueExt::and(prev_val, val),1219AtomicRmwOp::Or => DataValueExt::or(prev_val, val),1220AtomicRmwOp::Xor => DataValueExt::xor(prev_val, val),1221AtomicRmwOp::Nand => DataValueExt::and(prev_val, val).and_then(DataValue::not),1222AtomicRmwOp::Smax => DataValueExt::smax(prev_val, val),1223AtomicRmwOp::Smin => DataValueExt::smin(prev_val, val),1224AtomicRmwOp::Umax => DataValueExt::umax(val, prev_val),1225AtomicRmwOp::Umin => DataValueExt::umin(val, prev_val),1226}?;1227let stored = Address::try_from(addr)1228.and_then(|addr| state.checked_store(addr, replace, mem_flags));1229assign_or_memtrap(stored.map(|_| prev_val_to_assign))1230}1231Opcode::AtomicCas => {1232let addr = arg(0).into_int_unsigned()? as u64;1233let mem_flags = inst.memflags().expect("instruction to have memory flags");1234let loaded = Address::try_from(addr)1235.and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));1236let loaded_val = match loaded {1237Ok(v) => v,1238Err(e) => return Ok(ControlFlow::Trap(memerror_to_trap(e))),1239};1240let expected_val = arg(1);1241let val_to_assign = if loaded_val == expected_val {1242let val_to_store = arg(2);1243Address::try_from(addr)1244.and_then(|addr| state.checked_store(addr, val_to_store, mem_flags))1245.map(|_| loaded_val)1246} else {1247Ok(loaded_val)1248};1249assign_or_memtrap(val_to_assign)1250}1251Opcode::AtomicLoad => {1252let load_ty = inst_context.controlling_type().unwrap();1253let addr = arg(0).into_int_unsigned()? as u64;1254let mem_flags = inst.memflags().expect("instruction to have memory flags");1255// We are doing a regular load here, this isn't actually thread safe.1256assign_or_memtrap(1257Address::try_from(addr)1258.and_then(|addr| state.checked_load(addr, load_ty, mem_flags)),1259)1260}1261Opcode::AtomicStore => {1262let val = arg(0);1263let addr = arg(1).into_int_unsigned()? as u64;1264let mem_flags = inst.memflags().expect("instruction to have memory flags");1265// We are doing a regular store here, this isn't actually thread safe.1266continue_or_memtrap(1267Address::try_from(addr).and_then(|addr| state.checked_store(addr, val, mem_flags)),1268)1269}1270Opcode::Fence => {1271// The interpreter always runs in a single threaded context, so we don't1272// actually need to emit a fence here.1273ControlFlow::Continue1274}1275Opcode::SqmulRoundSat => {1276let lane_type = ctrl_ty.lane_type();1277let double_width = ctrl_ty.double_width().unwrap().lane_type();1278let arg0 = extractlanes(&arg(0), ctrl_ty)?;1279let arg1 = extractlanes(&arg(1), ctrl_ty)?;1280let (min, max) = lane_type.bounds(true);1281let min: DataValue = DataValueExt::int(min as i128, double_width)?;1282let max: DataValue = DataValueExt::int(max as i128, double_width)?;1283let new_vec = arg01284.into_iter()1285.zip(arg1.into_iter())1286.map(|(x, y)| {1287let x = x.into_int_signed()?;1288let y = y.into_int_signed()?;1289// temporarily double width of the value to avoid overflow.1290let z: DataValue = DataValueExt::int(1291(x * y + (1 << (lane_type.bits() - 2))) >> (lane_type.bits() - 1),1292double_width,1293)?;1294// check bounds, saturate, and truncate to correct width.1295let z = DataValueExt::smin(z, max.clone())?;1296let z = DataValueExt::smax(z, min.clone())?;1297let z = z.convert(ValueConversionKind::Truncate(lane_type))?;1298Ok(z)1299})1300.collect::<ValueResult<SimdVec<_>>>()?;1301assign(vectorizelanes(&new_vec, ctrl_ty)?)1302}1303Opcode::IaddPairwise => {1304assign(binary_pairwise(arg(0), arg(1), ctrl_ty, DataValueExt::add)?)1305}1306Opcode::ExtractVector => {1307unimplemented!("ExtractVector not supported");1308}1309Opcode::GetFramePointer => unimplemented!("GetFramePointer"),1310Opcode::GetStackPointer => unimplemented!("GetStackPointer"),1311Opcode::GetReturnAddress => unimplemented!("GetReturnAddress"),1312Opcode::X86Pshufb => unimplemented!("X86Pshufb"),1313Opcode::X86Blendv => unimplemented!("X86Blendv"),1314Opcode::X86Pmulhrsw => unimplemented!("X86Pmulhrsw"),1315Opcode::X86Pmaddubsw => unimplemented!("X86Pmaddubsw"),1316Opcode::X86Cvtt2dq => unimplemented!("X86Cvtt2dq"),1317Opcode::StackSwitch => unimplemented!("StackSwitch"),13181319Opcode::TryCall => unimplemented!("TryCall"),1320Opcode::TryCallIndirect => unimplemented!("TryCallIndirect"),1321})1322}13231324#[derive(Error, Debug)]1325pub enum StepError {1326#[error("unable to retrieve value from SSA reference: {0}")]1327UnknownValue(ValueRef),1328#[error("unable to find the following function: {0}")]1329UnknownFunction(FuncRef),1330#[error("cannot step with these values")]1331ValueError(#[from] ValueError),1332#[error("failed to access memory")]1333MemoryError(#[from] MemoryError),1334}13351336/// Enumerate the ways in which the control flow can change based on a single step in a Cranelift1337/// interpreter.1338#[derive(Debug, PartialEq)]1339pub enum ControlFlow<'a> {1340/// Return one or more values from an instruction to be assigned to a left-hand side, e.g.:1341/// in `v0 = iadd v1, v2`, the sum of `v1` and `v2` is assigned to `v0`.1342Assign(SmallVec<[DataValue; 1]>),1343/// Continue to the next available instruction, e.g.: in `nop`, we expect to resume execution1344/// at the instruction after it.1345Continue,1346/// Jump to another block with the given parameters, e.g.: in1347/// `brif v0, block42(v1, v2), block97`, if the condition is true, we continue execution at the1348/// first instruction of `block42` with the values in `v1` and `v2` filling in the block1349/// parameters.1350ContinueAt(Block, SmallVec<[DataValue; 1]>),1351/// Indicates a call the given [Function] with the supplied arguments.1352Call(&'a Function, SmallVec<[DataValue; 1]>),1353/// Indicates a tail call to the given [Function] with the supplied arguments.1354ReturnCall(&'a Function, SmallVec<[DataValue; 1]>),1355/// Return from the current function with the given parameters, e.g.: `return [v1, v2]`.1356Return(SmallVec<[DataValue; 1]>),1357/// Stop with a program-generated trap; note that these are distinct from errors that may occur1358/// during interpretation.1359Trap(CraneliftTrap),1360}13611362#[derive(Error, Debug, PartialEq, Eq, Hash)]1363pub enum CraneliftTrap {1364#[error("user code: {0}")]1365User(TrapCode),1366#[error("bad signature")]1367BadSignature,1368#[error("unreachable code has been reached")]1369UnreachableCodeReached,1370#[error("heap is misaligned")]1371HeapMisaligned,1372#[error("user debug")]1373Debug,1374}13751376/// Compare two values using the given integer condition `code`.1377fn icmp(1378ctrl_ty: types::Type,1379code: IntCC,1380left: &DataValue,1381right: &DataValue,1382) -> ValueResult<DataValue> {1383let cmp = |bool_ty: types::Type,1384code: IntCC,1385left: &DataValue,1386right: &DataValue|1387-> ValueResult<DataValue> {1388Ok(DataValueExt::bool(1389match code {1390IntCC::Equal => left == right,1391IntCC::NotEqual => left != right,1392IntCC::SignedGreaterThan => left > right,1393IntCC::SignedGreaterThanOrEqual => left >= right,1394IntCC::SignedLessThan => left < right,1395IntCC::SignedLessThanOrEqual => left <= right,1396IntCC::UnsignedGreaterThan => {1397left.clone().into_int_unsigned()? > right.clone().into_int_unsigned()?1398}1399IntCC::UnsignedGreaterThanOrEqual => {1400left.clone().into_int_unsigned()? >= right.clone().into_int_unsigned()?1401}1402IntCC::UnsignedLessThan => {1403left.clone().into_int_unsigned()? < right.clone().into_int_unsigned()?1404}1405IntCC::UnsignedLessThanOrEqual => {1406left.clone().into_int_unsigned()? <= right.clone().into_int_unsigned()?1407}1408},1409ctrl_ty.is_vector(),1410bool_ty,1411)?)1412};14131414let dst_ty = ctrl_ty.as_truthy();1415let left = extractlanes(left, ctrl_ty)?;1416let right = extractlanes(right, ctrl_ty)?;14171418let res = left1419.into_iter()1420.zip(right.into_iter())1421.map(|(l, r)| cmp(dst_ty.lane_type(), code, &l, &r))1422.collect::<ValueResult<SimdVec<DataValue>>>()?;14231424Ok(vectorizelanes(&res, dst_ty)?)1425}14261427/// Compare two values using the given floating point condition `code`.1428fn fcmp(code: FloatCC, left: &DataValue, right: &DataValue) -> ValueResult<bool> {1429Ok(match code {1430FloatCC::Ordered => left == right || left < right || left > right,1431FloatCC::Unordered => DataValueExt::uno(left, right)?,1432FloatCC::Equal => left == right,1433FloatCC::NotEqual => left < right || left > right || DataValueExt::uno(left, right)?,1434FloatCC::OrderedNotEqual => left < right || left > right,1435FloatCC::UnorderedOrEqual => left == right || DataValueExt::uno(left, right)?,1436FloatCC::LessThan => left < right,1437FloatCC::LessThanOrEqual => left <= right,1438FloatCC::GreaterThan => left > right,1439FloatCC::GreaterThanOrEqual => left >= right,1440FloatCC::UnorderedOrLessThan => DataValueExt::uno(left, right)? || left < right,1441FloatCC::UnorderedOrLessThanOrEqual => DataValueExt::uno(left, right)? || left <= right,1442FloatCC::UnorderedOrGreaterThan => DataValueExt::uno(left, right)? || left > right,1443FloatCC::UnorderedOrGreaterThanOrEqual => DataValueExt::uno(left, right)? || left >= right,1444})1445}14461447pub type SimdVec<DataValue> = SmallVec<[DataValue; 4]>;14481449/// Converts a SIMD vector value into a Rust array of [Value] for processing.1450/// If `x` is a scalar, it will be returned as a single-element array.1451pub(crate) fn extractlanes(1452x: &DataValue,1453vector_type: types::Type,1454) -> ValueResult<SimdVec<DataValue>> {1455let lane_type = vector_type.lane_type();1456let mut lanes = SimdVec::new();1457// Wrap scalar values as a single-element vector and return.1458if !x.ty().is_vector() {1459lanes.push(x.clone());1460return Ok(lanes);1461}14621463let iterations = match lane_type {1464types::I8 => 1,1465types::I16 | types::F16 => 2,1466types::I32 | types::F32 => 4,1467types::I64 | types::F64 => 8,1468_ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),1469};14701471let x = x.into_array()?;1472for i in 0..vector_type.lane_count() {1473let mut lane: i128 = 0;1474for j in 0..iterations {1475lane += (x[((i * iterations) + j) as usize] as i128) << (8 * j);1476}14771478let lane_val: DataValue = if lane_type.is_float() {1479DataValueExt::float(lane as u64, lane_type)?1480} else {1481DataValueExt::int(lane, lane_type)?1482};1483lanes.push(lane_val);1484}1485return Ok(lanes);1486}14871488/// Convert a Rust array of [Value] back into a `Value::vector`.1489/// Supplying a single-element array will simply return its contained value.1490fn vectorizelanes(x: &[DataValue], vector_type: types::Type) -> ValueResult<DataValue> {1491// If the array is only one element, return it as a scalar.1492if x.len() == 1 {1493Ok(x[0].clone())1494} else {1495vectorizelanes_all(x, vector_type)1496}1497}14981499/// Convert a Rust array of [Value] back into a `Value::vector`.1500fn vectorizelanes_all(x: &[DataValue], vector_type: types::Type) -> ValueResult<DataValue> {1501let lane_type = vector_type.lane_type();1502let iterations = match lane_type {1503types::I8 => 1,1504types::I16 | types::F16 => 2,1505types::I32 | types::F32 => 4,1506types::I64 | types::F64 => 8,1507_ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),1508};1509let mut result: [u8; 16] = [0; 16];1510for (i, val) in x.iter().enumerate() {1511let lane_val: i128 = val1512.clone()1513.convert(ValueConversionKind::Exact(lane_type.as_int()))?1514.into_int_unsigned()? as i128;15151516for j in 0..iterations {1517result[(i * iterations) + j] = (lane_val >> (8 * j)) as u8;1518}1519}1520DataValueExt::vector(result, vector_type)1521}15221523/// Performs a lanewise fold on a vector type1524fn fold_vector<F>(v: DataValue, ty: types::Type, init: DataValue, op: F) -> ValueResult<DataValue>1525where1526F: FnMut(DataValue, DataValue) -> ValueResult<DataValue>,1527{1528extractlanes(&v, ty)?.into_iter().try_fold(init, op)1529}15301531/// Performs the supplied unary arithmetic `op` on a Value, either Vector or Scalar.1532fn unary_arith<F>(x: DataValue, vector_type: types::Type, op: F) -> ValueResult<DataValue>1533where1534F: Fn(DataValue) -> ValueResult<DataValue>,1535{1536let arg = extractlanes(&x, vector_type)?;15371538let result = arg1539.into_iter()1540.map(|arg| Ok(op(arg)?))1541.collect::<ValueResult<SimdVec<DataValue>>>()?;15421543vectorizelanes(&result, vector_type)1544}15451546/// Performs the supplied binary arithmetic `op` on two values, either vector or scalar.1547fn binary_arith<F>(1548x: DataValue,1549y: DataValue,1550vector_type: types::Type,1551op: F,1552) -> ValueResult<DataValue>1553where1554F: Fn(DataValue, DataValue) -> ValueResult<DataValue>,1555{1556let arg0 = extractlanes(&x, vector_type)?;1557let arg1 = extractlanes(&y, vector_type)?;15581559let result = arg01560.into_iter()1561.zip(arg1)1562.map(|(lhs, rhs)| Ok(op(lhs, rhs)?))1563.collect::<ValueResult<SimdVec<DataValue>>>()?;15641565vectorizelanes(&result, vector_type)1566}15671568/// Performs the supplied pairwise arithmetic `op` on two SIMD vectors, where1569/// pairs are formed from adjacent vector elements and the vectors are1570/// concatenated at the end.1571fn binary_pairwise<F>(1572x: DataValue,1573y: DataValue,1574vector_type: types::Type,1575op: F,1576) -> ValueResult<DataValue>1577where1578F: Fn(DataValue, DataValue) -> ValueResult<DataValue>,1579{1580let arg0 = extractlanes(&x, vector_type)?;1581let arg1 = extractlanes(&y, vector_type)?;15821583let result = arg01584.chunks(2)1585.chain(arg1.chunks(2))1586.map(|pair| op(pair[0].clone(), pair[1].clone()))1587.collect::<ValueResult<SimdVec<DataValue>>>()?;15881589vectorizelanes(&result, vector_type)1590}15911592fn bitselect(c: DataValue, x: DataValue, y: DataValue) -> ValueResult<DataValue> {1593let mask_x = DataValueExt::and(c.clone(), x)?;1594let mask_y = DataValueExt::and(DataValueExt::not(c)?, y)?;1595DataValueExt::or(mask_x, mask_y)1596}15971598fn splat(ty: Type, val: DataValue) -> ValueResult<DataValue> {1599let mut new_vector = SimdVec::new();1600for _ in 0..ty.lane_count() {1601new_vector.push(val.clone());1602}1603vectorizelanes(&new_vector, ty)1604}16051606// Prepares the shift amount for a shift/rotate operation.1607// The shift amount must be the same type and have the same number of lanes as the vector.1608fn shift_amt(ty: Type, val: DataValue) -> ValueResult<DataValue> {1609splat(ty, val.convert(ValueConversionKind::Exact(ty.lane_type()))?)1610}161116121613