Path: blob/main/cranelift/interpreter/src/step.rs
3054 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 => {889let scalar_min = |a: DataValue, b: DataValue| -> ValueResult<DataValue> {890Ok(match (a, b) {891(a, _) if a.is_nan()? => a,892(_, b) if b.is_nan()? => b,893(a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => a,894(a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => b,895(a, b) => a.smin(b)?,896})897};898899if ctrl_ty.is_vector() {900let arg0 = extractlanes(&arg(0), ctrl_ty)?;901let arg1 = extractlanes(&arg(1), ctrl_ty)?;902903assign(vectorizelanes(904&(arg0905.into_iter()906.zip(arg1.into_iter())907.map(|(a, b)| scalar_min(a, b))908.collect::<ValueResult<SimdVec<DataValue>>>()?),909ctrl_ty,910)?)911} else {912assign(scalar_min(arg(0), arg(1))?)913}914}915Opcode::Fmax => {916let scalar_max = |a: DataValue, b: DataValue| -> ValueResult<DataValue> {917Ok(match (a, b) {918(a, _) if a.is_nan()? => a,919(_, b) if b.is_nan()? => b,920(a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => b,921(a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => a,922(a, b) => a.smax(b)?,923})924};925926if ctrl_ty.is_vector() {927let arg0 = extractlanes(&arg(0), ctrl_ty)?;928let arg1 = extractlanes(&arg(1), ctrl_ty)?;929930assign(vectorizelanes(931&(arg0932.into_iter()933.zip(arg1.into_iter())934.map(|(a, b)| scalar_max(a, b))935.collect::<ValueResult<SimdVec<DataValue>>>()?),936ctrl_ty,937)?)938} else {939assign(scalar_max(arg(0), arg(1))?)940}941}942Opcode::Ceil => unary(DataValueExt::ceil, arg(0))?,943Opcode::Floor => unary(DataValueExt::floor, arg(0))?,944Opcode::Trunc => unary(DataValueExt::trunc, arg(0))?,945Opcode::Nearest => unary(DataValueExt::nearest, arg(0))?,946Opcode::Bitcast | Opcode::ScalarToVector => {947let input_ty = inst_context.type_of(inst_context.args()[0]).unwrap();948let lanes = &if input_ty.is_vector() {949assert_eq!(950inst.memflags()951.expect("byte order flag to be set")952.endianness(Endianness::Little),953Endianness::Little,954"Only little endian bitcasts on vectors are supported"955);956extractlanes(&arg(0), ctrl_ty)?957} else {958extractlanes(&arg(0), input_ty)?959.into_iter()960.map(|x| DataValue::convert(x, ValueConversionKind::Exact(ctrl_ty.lane_type())))961.collect::<ValueResult<SimdVec<DataValue>>>()?962};963assign(match inst.opcode() {964Opcode::Bitcast => vectorizelanes(lanes, ctrl_ty)?,965Opcode::ScalarToVector => vectorizelanes_all(lanes, ctrl_ty)?,966_ => unreachable!(),967})968}969Opcode::Ireduce => assign(DataValueExt::convert(970arg(0),971ValueConversionKind::Truncate(ctrl_ty),972)?),973Opcode::Snarrow | Opcode::Unarrow | Opcode::Uunarrow => {974let arg0 = extractlanes(&arg(0), ctrl_ty)?;975let arg1 = extractlanes(&arg(1), ctrl_ty)?;976let new_type = ctrl_ty.split_lanes().unwrap();977let (min, max) = new_type.bounds(inst.opcode() == Opcode::Snarrow);978let min: DataValue = DataValueExt::int(min as i128, ctrl_ty.lane_type())?;979let max: DataValue = DataValueExt::int(max as i128, ctrl_ty.lane_type())?;980let narrow = |mut lane: DataValue| -> ValueResult<DataValue> {981if inst.opcode() == Opcode::Uunarrow {982lane = DataValueExt::umax(lane, min.clone())?;983lane = DataValueExt::umin(lane, max.clone())?;984} else {985lane = DataValueExt::smax(lane, min.clone())?;986lane = DataValueExt::smin(lane, max.clone())?;987}988lane = lane.convert(ValueConversionKind::Truncate(new_type.lane_type()))?;989Ok(lane)990};991let new_vec = arg0992.into_iter()993.chain(arg1)994.map(|lane| narrow(lane))995.collect::<ValueResult<Vec<_>>>()?;996assign(vectorizelanes(&new_vec, new_type)?)997}998Opcode::Bmask => assign({999let bool = arg(0);1000let bool_ty = ctrl_ty.as_truthy_pedantic();1001let lanes = extractlanes(&bool, bool_ty)?1002.into_iter()1003.map(|lane| lane.convert(ValueConversionKind::Mask(ctrl_ty.lane_type())))1004.collect::<ValueResult<SimdVec<DataValue>>>()?;1005vectorizelanes(&lanes, ctrl_ty)?1006}),1007Opcode::Sextend => assign(DataValueExt::convert(1008arg(0),1009ValueConversionKind::SignExtend(ctrl_ty),1010)?),1011Opcode::Uextend => assign(DataValueExt::convert(1012arg(0),1013ValueConversionKind::ZeroExtend(ctrl_ty),1014)?),1015Opcode::Fpromote => assign(DataValueExt::convert(1016arg(0),1017ValueConversionKind::Exact(ctrl_ty),1018)?),1019Opcode::Fdemote => assign(DataValueExt::convert(1020arg(0),1021ValueConversionKind::RoundNearestEven(ctrl_ty),1022)?),1023Opcode::Shuffle => {1024let mask = imm().into_array()?;1025let a = DataValueExt::into_array(&arg(0))?;1026let b = DataValueExt::into_array(&arg(1))?;1027let mut new = [0u8; 16];1028for i in 0..mask.len() {1029if (mask[i] as usize) < a.len() {1030new[i] = a[mask[i] as usize];1031} else if (mask[i] as usize - a.len()) < b.len() {1032new[i] = b[mask[i] as usize - a.len()];1033} // else leave as 0.1034}1035assign(DataValueExt::vector(new, types::I8X16)?)1036}1037Opcode::Swizzle => {1038let x = DataValueExt::into_array(&arg(0))?;1039let s = DataValueExt::into_array(&arg(1))?;1040let mut new = [0u8; 16];1041for i in 0..new.len() {1042if (s[i] as usize) < new.len() {1043new[i] = x[s[i] as usize];1044} // else leave as 01045}1046assign(DataValueExt::vector(new, types::I8X16)?)1047}1048Opcode::Splat => assign(splat(ctrl_ty, arg(0))?),1049Opcode::Insertlane => {1050let idx = imm().into_int_unsigned()? as usize;1051let mut vector = extractlanes(&arg(0), ctrl_ty)?;1052vector[idx] = arg(1);1053assign(vectorizelanes(&vector, ctrl_ty)?)1054}1055Opcode::Extractlane => {1056let idx = imm().into_int_unsigned()? as usize;1057let lanes = extractlanes(&arg(0), ctrl_ty)?;1058assign(lanes[idx].clone())1059}1060Opcode::VhighBits => {1061// `ctrl_ty` controls the return type for this, so the input type1062// must be retrieved via `inst_context`.1063let vector_type = inst_context1064.type_of(inst_context.args()[0])1065.unwrap()1066.as_int();1067let a = extractlanes(&arg(0), vector_type)?;1068let mut result: u128 = 0;1069for (i, val) in a.into_iter().enumerate() {1070let val = val.reverse_bits()?.into_int_unsigned()?; // MSB -> LSB1071result |= (val & 1) << i;1072}1073assign(DataValueExt::int(result as i128, ctrl_ty)?)1074}1075Opcode::VanyTrue => {1076let simd_ty = ctrl_ty.as_int();1077let lane_ty = simd_ty.lane_type();1078let init = DataValue::bool(false, true, lane_ty)?;1079let any = fold_vector(arg(0), simd_ty, init.clone(), |acc, lane| acc.or(lane))?;1080assign(DataValue::bool(any != init, false, types::I8)?)1081}1082Opcode::VallTrue => assign(DataValue::bool(1083!(arg(0)1084.iter_lanes(ctrl_ty.as_int())?1085.try_fold(false, |acc, lane| {1086Ok::<bool, ValueError>(acc | lane.is_zero()?)1087})?),1088false,1089types::I8,1090)?),1091Opcode::SwidenLow | Opcode::SwidenHigh | Opcode::UwidenLow | Opcode::UwidenHigh => {1092let new_type = ctrl_ty.merge_lanes().unwrap();1093let conv_type = match inst.opcode() {1094Opcode::SwidenLow | Opcode::SwidenHigh => {1095ValueConversionKind::SignExtend(new_type.lane_type())1096}1097Opcode::UwidenLow | Opcode::UwidenHigh => {1098ValueConversionKind::ZeroExtend(new_type.lane_type())1099}1100_ => unreachable!(),1101};1102let vec_iter = extractlanes(&arg(0), ctrl_ty)?.into_iter();1103let new_vec = match inst.opcode() {1104Opcode::SwidenLow | Opcode::UwidenLow => vec_iter1105.take(new_type.lane_count() as usize)1106.map(|lane| lane.convert(conv_type.clone()))1107.collect::<ValueResult<Vec<_>>>()?,1108Opcode::SwidenHigh | Opcode::UwidenHigh => vec_iter1109.skip(new_type.lane_count() as usize)1110.map(|lane| lane.convert(conv_type.clone()))1111.collect::<ValueResult<Vec<_>>>()?,1112_ => unreachable!(),1113};1114assign(vectorizelanes(&new_vec, new_type)?)1115}1116Opcode::FcvtToUint | Opcode::FcvtToSint => {1117// NaN check1118if arg(0).is_nan()? {1119return Ok(ControlFlow::Trap(CraneliftTrap::User(1120TrapCode::BAD_CONVERSION_TO_INTEGER,1121)));1122}1123let x = arg(0).into_float()? as i128;1124let is_signed = inst.opcode() == Opcode::FcvtToSint;1125let (min, max) = ctrl_ty.bounds(is_signed);1126let overflow = if is_signed {1127x < (min as i128) || x > (max as i128)1128} else {1129x < 0 || (x as u128) > max1130};1131// bounds check1132if overflow {1133return Ok(ControlFlow::Trap(CraneliftTrap::User(1134TrapCode::INTEGER_OVERFLOW,1135)));1136}1137// perform the conversion.1138assign(DataValueExt::int(x, ctrl_ty)?)1139}1140Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => {1141let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();1142let cvt = |x: DataValue| -> ValueResult<DataValue> {1143// NaN check1144if x.is_nan()? {1145DataValue::int(0, ctrl_ty.lane_type())1146} else {1147let is_signed = inst.opcode() == Opcode::FcvtToSintSat;1148let (min, max) = ctrl_ty.bounds(is_signed);1149let x = x.into_float()? as i128;1150let x = if is_signed {1151let x = i128::max(x, min as i128);1152let x = i128::min(x, max as i128);1153x1154} else {1155let x = if x < 0 { 0 } else { x };1156let x = u128::min(x as u128, max);1157x as i1281158};11591160DataValue::int(x, ctrl_ty.lane_type())1161}1162};11631164let x = extractlanes(&arg(0), in_ty)?;11651166assign(vectorizelanes(1167&x.into_iter()1168.map(cvt)1169.collect::<ValueResult<SimdVec<DataValue>>>()?,1170ctrl_ty,1171)?)1172}1173Opcode::FcvtFromUint | Opcode::FcvtFromSint => {1174let x = extractlanes(1175&arg(0),1176inst_context.type_of(inst_context.args()[0]).unwrap(),1177)?;1178let bits = |x: DataValue| -> ValueResult<u64> {1179Ok(match ctrl_ty.lane_type() {1180types::F32 => (if inst.opcode() == Opcode::FcvtFromUint {1181x.into_int_unsigned()? as f321182} else {1183x.into_int_signed()? as f321184})1185.to_bits() as u64,1186types::F64 => (if inst.opcode() == Opcode::FcvtFromUint {1187x.into_int_unsigned()? as f641188} else {1189x.into_int_signed()? as f641190})1191.to_bits(),1192_ => unimplemented!("unexpected conversion to {:?}", ctrl_ty.lane_type()),1193})1194};1195assign(vectorizelanes(1196&x.into_iter()1197.map(|x| DataValue::float(bits(x)?, ctrl_ty.lane_type()))1198.collect::<ValueResult<SimdVec<DataValue>>>()?,1199ctrl_ty,1200)?)1201}1202Opcode::FvpromoteLow => {1203let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();1204assert_eq!(in_ty, types::F32X4);1205let out_ty = types::F64X2;1206let x = extractlanes(&arg(0), in_ty)?;1207assign(vectorizelanes(1208&x[..(out_ty.lane_count() as usize)]1209.into_iter()1210.map(|x| {1211DataValue::convert(1212x.to_owned(),1213ValueConversionKind::Exact(out_ty.lane_type()),1214)1215})1216.collect::<ValueResult<SimdVec<DataValue>>>()?,1217out_ty,1218)?)1219}1220Opcode::Fvdemote => {1221let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();1222assert_eq!(in_ty, types::F64X2);1223let out_ty = types::F32X4;1224let x = extractlanes(&arg(0), in_ty)?;1225let x = &mut x1226.into_iter()1227.map(|x| {1228DataValue::convert(x, ValueConversionKind::RoundNearestEven(out_ty.lane_type()))1229})1230.collect::<ValueResult<SimdVec<DataValue>>>()?;1231// zero the high bits.1232for _ in 0..(out_ty.lane_count() as usize - x.len()) {1233x.push(DataValue::float(0, out_ty.lane_type())?);1234}1235assign(vectorizelanes(x, out_ty)?)1236}1237Opcode::Isplit => assign_multiple(&[1238DataValueExt::convert(arg(0), ValueConversionKind::Truncate(types::I64))?,1239DataValueExt::convert(arg(0), ValueConversionKind::ExtractUpper(types::I64))?,1240]),1241Opcode::Iconcat => assign(DataValueExt::concat(arg(0), arg(1))?),1242Opcode::AtomicRmw => {1243let op = inst.atomic_rmw_op().unwrap();1244let val = arg(1);1245let addr = arg(0).into_int_unsigned()? as u64;1246let mem_flags = inst.memflags().expect("instruction to have memory flags");1247let loaded = Address::try_from(addr)1248.and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));1249let prev_val = match loaded {1250Ok(v) => v,1251Err(e) => return Ok(ControlFlow::Trap(memerror_to_trap(e))),1252};1253let prev_val_to_assign = prev_val.clone();1254let replace = match op {1255AtomicRmwOp::Xchg => Ok(val),1256AtomicRmwOp::Add => DataValueExt::add(prev_val, val),1257AtomicRmwOp::Sub => DataValueExt::sub(prev_val, val),1258AtomicRmwOp::And => DataValueExt::and(prev_val, val),1259AtomicRmwOp::Or => DataValueExt::or(prev_val, val),1260AtomicRmwOp::Xor => DataValueExt::xor(prev_val, val),1261AtomicRmwOp::Nand => DataValueExt::and(prev_val, val).and_then(DataValue::not),1262AtomicRmwOp::Smax => DataValueExt::smax(prev_val, val),1263AtomicRmwOp::Smin => DataValueExt::smin(prev_val, val),1264AtomicRmwOp::Umax => DataValueExt::umax(val, prev_val),1265AtomicRmwOp::Umin => DataValueExt::umin(val, prev_val),1266}?;1267let stored = Address::try_from(addr)1268.and_then(|addr| state.checked_store(addr, replace, mem_flags));1269assign_or_memtrap(stored.map(|_| prev_val_to_assign))1270}1271Opcode::AtomicCas => {1272let addr = arg(0).into_int_unsigned()? as u64;1273let mem_flags = inst.memflags().expect("instruction to have memory flags");1274let loaded = Address::try_from(addr)1275.and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));1276let loaded_val = match loaded {1277Ok(v) => v,1278Err(e) => return Ok(ControlFlow::Trap(memerror_to_trap(e))),1279};1280let expected_val = arg(1);1281let val_to_assign = if loaded_val == expected_val {1282let val_to_store = arg(2);1283Address::try_from(addr)1284.and_then(|addr| state.checked_store(addr, val_to_store, mem_flags))1285.map(|_| loaded_val)1286} else {1287Ok(loaded_val)1288};1289assign_or_memtrap(val_to_assign)1290}1291Opcode::AtomicLoad => {1292let load_ty = inst_context.controlling_type().unwrap();1293let addr = arg(0).into_int_unsigned()? as u64;1294let mem_flags = inst.memflags().expect("instruction to have memory flags");1295// We are doing a regular load here, this isn't actually thread safe.1296assign_or_memtrap(1297Address::try_from(addr)1298.and_then(|addr| state.checked_load(addr, load_ty, mem_flags)),1299)1300}1301Opcode::AtomicStore => {1302let val = arg(0);1303let addr = arg(1).into_int_unsigned()? as u64;1304let mem_flags = inst.memflags().expect("instruction to have memory flags");1305// We are doing a regular store here, this isn't actually thread safe.1306continue_or_memtrap(1307Address::try_from(addr).and_then(|addr| state.checked_store(addr, val, mem_flags)),1308)1309}1310Opcode::Fence => {1311// The interpreter always runs in a single threaded context, so we don't1312// actually need to emit a fence here.1313ControlFlow::Continue1314}1315Opcode::SqmulRoundSat => {1316let lane_type = ctrl_ty.lane_type();1317let double_width = ctrl_ty.double_width().unwrap().lane_type();1318let arg0 = extractlanes(&arg(0), ctrl_ty)?;1319let arg1 = extractlanes(&arg(1), ctrl_ty)?;1320let (min, max) = lane_type.bounds(true);1321let min: DataValue = DataValueExt::int(min as i128, double_width)?;1322let max: DataValue = DataValueExt::int(max as i128, double_width)?;1323let new_vec = arg01324.into_iter()1325.zip(arg1.into_iter())1326.map(|(x, y)| {1327let x = x.into_int_signed()?;1328let y = y.into_int_signed()?;1329// temporarily double width of the value to avoid overflow.1330let z: DataValue = DataValueExt::int(1331(x * y + (1 << (lane_type.bits() - 2))) >> (lane_type.bits() - 1),1332double_width,1333)?;1334// check bounds, saturate, and truncate to correct width.1335let z = DataValueExt::smin(z, max.clone())?;1336let z = DataValueExt::smax(z, min.clone())?;1337let z = z.convert(ValueConversionKind::Truncate(lane_type))?;1338Ok(z)1339})1340.collect::<ValueResult<SimdVec<_>>>()?;1341assign(vectorizelanes(&new_vec, ctrl_ty)?)1342}1343Opcode::IaddPairwise => {1344assign(binary_pairwise(arg(0), arg(1), ctrl_ty, DataValueExt::add)?)1345}1346Opcode::ExtractVector => {1347unimplemented!("ExtractVector not supported");1348}1349Opcode::GetFramePointer => unimplemented!("GetFramePointer"),1350Opcode::GetStackPointer => unimplemented!("GetStackPointer"),1351Opcode::GetReturnAddress => unimplemented!("GetReturnAddress"),1352Opcode::X86Pshufb => unimplemented!("X86Pshufb"),1353Opcode::Blendv => unimplemented!("Blendv"),1354Opcode::X86Pmulhrsw => unimplemented!("X86Pmulhrsw"),1355Opcode::X86Pmaddubsw => unimplemented!("X86Pmaddubsw"),1356Opcode::X86Cvtt2dq => unimplemented!("X86Cvtt2dq"),1357Opcode::StackSwitch => unimplemented!("StackSwitch"),13581359Opcode::TryCall => unimplemented!("TryCall"),1360Opcode::TryCallIndirect => unimplemented!("TryCallIndirect"),13611362Opcode::GetExceptionHandlerAddress => unimplemented!("GetExceptionHandlerAddress"),13631364Opcode::SequencePoint => unimplemented!("SequencePoint"),1365})1366}13671368#[derive(Error, Debug)]1369pub enum StepError {1370#[error("unable to retrieve value from SSA reference: {0}")]1371UnknownValue(ValueRef),1372#[error("unable to find the following function: {0}")]1373UnknownFunction(FuncRef),1374#[error("cannot step with these values")]1375ValueError(#[from] ValueError),1376#[error("failed to access memory")]1377MemoryError(#[from] MemoryError),1378}13791380/// Enumerate the ways in which the control flow can change based on a single step in a Cranelift1381/// interpreter.1382#[derive(Debug, PartialEq)]1383pub enum ControlFlow<'a> {1384/// Return one or more values from an instruction to be assigned to a left-hand side, e.g.:1385/// in `v0 = iadd v1, v2`, the sum of `v1` and `v2` is assigned to `v0`.1386Assign(SmallVec<[DataValue; 1]>),1387/// Continue to the next available instruction, e.g.: in `nop`, we expect to resume execution1388/// at the instruction after it.1389Continue,1390/// Jump to another block with the given parameters, e.g.: in1391/// `brif v0, block42(v1, v2), block97`, if the condition is true, we continue execution at the1392/// first instruction of `block42` with the values in `v1` and `v2` filling in the block1393/// parameters.1394ContinueAt(Block, SmallVec<[DataValue; 1]>),1395/// Indicates a call the given [Function] with the supplied arguments.1396Call(&'a Function, SmallVec<[DataValue; 1]>),1397/// Indicates a tail call to the given [Function] with the supplied arguments.1398ReturnCall(&'a Function, SmallVec<[DataValue; 1]>),1399/// Return from the current function with the given parameters, e.g.: `return [v1, v2]`.1400Return(SmallVec<[DataValue; 1]>),1401/// Stop with a program-generated trap; note that these are distinct from errors that may occur1402/// during interpretation.1403Trap(CraneliftTrap),1404}14051406#[derive(Error, Debug, PartialEq, Eq, Hash)]1407pub enum CraneliftTrap {1408#[error("user code: {0}")]1409User(TrapCode),1410#[error("bad signature")]1411BadSignature,1412#[error("unreachable code has been reached")]1413UnreachableCodeReached,1414#[error("heap is misaligned")]1415HeapMisaligned,1416#[error("user debug")]1417Debug,1418}14191420/// Compare two values using the given integer condition `code`.1421fn icmp(1422ctrl_ty: types::Type,1423code: IntCC,1424left: &DataValue,1425right: &DataValue,1426) -> ValueResult<DataValue> {1427let cmp = |bool_ty: types::Type,1428code: IntCC,1429left: &DataValue,1430right: &DataValue|1431-> ValueResult<DataValue> {1432Ok(DataValueExt::bool(1433match code {1434IntCC::Equal => left == right,1435IntCC::NotEqual => left != right,1436IntCC::SignedGreaterThan => left > right,1437IntCC::SignedGreaterThanOrEqual => left >= right,1438IntCC::SignedLessThan => left < right,1439IntCC::SignedLessThanOrEqual => left <= right,1440IntCC::UnsignedGreaterThan => {1441left.clone().into_int_unsigned()? > right.clone().into_int_unsigned()?1442}1443IntCC::UnsignedGreaterThanOrEqual => {1444left.clone().into_int_unsigned()? >= right.clone().into_int_unsigned()?1445}1446IntCC::UnsignedLessThan => {1447left.clone().into_int_unsigned()? < right.clone().into_int_unsigned()?1448}1449IntCC::UnsignedLessThanOrEqual => {1450left.clone().into_int_unsigned()? <= right.clone().into_int_unsigned()?1451}1452},1453ctrl_ty.is_vector(),1454bool_ty,1455)?)1456};14571458let dst_ty = ctrl_ty.as_truthy();1459let left = extractlanes(left, ctrl_ty)?;1460let right = extractlanes(right, ctrl_ty)?;14611462let res = left1463.into_iter()1464.zip(right.into_iter())1465.map(|(l, r)| cmp(dst_ty.lane_type(), code, &l, &r))1466.collect::<ValueResult<SimdVec<DataValue>>>()?;14671468Ok(vectorizelanes(&res, dst_ty)?)1469}14701471/// Compare two values using the given floating point condition `code`.1472fn fcmp(code: FloatCC, left: &DataValue, right: &DataValue) -> ValueResult<bool> {1473Ok(match code {1474FloatCC::Ordered => left == right || left < right || left > right,1475FloatCC::Unordered => DataValueExt::uno(left, right)?,1476FloatCC::Equal => left == right,1477FloatCC::NotEqual => left < right || left > right || DataValueExt::uno(left, right)?,1478FloatCC::OrderedNotEqual => left < right || left > right,1479FloatCC::UnorderedOrEqual => left == right || DataValueExt::uno(left, right)?,1480FloatCC::LessThan => left < right,1481FloatCC::LessThanOrEqual => left <= right,1482FloatCC::GreaterThan => left > right,1483FloatCC::GreaterThanOrEqual => left >= right,1484FloatCC::UnorderedOrLessThan => DataValueExt::uno(left, right)? || left < right,1485FloatCC::UnorderedOrLessThanOrEqual => DataValueExt::uno(left, right)? || left <= right,1486FloatCC::UnorderedOrGreaterThan => DataValueExt::uno(left, right)? || left > right,1487FloatCC::UnorderedOrGreaterThanOrEqual => DataValueExt::uno(left, right)? || left >= right,1488})1489}14901491pub type SimdVec<DataValue> = SmallVec<[DataValue; 4]>;14921493/// Converts a SIMD vector value into a Rust array of [Value] for processing.1494/// If `x` is a scalar, it will be returned as a single-element array.1495pub(crate) fn extractlanes(1496x: &DataValue,1497vector_type: types::Type,1498) -> ValueResult<SimdVec<DataValue>> {1499let lane_type = vector_type.lane_type();1500let mut lanes = SimdVec::new();1501// Wrap scalar values as a single-element vector and return.1502if !x.ty().is_vector() {1503lanes.push(x.clone());1504return Ok(lanes);1505}15061507let iterations = match lane_type {1508types::I8 => 1,1509types::I16 | types::F16 => 2,1510types::I32 | types::F32 => 4,1511types::I64 | types::F64 => 8,1512_ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),1513};15141515let x = x.into_array()?;1516for i in 0..vector_type.lane_count() {1517let mut lane: i128 = 0;1518for j in 0..iterations {1519lane += (x[((i * iterations) + j) as usize] as i128) << (8 * j);1520}15211522let lane_val: DataValue = if lane_type.is_float() {1523DataValueExt::float(lane as u64, lane_type)?1524} else {1525DataValueExt::int(lane, lane_type)?1526};1527lanes.push(lane_val);1528}1529return Ok(lanes);1530}15311532/// Convert a Rust array of [Value] back into a `Value::vector`.1533/// Supplying a single-element array will simply return its contained value.1534fn vectorizelanes(x: &[DataValue], vector_type: types::Type) -> ValueResult<DataValue> {1535// If the array is only one element, return it as a scalar.1536if x.len() == 1 {1537Ok(x[0].clone())1538} else {1539vectorizelanes_all(x, vector_type)1540}1541}15421543/// Convert a Rust array of [Value] back into a `Value::vector`.1544fn vectorizelanes_all(x: &[DataValue], vector_type: types::Type) -> ValueResult<DataValue> {1545let lane_type = vector_type.lane_type();1546let iterations = match lane_type {1547types::I8 => 1,1548types::I16 | types::F16 => 2,1549types::I32 | types::F32 => 4,1550types::I64 | types::F64 => 8,1551_ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),1552};1553let mut result: [u8; 16] = [0; 16];1554for (i, val) in x.iter().enumerate() {1555let lane_val: i128 = val1556.clone()1557.convert(ValueConversionKind::Exact(lane_type.as_int()))?1558.into_int_unsigned()? as i128;15591560for j in 0..iterations {1561result[(i * iterations) + j] = (lane_val >> (8 * j)) as u8;1562}1563}1564DataValueExt::vector(result, vector_type)1565}15661567/// Performs a lanewise fold on a vector type1568fn fold_vector<F>(v: DataValue, ty: types::Type, init: DataValue, op: F) -> ValueResult<DataValue>1569where1570F: FnMut(DataValue, DataValue) -> ValueResult<DataValue>,1571{1572extractlanes(&v, ty)?.into_iter().try_fold(init, op)1573}15741575/// Performs the supplied unary arithmetic `op` on a Value, either Vector or Scalar.1576fn unary_arith<F>(x: DataValue, vector_type: types::Type, op: F) -> ValueResult<DataValue>1577where1578F: Fn(DataValue) -> ValueResult<DataValue>,1579{1580let arg = extractlanes(&x, vector_type)?;15811582let result = arg1583.into_iter()1584.map(|arg| Ok(op(arg)?))1585.collect::<ValueResult<SimdVec<DataValue>>>()?;15861587vectorizelanes(&result, vector_type)1588}15891590/// Performs the supplied binary arithmetic `op` on two values, either vector or scalar.1591fn binary_arith<F>(1592x: DataValue,1593y: DataValue,1594vector_type: types::Type,1595op: F,1596) -> ValueResult<DataValue>1597where1598F: Fn(DataValue, DataValue) -> ValueResult<DataValue>,1599{1600let arg0 = extractlanes(&x, vector_type)?;1601let arg1 = extractlanes(&y, vector_type)?;16021603let result = arg01604.into_iter()1605.zip(arg1)1606.map(|(lhs, rhs)| Ok(op(lhs, rhs)?))1607.collect::<ValueResult<SimdVec<DataValue>>>()?;16081609vectorizelanes(&result, vector_type)1610}16111612/// Performs the supplied pairwise arithmetic `op` on two SIMD vectors, where1613/// pairs are formed from adjacent vector elements and the vectors are1614/// concatenated at the end.1615fn binary_pairwise<F>(1616x: DataValue,1617y: DataValue,1618vector_type: types::Type,1619op: F,1620) -> ValueResult<DataValue>1621where1622F: Fn(DataValue, DataValue) -> ValueResult<DataValue>,1623{1624let arg0 = extractlanes(&x, vector_type)?;1625let arg1 = extractlanes(&y, vector_type)?;16261627let result = arg01628.chunks(2)1629.chain(arg1.chunks(2))1630.map(|pair| op(pair[0].clone(), pair[1].clone()))1631.collect::<ValueResult<SimdVec<DataValue>>>()?;16321633vectorizelanes(&result, vector_type)1634}16351636fn bitselect(c: DataValue, x: DataValue, y: DataValue) -> ValueResult<DataValue> {1637let mask_x = DataValueExt::and(c.clone(), x)?;1638let mask_y = DataValueExt::and(DataValueExt::not(c)?, y)?;1639DataValueExt::or(mask_x, mask_y)1640}16411642fn splat(ty: Type, val: DataValue) -> ValueResult<DataValue> {1643let mut new_vector = SimdVec::new();1644for _ in 0..ty.lane_count() {1645new_vector.push(val.clone());1646}1647vectorizelanes(&new_vector, ty)1648}16491650// Prepares the shift amount for a shift/rotate operation.1651// The shift amount must be the same type and have the same number of lanes as the vector.1652fn shift_amt(ty: Type, val: DataValue) -> ValueResult<DataValue> {1653splat(ty, val.convert(ValueConversionKind::Exact(ty.lane_type()))?)1654}165516561657