Remove ImmutableRegisterState and replace {get,set}_value in State with current_frame{,_mut} (#6179)

* Remove ImmutableRegisterState

It was introduced for an SCCP optimization pass, but a simplified
version of this will likely use the egraph infrastructure instead.

* Replace {get,set}_value in State with current_frame{,_mut}

The outer Interpreter needs this anyway and only offering one way to
get locals simplifies things.

* Update comment
This commit is contained in:
bjorn3
2023-04-11 14:15:22 +02:00
committed by GitHub
parent 96a60aa26b
commit 52440f0fc8
3 changed files with 216 additions and 342 deletions

View File

@@ -12,7 +12,7 @@ use crate::value::{DataValueExt, ValueError};
use cranelift_codegen::data_value::DataValue; use cranelift_codegen::data_value::DataValue;
use cranelift_codegen::ir::{ use cranelift_codegen::ir::{
ArgumentPurpose, Block, Endianness, ExternalName, FuncRef, Function, GlobalValue, ArgumentPurpose, Block, Endianness, ExternalName, FuncRef, Function, GlobalValue,
GlobalValueData, LibCall, MemFlags, StackSlot, TrapCode, Type, Value as ValueRef, GlobalValueData, LibCall, MemFlags, StackSlot, TrapCode, Type,
}; };
use log::trace; use log::trace;
use smallvec::SmallVec; use smallvec::SmallVec;
@@ -236,22 +236,6 @@ impl<'a> InterpreterState<'a> {
self.libcall_handler = handler; self.libcall_handler = handler;
self self
} }
fn current_frame_mut(&mut self) -> &mut Frame<'a> {
let num_frames = self.frame_stack.len();
match num_frames {
0 => panic!("unable to retrieve the current frame because no frames were pushed"),
_ => &mut self.frame_stack[num_frames - 1],
}
}
fn current_frame(&self) -> &Frame<'a> {
let num_frames = self.frame_stack.len();
match num_frames {
0 => panic!("unable to retrieve the current frame because no frames were pushed"),
_ => &self.frame_stack[num_frames - 1],
}
}
} }
impl<'a> State<'a> for InterpreterState<'a> { impl<'a> State<'a> for InterpreterState<'a> {
@@ -291,12 +275,20 @@ impl<'a> State<'a> for InterpreterState<'a> {
} }
} }
fn get_value(&self, name: ValueRef) -> Option<DataValue> { fn current_frame_mut(&mut self) -> &mut Frame<'a> {
Some(self.current_frame().get(name).clone()) // TODO avoid clone? let num_frames = self.frame_stack.len();
match num_frames {
0 => panic!("unable to retrieve the current frame because no frames were pushed"),
_ => &mut self.frame_stack[num_frames - 1],
}
} }
fn set_value(&mut self, name: ValueRef, value: DataValue) -> Option<DataValue> { fn current_frame(&self) -> &Frame<'a> {
self.current_frame_mut().set(name, value) let num_frames = self.frame_stack.len();
match num_frames {
0 => panic!("unable to retrieve the current frame because no frames were pushed"),
_ => &self.frame_stack[num_frames - 1],
}
} }
fn stack_address( fn stack_address(

View File

@@ -1,6 +1,7 @@
//! Cranelift instructions modify the state of the machine; the [State] trait describes these //! Cranelift instructions modify the state of the machine; the [State] trait describes these
//! ways this can happen. //! ways this can happen.
use crate::address::{Address, AddressSize}; use crate::address::{Address, AddressSize};
use crate::frame::Frame;
use crate::interpreter::LibCallHandler; use crate::interpreter::LibCallHandler;
use cranelift_codegen::data_value::DataValue; use cranelift_codegen::data_value::DataValue;
use cranelift_codegen::ir::{ use cranelift_codegen::ir::{
@@ -8,20 +9,17 @@ use cranelift_codegen::ir::{
Value, Value,
}; };
use cranelift_codegen::isa::CallConv; use cranelift_codegen::isa::CallConv;
use cranelift_entity::PrimaryMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use thiserror::Error; use thiserror::Error;
/// This trait manages the state necessary to interpret a single Cranelift instruction--it describes /// This trait manages the state necessary to interpret a single Cranelift instruction--it describes
/// all of the ways a Cranelift interpreter can interact with its virtual state. This makes it /// all of the ways a Cranelift interpreter can interact with its virtual state. This makes it
/// possible to use the [Interpreter](crate::interpreter::Interpreter) in a range of situations: /// possible to use the [Interpreter](crate::interpreter::Interpreter) in a range of situations:
/// - when interpretation requires understanding all of the ways state can change (e.g. loading and /// - when interpretation needs to happen in a way isolated from the host a state which keeps a
/// storing from the heap) we will use a full-fledged state, like /// stack and bound checks memory accesses can be used, like
/// [InterpreterState](crate::interpreter::InterpreterState). /// [InterpreterState](crate::interpreter::InterpreterState).
/// - when interpretation can ignore some state changes (e.g. abstract interpretation of arithmetic /// - when interpretation needs to have access to the host a state which allows direct access to the
/// instructions--no heap knowledge required), we can partially implement this trait. See /// host memory and native functions can be used.
/// [ImmutableRegisterState] for an example of this: it only exposes the values referenced by the
/// SSA references in the current frame and not much else.
pub trait State<'a> { pub trait State<'a> {
/// Retrieve a reference to a [Function]. /// Retrieve a reference to a [Function].
fn get_function(&self, func_ref: FuncRef) -> Option<&'a Function>; fn get_function(&self, func_ref: FuncRef) -> Option<&'a Function>;
@@ -29,29 +27,19 @@ pub trait State<'a> {
fn get_current_function(&self) -> &'a Function; fn get_current_function(&self) -> &'a Function;
/// Retrieve the handler callback for a [LibCall](cranelift_codegen::ir::LibCall) /// Retrieve the handler callback for a [LibCall](cranelift_codegen::ir::LibCall)
fn get_libcall_handler(&self) -> LibCallHandler; fn get_libcall_handler(&self) -> LibCallHandler;
/// Record that an interpreter has called into a new [Function]. /// Record that an interpreter has called into a new [Function].
fn push_frame(&mut self, function: &'a Function); fn push_frame(&mut self, function: &'a Function);
/// Record that an interpreter has returned from a called [Function]. /// Record that an interpreter has returned from a called [Function].
fn pop_frame(&mut self); fn pop_frame(&mut self);
/// Retrieve a value `V` by its [value reference](cranelift_codegen::ir::Value) from the fn current_frame_mut(&mut self) -> &mut Frame<'a>;
/// virtual register file. fn current_frame(&self) -> &Frame<'a>;
fn get_value(&self, name: Value) -> Option<DataValue>;
/// Assign a value `V` to its [value reference](cranelift_codegen::ir::Value) in the /// Collect a list of values `V` by their [value references](cranelift_codegen::ir::Value).
/// virtual register file. fn collect_values(&self, names: &[Value]) -> SmallVec<[DataValue; 1]> {
fn set_value(&mut self, name: Value, value: DataValue) -> Option<DataValue>; let frame = self.current_frame();
/// Collect a list of values `V` by their [value references](cranelift_codegen::ir::Value); names.into_iter().map(|n| frame.get(*n).clone()).collect()
/// this is a convenience method for `get_value`. If no value is found for a value reference,
/// return an `Err` containing the offending reference.
fn collect_values(&self, names: &[Value]) -> Result<SmallVec<[DataValue; 1]>, Value> {
let mut values = SmallVec::with_capacity(names.len());
for &n in names {
match self.get_value(n) {
None => return Err(n),
Some(v) => values.push(v),
}
}
Ok(values)
} }
/// Computes the stack address for this stack slot, including an offset. /// Computes the stack address for this stack slot, including an offset.
@@ -144,92 +132,3 @@ pub enum MemoryError {
#[error("Store of {store_size} bytes is misaligned at address {addr:?}")] #[error("Store of {store_size} bytes is misaligned at address {addr:?}")]
MisalignedStore { addr: Address, store_size: usize }, MisalignedStore { addr: Address, store_size: usize },
} }
/// This dummy state allows interpretation over an immutable mapping of values in a single frame.
pub struct ImmutableRegisterState<'a>(&'a PrimaryMap<Value, DataValue>);
impl<'a> ImmutableRegisterState<'a> {
pub fn new(values: &'a PrimaryMap<Value, DataValue>) -> Self {
Self(values)
}
}
impl<'a> State<'a> for ImmutableRegisterState<'a> {
fn get_function(&self, _func_ref: FuncRef) -> Option<&'a Function> {
None
}
fn get_current_function(&self) -> &'a Function {
unimplemented!()
}
fn get_libcall_handler(&self) -> LibCallHandler {
unimplemented!()
}
fn push_frame(&mut self, _function: &'a Function) {
unimplemented!()
}
fn pop_frame(&mut self) {
unimplemented!()
}
fn get_value(&self, name: Value) -> Option<DataValue> {
self.0.get(name).cloned()
}
fn set_value(&mut self, _name: Value, _value: DataValue) -> Option<DataValue> {
None
}
fn stack_address(
&self,
_size: AddressSize,
_slot: StackSlot,
_offset: u64,
) -> Result<Address, MemoryError> {
unimplemented!()
}
fn checked_load(
&self,
_addr: Address,
_ty: Type,
_mem_flags: MemFlags,
) -> Result<DataValue, MemoryError> {
unimplemented!()
}
fn checked_store(
&mut self,
_addr: Address,
_v: DataValue,
_mem_flags: MemFlags,
) -> Result<(), MemoryError> {
unimplemented!()
}
fn function_address(
&self,
_size: AddressSize,
_name: &ExternalName,
) -> Result<Address, MemoryError> {
unimplemented!()
}
fn get_function_from_address(&self, _address: Address) -> Option<InterpreterFunctionRef<'a>> {
unimplemented!()
}
fn resolve_global_value(&self, _gv: GlobalValue) -> Result<DataValue, MemoryError> {
unimplemented!()
}
fn get_pinned_reg(&self) -> DataValue {
unimplemented!()
}
fn set_pinned_reg(&mut self, _v: DataValue) {
unimplemented!()
}
}

View File

@@ -67,23 +67,17 @@ where
// frequently close over the `state` or `inst_context` for brevity. // frequently close over the `state` or `inst_context` for brevity.
// Retrieve the current value for an instruction argument. // Retrieve the current value for an instruction argument.
let arg = |index: usize| -> Result<DataValue, StepError> { let arg = |index: usize| -> DataValue {
let value_ref = inst_context.args()[index]; let value_ref = inst_context.args()[index];
state state.current_frame().get(value_ref).clone()
.get_value(value_ref)
.ok_or(StepError::UnknownValue(value_ref))
}; };
// Retrieve the current values for all of an instruction's arguments. // Retrieve the current values for all of an instruction's arguments.
let args = || -> Result<SmallVec<[DataValue; 1]>, StepError> { let args = || -> SmallVec<[DataValue; 1]> { state.collect_values(inst_context.args()) };
state
.collect_values(inst_context.args())
.map_err(|v| StepError::UnknownValue(v))
};
// Retrieve the current values for a range of an instruction's arguments. // Retrieve the current values for a range of an instruction's arguments.
let args_range = |indexes: RangeFrom<usize>| -> Result<SmallVec<[DataValue; 1]>, StepError> { let args_range = |indexes: RangeFrom<usize>| -> Result<SmallVec<[DataValue; 1]>, StepError> {
Ok(SmallVec::<[DataValue; 1]>::from(&args()?[indexes])) Ok(SmallVec::<[DataValue; 1]>::from(&args()[indexes]))
}; };
// Retrieve the immediate value for an instruction, expecting it to exist. // Retrieve the immediate value for an instruction, expecting it to exist.
@@ -261,9 +255,8 @@ where
// Retrieve an instruction's branch destination; expects the instruction to be a branch. // Retrieve an instruction's branch destination; expects the instruction to be a branch.
let continue_at = |block: BlockCall| { let continue_at = |block: BlockCall| {
let branch_args = state let branch_args =
.collect_values(block.args_slice(&state.get_current_function().dfg.value_lists)) state.collect_values(block.args_slice(&state.get_current_function().dfg.value_lists));
.map_err(|v| StepError::UnknownValue(v))?;
Ok(ControlFlow::ContinueAt( Ok(ControlFlow::ContinueAt(
block.block(&state.get_current_function().dfg.value_lists), block.block(&state.get_current_function().dfg.value_lists),
branch_args, branch_args,
@@ -353,7 +346,7 @@ where
.. ..
} = inst } = inst
{ {
let arg = state.get_value(arg).ok_or(StepError::UnknownValue(arg))?; let arg = state.current_frame().get(arg).clone();
let condition = arg.convert(ValueConversionKind::ToBoolean)?.into_bool()?; let condition = arg.convert(ValueConversionKind::ToBoolean)?.into_bool()?;
@@ -371,7 +364,7 @@ where
let jt_data = &state.get_current_function().stencil.dfg.jump_tables[table]; let jt_data = &state.get_current_function().stencil.dfg.jump_tables[table];
// Convert to usize to remove negative indexes from the following operations // Convert to usize to remove negative indexes from the following operations
let jump_target = usize::try_from(arg(0)?.into_int()?) let jump_target = usize::try_from(arg(0).into_int()?)
.ok() .ok()
.and_then(|i| jt_data.as_slice().get(i)) .and_then(|i| jt_data.as_slice().get(i))
.copied() .copied()
@@ -385,10 +378,10 @@ where
Opcode::Trap => ControlFlow::Trap(CraneliftTrap::User(trap_code())), Opcode::Trap => ControlFlow::Trap(CraneliftTrap::User(trap_code())),
Opcode::Debugtrap => ControlFlow::Trap(CraneliftTrap::Debug), Opcode::Debugtrap => ControlFlow::Trap(CraneliftTrap::Debug),
Opcode::ResumableTrap => ControlFlow::Trap(CraneliftTrap::Resumable), Opcode::ResumableTrap => ControlFlow::Trap(CraneliftTrap::Resumable),
Opcode::Trapz => trap_when(!arg(0)?.into_bool()?, CraneliftTrap::User(trap_code())), Opcode::Trapz => trap_when(!arg(0).into_bool()?, CraneliftTrap::User(trap_code())),
Opcode::Trapnz => trap_when(arg(0)?.into_bool()?, CraneliftTrap::User(trap_code())), Opcode::Trapnz => trap_when(arg(0).into_bool()?, CraneliftTrap::User(trap_code())),
Opcode::ResumableTrapnz => trap_when(arg(0)?.into_bool()?, CraneliftTrap::Resumable), Opcode::ResumableTrapnz => trap_when(arg(0).into_bool()?, CraneliftTrap::Resumable),
Opcode::Return => ControlFlow::Return(args()?), Opcode::Return => ControlFlow::Return(args()),
Opcode::Call | Opcode::ReturnCall => { Opcode::Call | Opcode::ReturnCall => {
let func_ref = if let InstructionData::Call { func_ref, .. } = inst { let func_ref = if let InstructionData::Call { func_ref, .. } = inst {
func_ref func_ref
@@ -403,7 +396,7 @@ where
.get(func_ref) .get(func_ref)
.ok_or(StepError::UnknownFunction(func_ref))?; .ok_or(StepError::UnknownFunction(func_ref))?;
let args = args()?; let args = args();
let func = match ext_data.name { let func = match ext_data.name {
// These functions should be registered in the regular function store // These functions should be registered in the regular function store
ExternalName::User(_) | ExternalName::TestCase(_) => { ExternalName::User(_) | ExternalName::TestCase(_) => {
@@ -425,8 +418,8 @@ where
call_func(func, args, make_control_flow)? call_func(func, args, make_control_flow)?
} }
Opcode::CallIndirect | Opcode::ReturnCallIndirect => { Opcode::CallIndirect | Opcode::ReturnCallIndirect => {
let args = args()?; let args = args();
let addr_dv = DataValue::U64(arg(0)?.into_int()? as u64); let addr_dv = DataValue::U64(arg(0).into_int()? as u64);
let addr = Address::try_from(addr_dv.clone()).map_err(StepError::MemoryError)?; let addr = Address::try_from(addr_dv.clone()).map_err(StepError::MemoryError)?;
let func = state let func = state
@@ -497,7 +490,7 @@ where
_ => unreachable!(), _ => unreachable!(),
}; };
let addr_value = calculate_addr(types::I64, imm(), args()?)?; let addr_value = calculate_addr(types::I64, imm(), args())?;
let mem_flags = inst.memflags().expect("instruction to have memory flags"); let mem_flags = inst.memflags().expect("instruction to have memory flags");
let loaded = assign_or_memtrap( let loaded = assign_or_memtrap(
Address::try_from(addr_value) Address::try_from(addr_value)
@@ -525,9 +518,9 @@ where
let addr_value = calculate_addr(types::I64, imm(), args_range(1..)?)?; let addr_value = calculate_addr(types::I64, imm(), args_range(1..)?)?;
let mem_flags = inst.memflags().expect("instruction to have memory flags"); let mem_flags = inst.memflags().expect("instruction to have memory flags");
let reduced = if let Some(c) = kind { let reduced = if let Some(c) = kind {
arg(0)?.convert(c)? arg(0).convert(c)?
} else { } else {
arg(0)? arg(0)
}; };
continue_or_memtrap( continue_or_memtrap(
Address::try_from(addr_value) Address::try_from(addr_value)
@@ -537,7 +530,7 @@ where
Opcode::StackLoad => { Opcode::StackLoad => {
let load_ty = inst_context.controlling_type().unwrap(); let load_ty = inst_context.controlling_type().unwrap();
let slot = inst.stack_slot().unwrap(); let slot = inst.stack_slot().unwrap();
let offset = sum(imm(), args()?)? as u64; let offset = sum(imm(), args())? as u64;
let mem_flags = MemFlags::new(); let mem_flags = MemFlags::new();
assign_or_memtrap({ assign_or_memtrap({
state state
@@ -546,7 +539,7 @@ where
}) })
} }
Opcode::StackStore => { Opcode::StackStore => {
let arg = arg(0)?; let arg = arg(0);
let slot = inst.stack_slot().unwrap(); let slot = inst.stack_slot().unwrap();
let offset = sum(imm(), args_range(1..)?)? as u64; let offset = sum(imm(), args_range(1..)?)? as u64;
let mem_flags = MemFlags::new(); let mem_flags = MemFlags::new();
@@ -559,7 +552,7 @@ where
Opcode::StackAddr => { Opcode::StackAddr => {
let load_ty = inst_context.controlling_type().unwrap(); let load_ty = inst_context.controlling_type().unwrap();
let slot = inst.stack_slot().unwrap(); let slot = inst.stack_slot().unwrap();
let offset = sum(imm(), args()?)? as u64; let offset = sum(imm(), args())? as u64;
assign_or_memtrap({ assign_or_memtrap({
AddressSize::try_from(load_ty).and_then(|addr_size| { AddressSize::try_from(load_ty).and_then(|addr_size| {
let addr = state.stack_address(addr_size, slot, offset)?; let addr = state.stack_address(addr_size, slot, offset)?;
@@ -580,7 +573,7 @@ where
} }
Opcode::GetPinnedReg => assign(state.get_pinned_reg()), Opcode::GetPinnedReg => assign(state.get_pinned_reg()),
Opcode::SetPinnedReg => { Opcode::SetPinnedReg => {
let arg0 = arg(0)?; let arg0 = arg(0);
state.set_pinned_reg(arg0); state.set_pinned_reg(arg0);
ControlFlow::Continue ControlFlow::Continue
} }
@@ -593,7 +586,7 @@ where
let element_size = DataValue::int(u64::from(table.element_size) as i128, index_ty)?; let element_size = DataValue::int(u64::from(table.element_size) as i128, index_ty)?;
let inst_offset = DataValue::int(i32::from(offset) as i128, index_ty)?; let inst_offset = DataValue::int(i32::from(offset) as i128, index_ty)?;
let byte_offset = arg(0)?.mul(element_size.clone())?.add(inst_offset)?; let byte_offset = arg(0).mul(element_size.clone())?.add(inst_offset)?;
let bound_bytes = bound.mul(element_size)?; let bound_bytes = bound.mul(element_size)?;
if byte_offset > bound_bytes { if byte_offset > bound_bytes {
return Ok(ControlFlow::Trap(CraneliftTrap::User( return Ok(ControlFlow::Trap(CraneliftTrap::User(
@@ -612,106 +605,99 @@ where
Opcode::Vconst => assign(imm()), Opcode::Vconst => assign(imm()),
Opcode::Null => unimplemented!("Null"), Opcode::Null => unimplemented!("Null"),
Opcode::Nop => ControlFlow::Continue, Opcode::Nop => ControlFlow::Continue,
Opcode::Select | Opcode::SelectSpectreGuard => { Opcode::Select | Opcode::SelectSpectreGuard => choose(arg(0).into_bool()?, arg(1), arg(2)),
choose(arg(0)?.into_bool()?, arg(1)?, arg(2)?) Opcode::Bitselect => assign(bitselect(arg(0), arg(1), arg(2))?),
} Opcode::Icmp => assign(icmp(ctrl_ty, inst.cond_code().unwrap(), &arg(0), &arg(1))?),
Opcode::Bitselect => assign(bitselect(arg(0)?, arg(1)?, arg(2)?)?),
Opcode::Icmp => assign(icmp(
ctrl_ty,
inst.cond_code().unwrap(),
&arg(0)?,
&arg(1)?,
)?),
Opcode::IcmpImm => assign(icmp( Opcode::IcmpImm => assign(icmp(
ctrl_ty, ctrl_ty,
inst.cond_code().unwrap(), inst.cond_code().unwrap(),
&arg(0)?, &arg(0),
&imm_as_ctrl_ty()?, &imm_as_ctrl_ty()?,
)?), )?),
Opcode::Smin => { Opcode::Smin => {
if ctrl_ty.is_vector() { if ctrl_ty.is_vector() {
let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(1)?, &arg(0)?)?; let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(1), &arg(0))?;
assign(bitselect(icmp, arg(0)?, arg(1)?)?) assign(bitselect(icmp, arg(0), arg(1))?)
} else { } else {
choose(arg(1)? > arg(0)?, arg(0)?, arg(1)?) choose(arg(1) > arg(0), arg(0), arg(1))
} }
} }
Opcode::Umin => { Opcode::Umin => {
if ctrl_ty.is_vector() { if ctrl_ty.is_vector() {
let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(1)?, &arg(0)?)?; let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(1), &arg(0))?;
assign(bitselect(icmp, arg(0)?, arg(1)?)?) assign(bitselect(icmp, arg(0), arg(1))?)
} else { } else {
choose( choose(
arg(1)?.convert(ValueConversionKind::ToUnsigned)? arg(1).convert(ValueConversionKind::ToUnsigned)?
> arg(0)?.convert(ValueConversionKind::ToUnsigned)?, > arg(0).convert(ValueConversionKind::ToUnsigned)?,
arg(0)?, arg(0),
arg(1)?, arg(1),
) )
} }
} }
Opcode::Smax => { Opcode::Smax => {
if ctrl_ty.is_vector() { if ctrl_ty.is_vector() {
let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(0)?, &arg(1)?)?; let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(0), &arg(1))?;
assign(bitselect(icmp, arg(0)?, arg(1)?)?) assign(bitselect(icmp, arg(0), arg(1))?)
} else { } else {
choose(arg(0)? > arg(1)?, arg(0)?, arg(1)?) choose(arg(0) > arg(1), arg(0), arg(1))
} }
} }
Opcode::Umax => { Opcode::Umax => {
if ctrl_ty.is_vector() { if ctrl_ty.is_vector() {
let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(0)?, &arg(1)?)?; let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(0), &arg(1))?;
assign(bitselect(icmp, arg(0)?, arg(1)?)?) assign(bitselect(icmp, arg(0), arg(1))?)
} else { } else {
choose( choose(
arg(0)?.convert(ValueConversionKind::ToUnsigned)? arg(0).convert(ValueConversionKind::ToUnsigned)?
> arg(1)?.convert(ValueConversionKind::ToUnsigned)?, > arg(1).convert(ValueConversionKind::ToUnsigned)?,
arg(0)?, arg(0),
arg(1)?, arg(1),
) )
} }
} }
Opcode::AvgRound => { Opcode::AvgRound => {
let sum = DataValueExt::add(arg(0)?, arg(1)?)?; let sum = DataValueExt::add(arg(0), arg(1))?;
let one = DataValueExt::int(1, arg(0)?.ty())?; let one = DataValueExt::int(1, arg(0).ty())?;
let inc = DataValueExt::add(sum, one)?; let inc = DataValueExt::add(sum, one)?;
let two = DataValueExt::int(2, arg(0)?.ty())?; let two = DataValueExt::int(2, arg(0).ty())?;
binary(DataValueExt::div, inc, two)? binary(DataValueExt::div, inc, two)?
} }
Opcode::Iadd => binary(DataValueExt::add, arg(0)?, arg(1)?)?, Opcode::Iadd => binary(DataValueExt::add, arg(0), arg(1))?,
Opcode::UaddSat => assign(binary_arith( Opcode::UaddSat => assign(binary_arith(
arg(0)?, arg(0),
arg(1)?, arg(1),
ctrl_ty, ctrl_ty,
DataValueExt::add_sat, DataValueExt::add_sat,
true, true,
)?), )?),
Opcode::SaddSat => assign(binary_arith( Opcode::SaddSat => assign(binary_arith(
arg(0)?, arg(0),
arg(1)?, arg(1),
ctrl_ty, ctrl_ty,
DataValueExt::add_sat, DataValueExt::add_sat,
false, false,
)?), )?),
Opcode::Isub => binary(DataValueExt::sub, arg(0)?, arg(1)?)?, Opcode::Isub => binary(DataValueExt::sub, arg(0), arg(1))?,
Opcode::UsubSat => assign(binary_arith( Opcode::UsubSat => assign(binary_arith(
arg(0)?, arg(0),
arg(1)?, arg(1),
ctrl_ty, ctrl_ty,
DataValueExt::sub_sat, DataValueExt::sub_sat,
true, true,
)?), )?),
Opcode::SsubSat => assign(binary_arith( Opcode::SsubSat => assign(binary_arith(
arg(0)?, arg(0),
arg(1)?, arg(1),
ctrl_ty, ctrl_ty,
DataValueExt::sub_sat, DataValueExt::sub_sat,
false, false,
)?), )?),
Opcode::Ineg => binary(DataValueExt::sub, DataValueExt::int(0, ctrl_ty)?, arg(0)?)?, Opcode::Ineg => binary(DataValueExt::sub, DataValueExt::int(0, ctrl_ty)?, arg(0))?,
Opcode::Iabs => { Opcode::Iabs => {
let (min_val, _) = ctrl_ty.lane_type().bounds(true); let (min_val, _) = ctrl_ty.lane_type().bounds(true);
let min_val: DataValue = DataValueExt::int(min_val as i128, ctrl_ty.lane_type())?; let min_val: DataValue = DataValueExt::int(min_val as i128, ctrl_ty.lane_type())?;
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?; let arg0 = extractlanes(&arg(0), ctrl_ty)?;
let new_vec = arg0 let new_vec = arg0
.into_iter() .into_iter()
.map(|lane| { .map(|lane| {
@@ -724,7 +710,7 @@ where
.collect::<ValueResult<SimdVec<DataValue>>>()?; .collect::<ValueResult<SimdVec<DataValue>>>()?;
assign(vectorizelanes(&new_vec, ctrl_ty)?) assign(vectorizelanes(&new_vec, ctrl_ty)?)
} }
Opcode::Imul => binary(DataValueExt::mul, arg(0)?, arg(1)?)?, Opcode::Imul => binary(DataValueExt::mul, arg(0), arg(1))?,
Opcode::Umulhi | Opcode::Smulhi => { Opcode::Umulhi | Opcode::Smulhi => {
let double_length = match ctrl_ty.lane_bits() { let double_length = match ctrl_ty.lane_bits() {
8 => types::I16, 8 => types::I16,
@@ -738,8 +724,8 @@ where
} else { } else {
ValueConversionKind::SignExtend(double_length) ValueConversionKind::SignExtend(double_length)
}; };
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?; let arg0 = extractlanes(&arg(0), ctrl_ty)?;
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?; let arg1 = extractlanes(&arg(1), ctrl_ty)?;
let res = arg0 let res = arg0
.into_iter() .into_iter()
@@ -755,35 +741,35 @@ where
assign(vectorizelanes(&res, ctrl_ty)?) assign(vectorizelanes(&res, ctrl_ty)?)
} }
Opcode::Udiv => binary_unsigned_can_trap(DataValueExt::div, arg(0)?, arg(1)?)?, Opcode::Udiv => binary_unsigned_can_trap(DataValueExt::div, arg(0), arg(1))?,
Opcode::Sdiv => binary_can_trap(DataValueExt::div, arg(0)?, arg(1)?)?, Opcode::Sdiv => binary_can_trap(DataValueExt::div, arg(0), arg(1))?,
Opcode::Urem => binary_unsigned_can_trap(DataValueExt::rem, arg(0)?, arg(1)?)?, Opcode::Urem => binary_unsigned_can_trap(DataValueExt::rem, arg(0), arg(1))?,
Opcode::Srem => binary_can_trap(DataValueExt::rem, arg(0)?, arg(1)?)?, Opcode::Srem => binary_can_trap(DataValueExt::rem, arg(0), arg(1))?,
Opcode::IaddImm => binary(DataValueExt::add, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::IaddImm => binary(DataValueExt::add, arg(0), imm_as_ctrl_ty()?)?,
Opcode::ImulImm => binary(DataValueExt::mul, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::ImulImm => binary(DataValueExt::mul, arg(0), imm_as_ctrl_ty()?)?,
Opcode::UdivImm => binary_unsigned_can_trap(DataValueExt::div, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::UdivImm => binary_unsigned_can_trap(DataValueExt::div, arg(0), imm_as_ctrl_ty()?)?,
Opcode::SdivImm => binary_can_trap(DataValueExt::div, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::SdivImm => binary_can_trap(DataValueExt::div, arg(0), imm_as_ctrl_ty()?)?,
Opcode::UremImm => binary_unsigned_can_trap(DataValueExt::rem, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::UremImm => binary_unsigned_can_trap(DataValueExt::rem, arg(0), imm_as_ctrl_ty()?)?,
Opcode::SremImm => binary_can_trap(DataValueExt::rem, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::SremImm => binary_can_trap(DataValueExt::rem, arg(0), imm_as_ctrl_ty()?)?,
Opcode::IrsubImm => binary(DataValueExt::sub, imm_as_ctrl_ty()?, arg(0)?)?, Opcode::IrsubImm => binary(DataValueExt::sub, imm_as_ctrl_ty()?, arg(0))?,
Opcode::IaddCin => choose( Opcode::IaddCin => choose(
DataValueExt::into_bool(arg(2)?)?, DataValueExt::into_bool(arg(2))?,
DataValueExt::add( DataValueExt::add(
DataValueExt::add(arg(0)?, arg(1)?)?, DataValueExt::add(arg(0), arg(1))?,
DataValueExt::int(1, ctrl_ty)?, DataValueExt::int(1, ctrl_ty)?,
)?, )?,
DataValueExt::add(arg(0)?, arg(1)?)?, DataValueExt::add(arg(0), arg(1))?,
), ),
Opcode::IaddCout => { Opcode::IaddCout => {
let carry = arg(0)?.checked_add(arg(1)?)?.is_none(); let carry = arg(0).checked_add(arg(1))?.is_none();
let sum = arg(0)?.add(arg(1)?)?; let sum = arg(0).add(arg(1))?;
assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?]) assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
} }
Opcode::IaddCarry => { Opcode::IaddCarry => {
let mut sum = DataValueExt::add(arg(0)?, arg(1)?)?; let mut sum = DataValueExt::add(arg(0), arg(1))?;
let mut carry = arg(0)?.checked_add(arg(1)?)?.is_none(); let mut carry = arg(0).checked_add(arg(1))?.is_none();
if DataValueExt::into_bool(arg(2)?)? { if DataValueExt::into_bool(arg(2))? {
carry |= sum carry |= sum
.clone() .clone()
.checked_add(DataValueExt::int(1, ctrl_ty)?)? .checked_add(DataValueExt::int(1, ctrl_ty)?)?
@@ -794,8 +780,8 @@ where
assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?]) assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
} }
Opcode::UaddOverflowTrap => { Opcode::UaddOverflowTrap => {
let sum = DataValueExt::add(arg(0)?, arg(1)?)?; let sum = DataValueExt::add(arg(0), arg(1))?;
let carry = sum < arg(0)? && sum < arg(1)?; let carry = sum < arg(0) && sum < arg(1);
if carry { if carry {
ControlFlow::Trap(CraneliftTrap::User(trap_code())) ControlFlow::Trap(CraneliftTrap::User(trap_code()))
} else { } else {
@@ -803,65 +789,65 @@ where
} }
} }
Opcode::IsubBin => choose( Opcode::IsubBin => choose(
DataValueExt::into_bool(arg(2)?)?, DataValueExt::into_bool(arg(2))?,
DataValueExt::sub( DataValueExt::sub(
arg(0)?, arg(0),
DataValueExt::add(arg(1)?, DataValueExt::int(1, ctrl_ty)?)?, DataValueExt::add(arg(1), DataValueExt::int(1, ctrl_ty)?)?,
)?, )?,
DataValueExt::sub(arg(0)?, arg(1)?)?, DataValueExt::sub(arg(0), arg(1))?,
), ),
Opcode::IsubBout => { Opcode::IsubBout => {
let sum = DataValueExt::sub(arg(0)?, arg(1)?)?; let sum = DataValueExt::sub(arg(0), arg(1))?;
let borrow = arg(0)? < arg(1)?; let borrow = arg(0) < arg(1);
assign_multiple(&[sum, DataValueExt::bool(borrow, false, types::I8)?]) assign_multiple(&[sum, DataValueExt::bool(borrow, false, types::I8)?])
} }
Opcode::IsubBorrow => { Opcode::IsubBorrow => {
let rhs = if DataValueExt::into_bool(arg(2)?)? { let rhs = if DataValueExt::into_bool(arg(2))? {
DataValueExt::add(arg(1)?, DataValueExt::int(1, ctrl_ty)?)? DataValueExt::add(arg(1), DataValueExt::int(1, ctrl_ty)?)?
} else { } else {
arg(1)? arg(1)
}; };
let borrow = arg(0)? < rhs; let borrow = arg(0) < rhs;
let sum = DataValueExt::sub(arg(0)?, rhs)?; let sum = DataValueExt::sub(arg(0), rhs)?;
assign_multiple(&[sum, DataValueExt::bool(borrow, false, types::I8)?]) assign_multiple(&[sum, DataValueExt::bool(borrow, false, types::I8)?])
} }
Opcode::Band => binary(DataValueExt::and, arg(0)?, arg(1)?)?, Opcode::Band => binary(DataValueExt::and, arg(0), arg(1))?,
Opcode::Bor => binary(DataValueExt::or, arg(0)?, arg(1)?)?, Opcode::Bor => binary(DataValueExt::or, arg(0), arg(1))?,
Opcode::Bxor => binary(DataValueExt::xor, arg(0)?, arg(1)?)?, Opcode::Bxor => binary(DataValueExt::xor, arg(0), arg(1))?,
Opcode::Bnot => unary(DataValueExt::not, arg(0)?)?, Opcode::Bnot => unary(DataValueExt::not, arg(0))?,
Opcode::BandNot => binary(DataValueExt::and, arg(0)?, DataValueExt::not(arg(1)?)?)?, Opcode::BandNot => binary(DataValueExt::and, arg(0), DataValueExt::not(arg(1))?)?,
Opcode::BorNot => binary(DataValueExt::or, arg(0)?, DataValueExt::not(arg(1)?)?)?, Opcode::BorNot => binary(DataValueExt::or, arg(0), DataValueExt::not(arg(1))?)?,
Opcode::BxorNot => binary(DataValueExt::xor, arg(0)?, DataValueExt::not(arg(1)?)?)?, Opcode::BxorNot => binary(DataValueExt::xor, arg(0), DataValueExt::not(arg(1))?)?,
Opcode::BandImm => binary(DataValueExt::and, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::BandImm => binary(DataValueExt::and, arg(0), imm_as_ctrl_ty()?)?,
Opcode::BorImm => binary(DataValueExt::or, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::BorImm => binary(DataValueExt::or, arg(0), imm_as_ctrl_ty()?)?,
Opcode::BxorImm => binary(DataValueExt::xor, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::BxorImm => binary(DataValueExt::xor, arg(0), imm_as_ctrl_ty()?)?,
Opcode::Rotl => binary(DataValueExt::rotl, arg(0)?, arg(1)?)?, Opcode::Rotl => binary(DataValueExt::rotl, arg(0), arg(1))?,
Opcode::Rotr => binary(DataValueExt::rotr, arg(0)?, arg(1)?)?, Opcode::Rotr => binary(DataValueExt::rotr, arg(0), arg(1))?,
Opcode::RotlImm => binary(DataValueExt::rotl, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::RotlImm => binary(DataValueExt::rotl, arg(0), imm_as_ctrl_ty()?)?,
Opcode::RotrImm => binary(DataValueExt::rotr, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::RotrImm => binary(DataValueExt::rotr, arg(0), imm_as_ctrl_ty()?)?,
Opcode::Ishl => binary(DataValueExt::shl, arg(0)?, arg(1)?)?, Opcode::Ishl => binary(DataValueExt::shl, arg(0), arg(1))?,
Opcode::Ushr => binary_unsigned(DataValueExt::ushr, arg(0)?, arg(1)?)?, Opcode::Ushr => binary_unsigned(DataValueExt::ushr, arg(0), arg(1))?,
Opcode::Sshr => binary(DataValueExt::ishr, arg(0)?, arg(1)?)?, Opcode::Sshr => binary(DataValueExt::ishr, arg(0), arg(1))?,
Opcode::IshlImm => binary(DataValueExt::shl, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::IshlImm => binary(DataValueExt::shl, arg(0), imm_as_ctrl_ty()?)?,
Opcode::UshrImm => binary_unsigned(DataValueExt::ushr, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::UshrImm => binary_unsigned(DataValueExt::ushr, arg(0), imm_as_ctrl_ty()?)?,
Opcode::SshrImm => binary(DataValueExt::ishr, arg(0)?, imm_as_ctrl_ty()?)?, Opcode::SshrImm => binary(DataValueExt::ishr, arg(0), imm_as_ctrl_ty()?)?,
Opcode::Bitrev => unary(DataValueExt::reverse_bits, arg(0)?)?, Opcode::Bitrev => unary(DataValueExt::reverse_bits, arg(0))?,
Opcode::Bswap => unary(DataValueExt::swap_bytes, arg(0)?)?, Opcode::Bswap => unary(DataValueExt::swap_bytes, arg(0))?,
Opcode::Clz => unary(DataValueExt::leading_zeros, arg(0)?)?, Opcode::Clz => unary(DataValueExt::leading_zeros, arg(0))?,
Opcode::Cls => { Opcode::Cls => {
let count = if arg(0)? < DataValueExt::int(0, ctrl_ty)? { let count = if arg(0) < DataValueExt::int(0, ctrl_ty)? {
arg(0)?.leading_ones()? arg(0).leading_ones()?
} else { } else {
arg(0)?.leading_zeros()? arg(0).leading_zeros()?
}; };
assign(DataValueExt::sub(count, DataValueExt::int(1, ctrl_ty)?)?) assign(DataValueExt::sub(count, DataValueExt::int(1, ctrl_ty)?)?)
} }
Opcode::Ctz => unary(DataValueExt::trailing_zeros, arg(0)?)?, Opcode::Ctz => unary(DataValueExt::trailing_zeros, arg(0))?,
Opcode::Popcnt => { Opcode::Popcnt => {
let count = if arg(0)?.ty().is_int() { let count = if arg(0).ty().is_int() {
arg(0)?.count_ones()? arg(0).count_ones()?
} else { } else {
let lanes = extractlanes(&arg(0)?, ctrl_ty)? let lanes = extractlanes(&arg(0), ctrl_ty)?
.into_iter() .into_iter()
.map(|lane| lane.count_ones()) .map(|lane| lane.count_ones())
.collect::<ValueResult<SimdVec<DataValue>>>()?; .collect::<ValueResult<SimdVec<DataValue>>>()?;
@@ -871,8 +857,8 @@ where
} }
Opcode::Fcmp => { Opcode::Fcmp => {
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?; let arg0 = extractlanes(&arg(0), ctrl_ty)?;
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?; let arg1 = extractlanes(&arg(1), ctrl_ty)?;
assign(vectorizelanes( assign(vectorizelanes(
&(arg0 &(arg0
@@ -889,15 +875,15 @@ where
ctrl_ty, ctrl_ty,
)?) )?)
} }
Opcode::Fadd => binary(DataValueExt::add, arg(0)?, arg(1)?)?, Opcode::Fadd => binary(DataValueExt::add, arg(0), arg(1))?,
Opcode::Fsub => binary(DataValueExt::sub, arg(0)?, arg(1)?)?, Opcode::Fsub => binary(DataValueExt::sub, arg(0), arg(1))?,
Opcode::Fmul => binary(DataValueExt::mul, arg(0)?, arg(1)?)?, Opcode::Fmul => binary(DataValueExt::mul, arg(0), arg(1))?,
Opcode::Fdiv => binary(DataValueExt::div, arg(0)?, arg(1)?)?, Opcode::Fdiv => binary(DataValueExt::div, arg(0), arg(1))?,
Opcode::Sqrt => unary(DataValueExt::sqrt, arg(0)?)?, Opcode::Sqrt => unary(DataValueExt::sqrt, arg(0))?,
Opcode::Fma => { Opcode::Fma => {
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?; let arg0 = extractlanes(&arg(0), ctrl_ty)?;
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?; let arg1 = extractlanes(&arg(1), ctrl_ty)?;
let arg2 = extractlanes(&arg(2)?, ctrl_ty)?; let arg2 = extractlanes(&arg(2), ctrl_ty)?;
assign(vectorizelanes( assign(vectorizelanes(
&(arg0 &(arg0
@@ -909,42 +895,42 @@ where
ctrl_ty, ctrl_ty,
)?) )?)
} }
Opcode::Fneg => unary(DataValueExt::neg, arg(0)?)?, Opcode::Fneg => unary(DataValueExt::neg, arg(0))?,
Opcode::Fabs => unary(DataValueExt::abs, arg(0)?)?, Opcode::Fabs => unary(DataValueExt::abs, arg(0))?,
Opcode::Fcopysign => binary(DataValueExt::copysign, arg(0)?, arg(1)?)?, Opcode::Fcopysign => binary(DataValueExt::copysign, arg(0), arg(1))?,
Opcode::Fmin => assign(match (arg(0)?, arg(1)?) { Opcode::Fmin => assign(match (arg(0), arg(1)) {
(a, _) if a.is_nan()? => a, (a, _) if a.is_nan()? => a,
(_, b) if b.is_nan()? => b, (_, b) if b.is_nan()? => b,
(a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => a, (a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => a,
(a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => b, (a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => b,
(a, b) => a.min(b)?, (a, b) => a.min(b)?,
}), }),
Opcode::FminPseudo => assign(match (arg(0)?, arg(1)?) { Opcode::FminPseudo => assign(match (arg(0), arg(1)) {
(a, b) if a.is_nan()? || b.is_nan()? => a, (a, b) if a.is_nan()? || b.is_nan()? => a,
(a, b) if a.is_zero()? && b.is_zero()? => a, (a, b) if a.is_zero()? && b.is_zero()? => a,
(a, b) => a.min(b)?, (a, b) => a.min(b)?,
}), }),
Opcode::Fmax => assign(match (arg(0)?, arg(1)?) { Opcode::Fmax => assign(match (arg(0), arg(1)) {
(a, _) if a.is_nan()? => a, (a, _) if a.is_nan()? => a,
(_, b) if b.is_nan()? => b, (_, b) if b.is_nan()? => b,
(a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => b, (a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => b,
(a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => a, (a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => a,
(a, b) => a.max(b)?, (a, b) => a.max(b)?,
}), }),
Opcode::FmaxPseudo => assign(match (arg(0)?, arg(1)?) { Opcode::FmaxPseudo => assign(match (arg(0), arg(1)) {
(a, b) if a.is_nan()? || b.is_nan()? => a, (a, b) if a.is_nan()? || b.is_nan()? => a,
(a, b) if a.is_zero()? && b.is_zero()? => a, (a, b) if a.is_zero()? && b.is_zero()? => a,
(a, b) => a.max(b)?, (a, b) => a.max(b)?,
}), }),
Opcode::Ceil => unary(DataValueExt::ceil, arg(0)?)?, Opcode::Ceil => unary(DataValueExt::ceil, arg(0))?,
Opcode::Floor => unary(DataValueExt::floor, arg(0)?)?, Opcode::Floor => unary(DataValueExt::floor, arg(0))?,
Opcode::Trunc => unary(DataValueExt::trunc, arg(0)?)?, Opcode::Trunc => unary(DataValueExt::trunc, arg(0))?,
Opcode::Nearest => unary(DataValueExt::nearest, arg(0)?)?, Opcode::Nearest => unary(DataValueExt::nearest, arg(0))?,
Opcode::IsNull => unimplemented!("IsNull"), Opcode::IsNull => unimplemented!("IsNull"),
Opcode::IsInvalid => unimplemented!("IsInvalid"), Opcode::IsInvalid => unimplemented!("IsInvalid"),
Opcode::Bitcast | Opcode::ScalarToVector => { Opcode::Bitcast | Opcode::ScalarToVector => {
let input_ty = inst_context.type_of(inst_context.args()[0]).unwrap(); let input_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
let arg0 = extractlanes(&arg(0)?, input_ty)?; let arg0 = extractlanes(&arg(0), input_ty)?;
let lanes = &arg0 let lanes = &arg0
.into_iter() .into_iter()
.map(|x| DataValue::convert(x, ValueConversionKind::Exact(ctrl_ty.lane_type()))) .map(|x| DataValue::convert(x, ValueConversionKind::Exact(ctrl_ty.lane_type())))
@@ -956,12 +942,12 @@ where
}) })
} }
Opcode::Ireduce => assign(DataValueExt::convert( Opcode::Ireduce => assign(DataValueExt::convert(
arg(0)?, arg(0),
ValueConversionKind::Truncate(ctrl_ty), ValueConversionKind::Truncate(ctrl_ty),
)?), )?),
Opcode::Snarrow | Opcode::Unarrow | Opcode::Uunarrow => { Opcode::Snarrow | Opcode::Unarrow | Opcode::Uunarrow => {
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?; let arg0 = extractlanes(&arg(0), ctrl_ty)?;
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?; let arg1 = extractlanes(&arg(1), ctrl_ty)?;
let new_type = ctrl_ty.split_lanes().unwrap(); let new_type = ctrl_ty.split_lanes().unwrap();
let (min, max) = new_type.bounds(inst.opcode() == Opcode::Snarrow); let (min, max) = new_type.bounds(inst.opcode() == Opcode::Snarrow);
let mut min: DataValue = DataValueExt::int(min as i128, ctrl_ty.lane_type())?; let mut min: DataValue = DataValueExt::int(min as i128, ctrl_ty.lane_type())?;
@@ -990,7 +976,7 @@ where
assign(vectorizelanes(&new_vec, new_type)?) assign(vectorizelanes(&new_vec, new_type)?)
} }
Opcode::Bmask => assign({ Opcode::Bmask => assign({
let bool = arg(0)?; let bool = arg(0);
let bool_ty = ctrl_ty.as_truthy_pedantic(); let bool_ty = ctrl_ty.as_truthy_pedantic();
let lanes = extractlanes(&bool, bool_ty)? let lanes = extractlanes(&bool, bool_ty)?
.into_iter() .into_iter()
@@ -999,25 +985,25 @@ where
vectorizelanes(&lanes, ctrl_ty)? vectorizelanes(&lanes, ctrl_ty)?
}), }),
Opcode::Sextend => assign(DataValueExt::convert( Opcode::Sextend => assign(DataValueExt::convert(
arg(0)?, arg(0),
ValueConversionKind::SignExtend(ctrl_ty), ValueConversionKind::SignExtend(ctrl_ty),
)?), )?),
Opcode::Uextend => assign(DataValueExt::convert( Opcode::Uextend => assign(DataValueExt::convert(
arg(0)?, arg(0),
ValueConversionKind::ZeroExtend(ctrl_ty), ValueConversionKind::ZeroExtend(ctrl_ty),
)?), )?),
Opcode::Fpromote => assign(DataValueExt::convert( Opcode::Fpromote => assign(DataValueExt::convert(
arg(0)?, arg(0),
ValueConversionKind::Exact(ctrl_ty), ValueConversionKind::Exact(ctrl_ty),
)?), )?),
Opcode::Fdemote => assign(DataValueExt::convert( Opcode::Fdemote => assign(DataValueExt::convert(
arg(0)?, arg(0),
ValueConversionKind::RoundNearestEven(ctrl_ty), ValueConversionKind::RoundNearestEven(ctrl_ty),
)?), )?),
Opcode::Shuffle => { Opcode::Shuffle => {
let mask = imm().into_array()?; let mask = imm().into_array()?;
let a = DataValueExt::into_array(&arg(0)?)?; let a = DataValueExt::into_array(&arg(0))?;
let b = DataValueExt::into_array(&arg(1)?)?; let b = DataValueExt::into_array(&arg(1))?;
let mut new = [0u8; 16]; let mut new = [0u8; 16];
for i in 0..mask.len() { for i in 0..mask.len() {
if (mask[i] as usize) < a.len() { if (mask[i] as usize) < a.len() {
@@ -1029,8 +1015,8 @@ where
assign(DataValueExt::vector(new, types::I8X16)?) assign(DataValueExt::vector(new, types::I8X16)?)
} }
Opcode::Swizzle => { Opcode::Swizzle => {
let x = DataValueExt::into_array(&arg(0)?)?; let x = DataValueExt::into_array(&arg(0))?;
let s = DataValueExt::into_array(&arg(1)?)?; let s = DataValueExt::into_array(&arg(1))?;
let mut new = [0u8; 16]; let mut new = [0u8; 16];
for i in 0..new.len() { for i in 0..new.len() {
if (s[i] as usize) < new.len() { if (s[i] as usize) < new.len() {
@@ -1042,26 +1028,26 @@ where
Opcode::Splat => { Opcode::Splat => {
let mut new_vector = SimdVec::new(); let mut new_vector = SimdVec::new();
for _ in 0..ctrl_ty.lane_count() { for _ in 0..ctrl_ty.lane_count() {
new_vector.push(arg(0)?); new_vector.push(arg(0));
} }
assign(vectorizelanes(&new_vector, ctrl_ty)?) assign(vectorizelanes(&new_vector, ctrl_ty)?)
} }
Opcode::Insertlane => { Opcode::Insertlane => {
let idx = imm().into_int()? as usize; let idx = imm().into_int()? as usize;
let mut vector = extractlanes(&arg(0)?, ctrl_ty)?; let mut vector = extractlanes(&arg(0), ctrl_ty)?;
vector[idx] = arg(1)?; vector[idx] = arg(1);
assign(vectorizelanes(&vector, ctrl_ty)?) assign(vectorizelanes(&vector, ctrl_ty)?)
} }
Opcode::Extractlane => { Opcode::Extractlane => {
let idx = imm().into_int()? as usize; let idx = imm().into_int()? as usize;
let lanes = extractlanes(&arg(0)?, ctrl_ty)?; let lanes = extractlanes(&arg(0), ctrl_ty)?;
assign(lanes[idx].clone()) assign(lanes[idx].clone())
} }
Opcode::VhighBits => { Opcode::VhighBits => {
// `ctrl_ty` controls the return type for this, so the input type // `ctrl_ty` controls the return type for this, so the input type
// must be retrieved via `inst_context`. // must be retrieved via `inst_context`.
let vector_type = inst_context.type_of(inst_context.args()[0]).unwrap(); let vector_type = inst_context.type_of(inst_context.args()[0]).unwrap();
let a = extractlanes(&arg(0)?, vector_type)?; let a = extractlanes(&arg(0), vector_type)?;
let mut result: i128 = 0; let mut result: i128 = 0;
for (i, val) in a.into_iter().enumerate() { for (i, val) in a.into_iter().enumerate() {
let val = val.reverse_bits()?.into_int()?; // MSB -> LSB let val = val.reverse_bits()?.into_int()?; // MSB -> LSB
@@ -1072,13 +1058,13 @@ where
Opcode::VanyTrue => { Opcode::VanyTrue => {
let lane_ty = ctrl_ty.lane_type(); let lane_ty = ctrl_ty.lane_type();
let init = DataValue::bool(false, true, lane_ty)?; let init = DataValue::bool(false, true, lane_ty)?;
let any = fold_vector(arg(0)?, ctrl_ty, init.clone(), |acc, lane| acc.or(lane))?; let any = fold_vector(arg(0), ctrl_ty, init.clone(), |acc, lane| acc.or(lane))?;
assign(DataValue::bool(any != init, false, types::I8)?) assign(DataValue::bool(any != init, false, types::I8)?)
} }
Opcode::VallTrue => { Opcode::VallTrue => {
let lane_ty = ctrl_ty.lane_type(); let lane_ty = ctrl_ty.lane_type();
let init = DataValue::bool(true, true, lane_ty)?; let init = DataValue::bool(true, true, lane_ty)?;
let all = fold_vector(arg(0)?, ctrl_ty, init.clone(), |acc, lane| acc.and(lane))?; let all = fold_vector(arg(0), ctrl_ty, init.clone(), |acc, lane| acc.and(lane))?;
assign(DataValue::bool(all == init, false, types::I8)?) assign(DataValue::bool(all == init, false, types::I8)?)
} }
Opcode::SwidenLow | Opcode::SwidenHigh | Opcode::UwidenLow | Opcode::UwidenHigh => { Opcode::SwidenLow | Opcode::SwidenHigh | Opcode::UwidenLow | Opcode::UwidenHigh => {
@@ -1092,7 +1078,7 @@ where
} }
_ => unreachable!(), _ => unreachable!(),
}; };
let vec_iter = extractlanes(&arg(0)?, ctrl_ty)?.into_iter(); let vec_iter = extractlanes(&arg(0), ctrl_ty)?.into_iter();
let new_vec = match inst.opcode() { let new_vec = match inst.opcode() {
Opcode::SwidenLow | Opcode::UwidenLow => vec_iter Opcode::SwidenLow | Opcode::UwidenLow => vec_iter
.take(new_type.lane_count() as usize) .take(new_type.lane_count() as usize)
@@ -1108,12 +1094,12 @@ where
} }
Opcode::FcvtToUint | Opcode::FcvtToSint => { Opcode::FcvtToUint | Opcode::FcvtToSint => {
// NaN check // NaN check
if arg(0)?.is_nan()? { if arg(0).is_nan()? {
return Ok(ControlFlow::Trap(CraneliftTrap::User( return Ok(ControlFlow::Trap(CraneliftTrap::User(
TrapCode::BadConversionToInteger, TrapCode::BadConversionToInteger,
))); )));
} }
let x = arg(0)?.into_float()? as i128; let x = arg(0).into_float()? as i128;
let is_signed = inst.opcode() == Opcode::FcvtToSint; let is_signed = inst.opcode() == Opcode::FcvtToSint;
let (min, max) = ctrl_ty.bounds(is_signed); let (min, max) = ctrl_ty.bounds(is_signed);
let overflow = if is_signed { let overflow = if is_signed {
@@ -1154,7 +1140,7 @@ where
} }
}; };
let x = extractlanes(&arg(0)?, in_ty)?; let x = extractlanes(&arg(0), in_ty)?;
assign(vectorizelanes( assign(vectorizelanes(
&x.into_iter() &x.into_iter()
@@ -1165,7 +1151,7 @@ where
} }
Opcode::FcvtFromUint | Opcode::FcvtFromSint => { Opcode::FcvtFromUint | Opcode::FcvtFromSint => {
let x = extractlanes( let x = extractlanes(
&arg(0)?, &arg(0),
inst_context.type_of(inst_context.args()[0]).unwrap(), inst_context.type_of(inst_context.args()[0]).unwrap(),
)?; )?;
let bits = |x: DataValue| -> ValueResult<u64> { let bits = |x: DataValue| -> ValueResult<u64> {
@@ -1189,7 +1175,7 @@ where
} }
Opcode::FcvtLowFromSint => { Opcode::FcvtLowFromSint => {
let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap(); let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
let x = extractlanes(&arg(0)?, in_ty)?; let x = extractlanes(&arg(0), in_ty)?;
assign(vectorizelanes( assign(vectorizelanes(
&(x[..(ctrl_ty.lane_count() as usize)] &(x[..(ctrl_ty.lane_count() as usize)]
@@ -1212,7 +1198,7 @@ where
let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap(); let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
assert_eq!(in_ty, types::F32X4); assert_eq!(in_ty, types::F32X4);
let out_ty = types::F64X2; let out_ty = types::F64X2;
let x = extractlanes(&arg(0)?, in_ty)?; let x = extractlanes(&arg(0), in_ty)?;
assign(vectorizelanes( assign(vectorizelanes(
&x[..(out_ty.lane_count() as usize)] &x[..(out_ty.lane_count() as usize)]
.into_iter() .into_iter()
@@ -1230,7 +1216,7 @@ where
let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap(); let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
assert_eq!(in_ty, types::F64X2); assert_eq!(in_ty, types::F64X2);
let out_ty = types::F32X4; let out_ty = types::F32X4;
let x = extractlanes(&arg(0)?, in_ty)?; let x = extractlanes(&arg(0), in_ty)?;
let x = &mut x let x = &mut x
.into_iter() .into_iter()
.map(|x| { .map(|x| {
@@ -1244,14 +1230,14 @@ where
assign(vectorizelanes(x, out_ty)?) assign(vectorizelanes(x, out_ty)?)
} }
Opcode::Isplit => assign_multiple(&[ Opcode::Isplit => assign_multiple(&[
DataValueExt::convert(arg(0)?, ValueConversionKind::Truncate(types::I64))?, DataValueExt::convert(arg(0), ValueConversionKind::Truncate(types::I64))?,
DataValueExt::convert(arg(0)?, ValueConversionKind::ExtractUpper(types::I64))?, DataValueExt::convert(arg(0), ValueConversionKind::ExtractUpper(types::I64))?,
]), ]),
Opcode::Iconcat => assign(DataValueExt::concat(arg(0)?, arg(1)?)?), Opcode::Iconcat => assign(DataValueExt::concat(arg(0), arg(1))?),
Opcode::AtomicRmw => { Opcode::AtomicRmw => {
let op = inst.atomic_rmw_op().unwrap(); let op = inst.atomic_rmw_op().unwrap();
let val = arg(1)?; let val = arg(1);
let addr = arg(0)?.into_int()? as u64; let addr = arg(0).into_int()? as u64;
let mem_flags = inst.memflags().expect("instruction to have memory flags"); let mem_flags = inst.memflags().expect("instruction to have memory flags");
let loaded = Address::try_from(addr) let loaded = Address::try_from(addr)
.and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags)); .and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));
@@ -1286,7 +1272,7 @@ where
assign_or_memtrap(stored.map(|_| prev_val_to_assign)) assign_or_memtrap(stored.map(|_| prev_val_to_assign))
} }
Opcode::AtomicCas => { Opcode::AtomicCas => {
let addr = arg(0)?.into_int()? as u64; let addr = arg(0).into_int()? as u64;
let mem_flags = inst.memflags().expect("instruction to have memory flags"); let mem_flags = inst.memflags().expect("instruction to have memory flags");
let loaded = Address::try_from(addr) let loaded = Address::try_from(addr)
.and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags)); .and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));
@@ -1294,9 +1280,9 @@ where
Ok(v) => v, Ok(v) => v,
Err(e) => return Ok(ControlFlow::Trap(CraneliftTrap::User(memerror_to_trap(e)))), Err(e) => return Ok(ControlFlow::Trap(CraneliftTrap::User(memerror_to_trap(e)))),
}; };
let expected_val = arg(1)?; let expected_val = arg(1);
let val_to_assign = if loaded_val == expected_val { let val_to_assign = if loaded_val == expected_val {
let val_to_store = arg(2)?; let val_to_store = arg(2);
Address::try_from(addr) Address::try_from(addr)
.and_then(|addr| state.checked_store(addr, val_to_store, mem_flags)) .and_then(|addr| state.checked_store(addr, val_to_store, mem_flags))
.map(|_| loaded_val) .map(|_| loaded_val)
@@ -1307,7 +1293,7 @@ where
} }
Opcode::AtomicLoad => { Opcode::AtomicLoad => {
let load_ty = inst_context.controlling_type().unwrap(); let load_ty = inst_context.controlling_type().unwrap();
let addr = arg(0)?.into_int()? as u64; let addr = arg(0).into_int()? as u64;
let mem_flags = inst.memflags().expect("instruction to have memory flags"); let mem_flags = inst.memflags().expect("instruction to have memory flags");
// We are doing a regular load here, this isn't actually thread safe. // We are doing a regular load here, this isn't actually thread safe.
assign_or_memtrap( assign_or_memtrap(
@@ -1316,8 +1302,8 @@ where
) )
} }
Opcode::AtomicStore => { Opcode::AtomicStore => {
let val = arg(0)?; let val = arg(0);
let addr = arg(1)?.into_int()? as u64; let addr = arg(1).into_int()? as u64;
let mem_flags = inst.memflags().expect("instruction to have memory flags"); let mem_flags = inst.memflags().expect("instruction to have memory flags");
// We are doing a regular store here, this isn't actually thread safe. // We are doing a regular store here, this isn't actually thread safe.
continue_or_memtrap( continue_or_memtrap(
@@ -1332,8 +1318,8 @@ where
Opcode::SqmulRoundSat => { Opcode::SqmulRoundSat => {
let lane_type = ctrl_ty.lane_type(); let lane_type = ctrl_ty.lane_type();
let double_width = ctrl_ty.double_width().unwrap().lane_type(); let double_width = ctrl_ty.double_width().unwrap().lane_type();
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?; let arg0 = extractlanes(&arg(0), ctrl_ty)?;
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?; let arg1 = extractlanes(&arg(1), ctrl_ty)?;
let (min, max) = lane_type.bounds(true); let (min, max) = lane_type.bounds(true);
let min: DataValue = DataValueExt::int(min as i128, double_width)?; let min: DataValue = DataValueExt::int(min as i128, double_width)?;
let max: DataValue = DataValueExt::int(max as i128, double_width)?; let max: DataValue = DataValueExt::int(max as i128, double_width)?;
@@ -1357,12 +1343,9 @@ where
.collect::<ValueResult<SimdVec<_>>>()?; .collect::<ValueResult<SimdVec<_>>>()?;
assign(vectorizelanes(&new_vec, ctrl_ty)?) assign(vectorizelanes(&new_vec, ctrl_ty)?)
} }
Opcode::IaddPairwise => assign(binary_pairwise( Opcode::IaddPairwise => {
arg(0)?, assign(binary_pairwise(arg(0), arg(1), ctrl_ty, DataValueExt::add)?)
arg(1)?, }
ctrl_ty,
DataValueExt::add,
)?),
Opcode::ExtractVector => { Opcode::ExtractVector => {
unimplemented!("ExtractVector not supported"); unimplemented!("ExtractVector not supported");
} }