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:
@@ -12,7 +12,7 @@ use crate::value::{DataValueExt, ValueError};
|
||||
use cranelift_codegen::data_value::DataValue;
|
||||
use cranelift_codegen::ir::{
|
||||
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 smallvec::SmallVec;
|
||||
@@ -236,22 +236,6 @@ impl<'a> InterpreterState<'a> {
|
||||
self.libcall_handler = handler;
|
||||
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> {
|
||||
@@ -291,12 +275,20 @@ impl<'a> State<'a> for InterpreterState<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value(&self, name: ValueRef) -> Option<DataValue> {
|
||||
Some(self.current_frame().get(name).clone()) // TODO avoid clone?
|
||||
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 set_value(&mut self, name: ValueRef, value: DataValue) -> Option<DataValue> {
|
||||
self.current_frame_mut().set(name, value)
|
||||
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],
|
||||
}
|
||||
}
|
||||
|
||||
fn stack_address(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Cranelift instructions modify the state of the machine; the [State] trait describes these
|
||||
//! ways this can happen.
|
||||
use crate::address::{Address, AddressSize};
|
||||
use crate::frame::Frame;
|
||||
use crate::interpreter::LibCallHandler;
|
||||
use cranelift_codegen::data_value::DataValue;
|
||||
use cranelift_codegen::ir::{
|
||||
@@ -8,20 +9,17 @@ use cranelift_codegen::ir::{
|
||||
Value,
|
||||
};
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use smallvec::SmallVec;
|
||||
use thiserror::Error;
|
||||
|
||||
/// 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
|
||||
/// 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
|
||||
/// storing from the heap) we will use a full-fledged state, like
|
||||
/// - when interpretation needs to happen in a way isolated from the host a state which keeps a
|
||||
/// stack and bound checks memory accesses can be used, like
|
||||
/// [InterpreterState](crate::interpreter::InterpreterState).
|
||||
/// - when interpretation can ignore some state changes (e.g. abstract interpretation of arithmetic
|
||||
/// instructions--no heap knowledge required), we can partially implement this trait. See
|
||||
/// [ImmutableRegisterState] for an example of this: it only exposes the values referenced by the
|
||||
/// SSA references in the current frame and not much else.
|
||||
/// - when interpretation needs to have access to the host a state which allows direct access to the
|
||||
/// host memory and native functions can be used.
|
||||
pub trait State<'a> {
|
||||
/// Retrieve a reference to 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;
|
||||
/// Retrieve the handler callback for a [LibCall](cranelift_codegen::ir::LibCall)
|
||||
fn get_libcall_handler(&self) -> LibCallHandler;
|
||||
|
||||
/// Record that an interpreter has called into a new [Function].
|
||||
fn push_frame(&mut self, function: &'a Function);
|
||||
/// Record that an interpreter has returned from a called [Function].
|
||||
fn pop_frame(&mut self);
|
||||
|
||||
/// Retrieve a value `V` by its [value reference](cranelift_codegen::ir::Value) from the
|
||||
/// virtual register file.
|
||||
fn get_value(&self, name: Value) -> Option<DataValue>;
|
||||
/// Assign a value `V` to its [value reference](cranelift_codegen::ir::Value) in the
|
||||
/// virtual register file.
|
||||
fn set_value(&mut self, name: Value, value: DataValue) -> Option<DataValue>;
|
||||
/// Collect a list of values `V` by their [value references](cranelift_codegen::ir::Value);
|
||||
/// 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)
|
||||
fn current_frame_mut(&mut self) -> &mut Frame<'a>;
|
||||
fn current_frame(&self) -> &Frame<'a>;
|
||||
|
||||
/// Collect a list of values `V` by their [value references](cranelift_codegen::ir::Value).
|
||||
fn collect_values(&self, names: &[Value]) -> SmallVec<[DataValue; 1]> {
|
||||
let frame = self.current_frame();
|
||||
names.into_iter().map(|n| frame.get(*n).clone()).collect()
|
||||
}
|
||||
|
||||
/// 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:?}")]
|
||||
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!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,23 +67,17 @@ where
|
||||
// frequently close over the `state` or `inst_context` for brevity.
|
||||
|
||||
// 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];
|
||||
state
|
||||
.get_value(value_ref)
|
||||
.ok_or(StepError::UnknownValue(value_ref))
|
||||
state.current_frame().get(value_ref).clone()
|
||||
};
|
||||
|
||||
// Retrieve the current values for all of an instruction's arguments.
|
||||
let args = || -> Result<SmallVec<[DataValue; 1]>, StepError> {
|
||||
state
|
||||
.collect_values(inst_context.args())
|
||||
.map_err(|v| StepError::UnknownValue(v))
|
||||
};
|
||||
let args = || -> SmallVec<[DataValue; 1]> { state.collect_values(inst_context.args()) };
|
||||
|
||||
// Retrieve the current values for a range of an instruction's arguments.
|
||||
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.
|
||||
@@ -261,9 +255,8 @@ where
|
||||
// Retrieve an instruction's branch destination; expects the instruction to be a branch.
|
||||
|
||||
let continue_at = |block: BlockCall| {
|
||||
let branch_args = state
|
||||
.collect_values(block.args_slice(&state.get_current_function().dfg.value_lists))
|
||||
.map_err(|v| StepError::UnknownValue(v))?;
|
||||
let branch_args =
|
||||
state.collect_values(block.args_slice(&state.get_current_function().dfg.value_lists));
|
||||
Ok(ControlFlow::ContinueAt(
|
||||
block.block(&state.get_current_function().dfg.value_lists),
|
||||
branch_args,
|
||||
@@ -353,7 +346,7 @@ where
|
||||
..
|
||||
} = 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()?;
|
||||
|
||||
@@ -371,7 +364,7 @@ where
|
||||
let jt_data = &state.get_current_function().stencil.dfg.jump_tables[table];
|
||||
|
||||
// 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()
|
||||
.and_then(|i| jt_data.as_slice().get(i))
|
||||
.copied()
|
||||
@@ -385,10 +378,10 @@ where
|
||||
Opcode::Trap => ControlFlow::Trap(CraneliftTrap::User(trap_code())),
|
||||
Opcode::Debugtrap => ControlFlow::Trap(CraneliftTrap::Debug),
|
||||
Opcode::ResumableTrap => ControlFlow::Trap(CraneliftTrap::Resumable),
|
||||
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::ResumableTrapnz => trap_when(arg(0)?.into_bool()?, CraneliftTrap::Resumable),
|
||||
Opcode::Return => ControlFlow::Return(args()?),
|
||||
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::ResumableTrapnz => trap_when(arg(0).into_bool()?, CraneliftTrap::Resumable),
|
||||
Opcode::Return => ControlFlow::Return(args()),
|
||||
Opcode::Call | Opcode::ReturnCall => {
|
||||
let func_ref = if let InstructionData::Call { func_ref, .. } = inst {
|
||||
func_ref
|
||||
@@ -403,7 +396,7 @@ where
|
||||
.get(func_ref)
|
||||
.ok_or(StepError::UnknownFunction(func_ref))?;
|
||||
|
||||
let args = args()?;
|
||||
let args = args();
|
||||
let func = match ext_data.name {
|
||||
// These functions should be registered in the regular function store
|
||||
ExternalName::User(_) | ExternalName::TestCase(_) => {
|
||||
@@ -425,8 +418,8 @@ where
|
||||
call_func(func, args, make_control_flow)?
|
||||
}
|
||||
Opcode::CallIndirect | Opcode::ReturnCallIndirect => {
|
||||
let args = args()?;
|
||||
let addr_dv = DataValue::U64(arg(0)?.into_int()? as u64);
|
||||
let args = args();
|
||||
let addr_dv = DataValue::U64(arg(0).into_int()? as u64);
|
||||
let addr = Address::try_from(addr_dv.clone()).map_err(StepError::MemoryError)?;
|
||||
|
||||
let func = state
|
||||
@@ -497,7 +490,7 @@ where
|
||||
_ => 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 loaded = assign_or_memtrap(
|
||||
Address::try_from(addr_value)
|
||||
@@ -525,9 +518,9 @@ where
|
||||
let addr_value = calculate_addr(types::I64, imm(), args_range(1..)?)?;
|
||||
let mem_flags = inst.memflags().expect("instruction to have memory flags");
|
||||
let reduced = if let Some(c) = kind {
|
||||
arg(0)?.convert(c)?
|
||||
arg(0).convert(c)?
|
||||
} else {
|
||||
arg(0)?
|
||||
arg(0)
|
||||
};
|
||||
continue_or_memtrap(
|
||||
Address::try_from(addr_value)
|
||||
@@ -537,7 +530,7 @@ where
|
||||
Opcode::StackLoad => {
|
||||
let load_ty = inst_context.controlling_type().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();
|
||||
assign_or_memtrap({
|
||||
state
|
||||
@@ -546,7 +539,7 @@ where
|
||||
})
|
||||
}
|
||||
Opcode::StackStore => {
|
||||
let arg = arg(0)?;
|
||||
let arg = arg(0);
|
||||
let slot = inst.stack_slot().unwrap();
|
||||
let offset = sum(imm(), args_range(1..)?)? as u64;
|
||||
let mem_flags = MemFlags::new();
|
||||
@@ -559,7 +552,7 @@ where
|
||||
Opcode::StackAddr => {
|
||||
let load_ty = inst_context.controlling_type().unwrap();
|
||||
let slot = inst.stack_slot().unwrap();
|
||||
let offset = sum(imm(), args()?)? as u64;
|
||||
let offset = sum(imm(), args())? as u64;
|
||||
assign_or_memtrap({
|
||||
AddressSize::try_from(load_ty).and_then(|addr_size| {
|
||||
let addr = state.stack_address(addr_size, slot, offset)?;
|
||||
@@ -580,7 +573,7 @@ where
|
||||
}
|
||||
Opcode::GetPinnedReg => assign(state.get_pinned_reg()),
|
||||
Opcode::SetPinnedReg => {
|
||||
let arg0 = arg(0)?;
|
||||
let arg0 = arg(0);
|
||||
state.set_pinned_reg(arg0);
|
||||
ControlFlow::Continue
|
||||
}
|
||||
@@ -593,7 +586,7 @@ where
|
||||
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 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)?;
|
||||
if byte_offset > bound_bytes {
|
||||
return Ok(ControlFlow::Trap(CraneliftTrap::User(
|
||||
@@ -612,106 +605,99 @@ where
|
||||
Opcode::Vconst => assign(imm()),
|
||||
Opcode::Null => unimplemented!("Null"),
|
||||
Opcode::Nop => ControlFlow::Continue,
|
||||
Opcode::Select | Opcode::SelectSpectreGuard => {
|
||||
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::Select | Opcode::SelectSpectreGuard => 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::IcmpImm => assign(icmp(
|
||||
ctrl_ty,
|
||||
inst.cond_code().unwrap(),
|
||||
&arg(0)?,
|
||||
&arg(0),
|
||||
&imm_as_ctrl_ty()?,
|
||||
)?),
|
||||
Opcode::Smin => {
|
||||
if ctrl_ty.is_vector() {
|
||||
let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(1)?, &arg(0)?)?;
|
||||
assign(bitselect(icmp, arg(0)?, arg(1)?)?)
|
||||
let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(1), &arg(0))?;
|
||||
assign(bitselect(icmp, arg(0), arg(1))?)
|
||||
} else {
|
||||
choose(arg(1)? > arg(0)?, arg(0)?, arg(1)?)
|
||||
choose(arg(1) > arg(0), arg(0), arg(1))
|
||||
}
|
||||
}
|
||||
Opcode::Umin => {
|
||||
if ctrl_ty.is_vector() {
|
||||
let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(1)?, &arg(0)?)?;
|
||||
assign(bitselect(icmp, arg(0)?, arg(1)?)?)
|
||||
let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(1), &arg(0))?;
|
||||
assign(bitselect(icmp, arg(0), arg(1))?)
|
||||
} else {
|
||||
choose(
|
||||
arg(1)?.convert(ValueConversionKind::ToUnsigned)?
|
||||
> arg(0)?.convert(ValueConversionKind::ToUnsigned)?,
|
||||
arg(0)?,
|
||||
arg(1)?,
|
||||
arg(1).convert(ValueConversionKind::ToUnsigned)?
|
||||
> arg(0).convert(ValueConversionKind::ToUnsigned)?,
|
||||
arg(0),
|
||||
arg(1),
|
||||
)
|
||||
}
|
||||
}
|
||||
Opcode::Smax => {
|
||||
if ctrl_ty.is_vector() {
|
||||
let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(0)?, &arg(1)?)?;
|
||||
assign(bitselect(icmp, arg(0)?, arg(1)?)?)
|
||||
let icmp = icmp(ctrl_ty, IntCC::SignedGreaterThan, &arg(0), &arg(1))?;
|
||||
assign(bitselect(icmp, arg(0), arg(1))?)
|
||||
} else {
|
||||
choose(arg(0)? > arg(1)?, arg(0)?, arg(1)?)
|
||||
choose(arg(0) > arg(1), arg(0), arg(1))
|
||||
}
|
||||
}
|
||||
Opcode::Umax => {
|
||||
if ctrl_ty.is_vector() {
|
||||
let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(0)?, &arg(1)?)?;
|
||||
assign(bitselect(icmp, arg(0)?, arg(1)?)?)
|
||||
let icmp = icmp(ctrl_ty, IntCC::UnsignedGreaterThan, &arg(0), &arg(1))?;
|
||||
assign(bitselect(icmp, arg(0), arg(1))?)
|
||||
} else {
|
||||
choose(
|
||||
arg(0)?.convert(ValueConversionKind::ToUnsigned)?
|
||||
> arg(1)?.convert(ValueConversionKind::ToUnsigned)?,
|
||||
arg(0)?,
|
||||
arg(1)?,
|
||||
arg(0).convert(ValueConversionKind::ToUnsigned)?
|
||||
> arg(1).convert(ValueConversionKind::ToUnsigned)?,
|
||||
arg(0),
|
||||
arg(1),
|
||||
)
|
||||
}
|
||||
}
|
||||
Opcode::AvgRound => {
|
||||
let sum = DataValueExt::add(arg(0)?, arg(1)?)?;
|
||||
let one = DataValueExt::int(1, arg(0)?.ty())?;
|
||||
let sum = DataValueExt::add(arg(0), arg(1))?;
|
||||
let one = DataValueExt::int(1, arg(0).ty())?;
|
||||
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)?
|
||||
}
|
||||
Opcode::Iadd => binary(DataValueExt::add, arg(0)?, arg(1)?)?,
|
||||
Opcode::Iadd => binary(DataValueExt::add, arg(0), arg(1))?,
|
||||
Opcode::UaddSat => assign(binary_arith(
|
||||
arg(0)?,
|
||||
arg(1)?,
|
||||
arg(0),
|
||||
arg(1),
|
||||
ctrl_ty,
|
||||
DataValueExt::add_sat,
|
||||
true,
|
||||
)?),
|
||||
Opcode::SaddSat => assign(binary_arith(
|
||||
arg(0)?,
|
||||
arg(1)?,
|
||||
arg(0),
|
||||
arg(1),
|
||||
ctrl_ty,
|
||||
DataValueExt::add_sat,
|
||||
false,
|
||||
)?),
|
||||
Opcode::Isub => binary(DataValueExt::sub, arg(0)?, arg(1)?)?,
|
||||
Opcode::Isub => binary(DataValueExt::sub, arg(0), arg(1))?,
|
||||
Opcode::UsubSat => assign(binary_arith(
|
||||
arg(0)?,
|
||||
arg(1)?,
|
||||
arg(0),
|
||||
arg(1),
|
||||
ctrl_ty,
|
||||
DataValueExt::sub_sat,
|
||||
true,
|
||||
)?),
|
||||
Opcode::SsubSat => assign(binary_arith(
|
||||
arg(0)?,
|
||||
arg(1)?,
|
||||
arg(0),
|
||||
arg(1),
|
||||
ctrl_ty,
|
||||
DataValueExt::sub_sat,
|
||||
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 => {
|
||||
let (min_val, _) = ctrl_ty.lane_type().bounds(true);
|
||||
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
|
||||
.into_iter()
|
||||
.map(|lane| {
|
||||
@@ -724,7 +710,7 @@ where
|
||||
.collect::<ValueResult<SimdVec<DataValue>>>()?;
|
||||
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 => {
|
||||
let double_length = match ctrl_ty.lane_bits() {
|
||||
8 => types::I16,
|
||||
@@ -738,8 +724,8 @@ where
|
||||
} else {
|
||||
ValueConversionKind::SignExtend(double_length)
|
||||
};
|
||||
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?;
|
||||
let arg0 = extractlanes(&arg(0), ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1), ctrl_ty)?;
|
||||
|
||||
let res = arg0
|
||||
.into_iter()
|
||||
@@ -755,35 +741,35 @@ where
|
||||
|
||||
assign(vectorizelanes(&res, ctrl_ty)?)
|
||||
}
|
||||
Opcode::Udiv => binary_unsigned_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::Srem => binary_can_trap(DataValueExt::rem, arg(0)?, arg(1)?)?,
|
||||
Opcode::IaddImm => binary(DataValueExt::add, 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::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::SremImm => binary_can_trap(DataValueExt::rem, arg(0)?, imm_as_ctrl_ty()?)?,
|
||||
Opcode::IrsubImm => binary(DataValueExt::sub, imm_as_ctrl_ty()?, arg(0)?)?,
|
||||
Opcode::Udiv => binary_unsigned_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::Srem => binary_can_trap(DataValueExt::rem, arg(0), arg(1))?,
|
||||
Opcode::IaddImm => binary(DataValueExt::add, 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::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::SremImm => binary_can_trap(DataValueExt::rem, arg(0), imm_as_ctrl_ty()?)?,
|
||||
Opcode::IrsubImm => binary(DataValueExt::sub, imm_as_ctrl_ty()?, arg(0))?,
|
||||
Opcode::IaddCin => choose(
|
||||
DataValueExt::into_bool(arg(2)?)?,
|
||||
DataValueExt::into_bool(arg(2))?,
|
||||
DataValueExt::add(
|
||||
DataValueExt::add(arg(0)?, arg(1)?)?,
|
||||
DataValueExt::add(arg(0), arg(1))?,
|
||||
DataValueExt::int(1, ctrl_ty)?,
|
||||
)?,
|
||||
DataValueExt::add(arg(0)?, arg(1)?)?,
|
||||
DataValueExt::add(arg(0), arg(1))?,
|
||||
),
|
||||
Opcode::IaddCout => {
|
||||
let carry = arg(0)?.checked_add(arg(1)?)?.is_none();
|
||||
let sum = arg(0)?.add(arg(1)?)?;
|
||||
let carry = arg(0).checked_add(arg(1))?.is_none();
|
||||
let sum = arg(0).add(arg(1))?;
|
||||
assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
|
||||
}
|
||||
Opcode::IaddCarry => {
|
||||
let mut sum = DataValueExt::add(arg(0)?, arg(1)?)?;
|
||||
let mut carry = arg(0)?.checked_add(arg(1)?)?.is_none();
|
||||
let mut sum = DataValueExt::add(arg(0), arg(1))?;
|
||||
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
|
||||
.clone()
|
||||
.checked_add(DataValueExt::int(1, ctrl_ty)?)?
|
||||
@@ -794,8 +780,8 @@ where
|
||||
assign_multiple(&[sum, DataValueExt::bool(carry, false, types::I8)?])
|
||||
}
|
||||
Opcode::UaddOverflowTrap => {
|
||||
let sum = DataValueExt::add(arg(0)?, arg(1)?)?;
|
||||
let carry = sum < arg(0)? && sum < arg(1)?;
|
||||
let sum = DataValueExt::add(arg(0), arg(1))?;
|
||||
let carry = sum < arg(0) && sum < arg(1);
|
||||
if carry {
|
||||
ControlFlow::Trap(CraneliftTrap::User(trap_code()))
|
||||
} else {
|
||||
@@ -803,65 +789,65 @@ where
|
||||
}
|
||||
}
|
||||
Opcode::IsubBin => choose(
|
||||
DataValueExt::into_bool(arg(2)?)?,
|
||||
DataValueExt::into_bool(arg(2))?,
|
||||
DataValueExt::sub(
|
||||
arg(0)?,
|
||||
DataValueExt::add(arg(1)?, DataValueExt::int(1, ctrl_ty)?)?,
|
||||
arg(0),
|
||||
DataValueExt::add(arg(1), DataValueExt::int(1, ctrl_ty)?)?,
|
||||
)?,
|
||||
DataValueExt::sub(arg(0)?, arg(1)?)?,
|
||||
DataValueExt::sub(arg(0), arg(1))?,
|
||||
),
|
||||
Opcode::IsubBout => {
|
||||
let sum = DataValueExt::sub(arg(0)?, arg(1)?)?;
|
||||
let borrow = arg(0)? < arg(1)?;
|
||||
let sum = DataValueExt::sub(arg(0), arg(1))?;
|
||||
let borrow = arg(0) < arg(1);
|
||||
assign_multiple(&[sum, DataValueExt::bool(borrow, false, types::I8)?])
|
||||
}
|
||||
Opcode::IsubBorrow => {
|
||||
let rhs = if DataValueExt::into_bool(arg(2)?)? {
|
||||
DataValueExt::add(arg(1)?, DataValueExt::int(1, ctrl_ty)?)?
|
||||
let rhs = if DataValueExt::into_bool(arg(2))? {
|
||||
DataValueExt::add(arg(1), DataValueExt::int(1, ctrl_ty)?)?
|
||||
} else {
|
||||
arg(1)?
|
||||
arg(1)
|
||||
};
|
||||
let borrow = arg(0)? < rhs;
|
||||
let sum = DataValueExt::sub(arg(0)?, rhs)?;
|
||||
let borrow = arg(0) < rhs;
|
||||
let sum = DataValueExt::sub(arg(0), rhs)?;
|
||||
assign_multiple(&[sum, DataValueExt::bool(borrow, false, types::I8)?])
|
||||
}
|
||||
Opcode::Band => binary(DataValueExt::and, arg(0)?, arg(1)?)?,
|
||||
Opcode::Bor => binary(DataValueExt::or, arg(0)?, arg(1)?)?,
|
||||
Opcode::Bxor => binary(DataValueExt::xor, arg(0)?, arg(1)?)?,
|
||||
Opcode::Bnot => unary(DataValueExt::not, arg(0)?)?,
|
||||
Opcode::BandNot => binary(DataValueExt::and, 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::BandImm => binary(DataValueExt::and, 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::Rotl => binary(DataValueExt::rotl, 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::RotrImm => binary(DataValueExt::rotr, arg(0)?, imm_as_ctrl_ty()?)?,
|
||||
Opcode::Ishl => binary(DataValueExt::shl, arg(0)?, arg(1)?)?,
|
||||
Opcode::Ushr => binary_unsigned(DataValueExt::ushr, 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::UshrImm => binary_unsigned(DataValueExt::ushr, 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::Bswap => unary(DataValueExt::swap_bytes, arg(0)?)?,
|
||||
Opcode::Clz => unary(DataValueExt::leading_zeros, arg(0)?)?,
|
||||
Opcode::Band => binary(DataValueExt::and, arg(0), arg(1))?,
|
||||
Opcode::Bor => binary(DataValueExt::or, arg(0), arg(1))?,
|
||||
Opcode::Bxor => binary(DataValueExt::xor, arg(0), arg(1))?,
|
||||
Opcode::Bnot => unary(DataValueExt::not, arg(0))?,
|
||||
Opcode::BandNot => binary(DataValueExt::and, 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::BandImm => binary(DataValueExt::and, 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::Rotl => binary(DataValueExt::rotl, 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::RotrImm => binary(DataValueExt::rotr, arg(0), imm_as_ctrl_ty()?)?,
|
||||
Opcode::Ishl => binary(DataValueExt::shl, arg(0), arg(1))?,
|
||||
Opcode::Ushr => binary_unsigned(DataValueExt::ushr, 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::UshrImm => binary_unsigned(DataValueExt::ushr, 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::Bswap => unary(DataValueExt::swap_bytes, arg(0))?,
|
||||
Opcode::Clz => unary(DataValueExt::leading_zeros, arg(0))?,
|
||||
Opcode::Cls => {
|
||||
let count = if arg(0)? < DataValueExt::int(0, ctrl_ty)? {
|
||||
arg(0)?.leading_ones()?
|
||||
let count = if arg(0) < DataValueExt::int(0, ctrl_ty)? {
|
||||
arg(0).leading_ones()?
|
||||
} else {
|
||||
arg(0)?.leading_zeros()?
|
||||
arg(0).leading_zeros()?
|
||||
};
|
||||
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 => {
|
||||
let count = if arg(0)?.ty().is_int() {
|
||||
arg(0)?.count_ones()?
|
||||
let count = if arg(0).ty().is_int() {
|
||||
arg(0).count_ones()?
|
||||
} else {
|
||||
let lanes = extractlanes(&arg(0)?, ctrl_ty)?
|
||||
let lanes = extractlanes(&arg(0), ctrl_ty)?
|
||||
.into_iter()
|
||||
.map(|lane| lane.count_ones())
|
||||
.collect::<ValueResult<SimdVec<DataValue>>>()?;
|
||||
@@ -871,8 +857,8 @@ where
|
||||
}
|
||||
|
||||
Opcode::Fcmp => {
|
||||
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?;
|
||||
let arg0 = extractlanes(&arg(0), ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1), ctrl_ty)?;
|
||||
|
||||
assign(vectorizelanes(
|
||||
&(arg0
|
||||
@@ -889,15 +875,15 @@ where
|
||||
ctrl_ty,
|
||||
)?)
|
||||
}
|
||||
Opcode::Fadd => binary(DataValueExt::add, arg(0)?, arg(1)?)?,
|
||||
Opcode::Fsub => binary(DataValueExt::sub, arg(0)?, arg(1)?)?,
|
||||
Opcode::Fmul => binary(DataValueExt::mul, arg(0)?, arg(1)?)?,
|
||||
Opcode::Fdiv => binary(DataValueExt::div, arg(0)?, arg(1)?)?,
|
||||
Opcode::Sqrt => unary(DataValueExt::sqrt, arg(0)?)?,
|
||||
Opcode::Fadd => binary(DataValueExt::add, arg(0), arg(1))?,
|
||||
Opcode::Fsub => binary(DataValueExt::sub, arg(0), arg(1))?,
|
||||
Opcode::Fmul => binary(DataValueExt::mul, arg(0), arg(1))?,
|
||||
Opcode::Fdiv => binary(DataValueExt::div, arg(0), arg(1))?,
|
||||
Opcode::Sqrt => unary(DataValueExt::sqrt, arg(0))?,
|
||||
Opcode::Fma => {
|
||||
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?;
|
||||
let arg2 = extractlanes(&arg(2)?, ctrl_ty)?;
|
||||
let arg0 = extractlanes(&arg(0), ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1), ctrl_ty)?;
|
||||
let arg2 = extractlanes(&arg(2), ctrl_ty)?;
|
||||
|
||||
assign(vectorizelanes(
|
||||
&(arg0
|
||||
@@ -909,42 +895,42 @@ where
|
||||
ctrl_ty,
|
||||
)?)
|
||||
}
|
||||
Opcode::Fneg => unary(DataValueExt::neg, arg(0)?)?,
|
||||
Opcode::Fabs => unary(DataValueExt::abs, arg(0)?)?,
|
||||
Opcode::Fcopysign => binary(DataValueExt::copysign, arg(0)?, arg(1)?)?,
|
||||
Opcode::Fmin => assign(match (arg(0)?, arg(1)?) {
|
||||
Opcode::Fneg => unary(DataValueExt::neg, arg(0))?,
|
||||
Opcode::Fabs => unary(DataValueExt::abs, arg(0))?,
|
||||
Opcode::Fcopysign => binary(DataValueExt::copysign, arg(0), arg(1))?,
|
||||
Opcode::Fmin => assign(match (arg(0), arg(1)) {
|
||||
(a, _) if a.is_nan()? => a,
|
||||
(_, 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()? && b.is_negative()? => 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_zero()? && b.is_zero()? => a,
|
||||
(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,
|
||||
(_, 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()? && b.is_negative()? => a,
|
||||
(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_zero()? && b.is_zero()? => a,
|
||||
(a, b) => a.max(b)?,
|
||||
}),
|
||||
Opcode::Ceil => unary(DataValueExt::ceil, arg(0)?)?,
|
||||
Opcode::Floor => unary(DataValueExt::floor, arg(0)?)?,
|
||||
Opcode::Trunc => unary(DataValueExt::trunc, arg(0)?)?,
|
||||
Opcode::Nearest => unary(DataValueExt::nearest, arg(0)?)?,
|
||||
Opcode::Ceil => unary(DataValueExt::ceil, arg(0))?,
|
||||
Opcode::Floor => unary(DataValueExt::floor, arg(0))?,
|
||||
Opcode::Trunc => unary(DataValueExt::trunc, arg(0))?,
|
||||
Opcode::Nearest => unary(DataValueExt::nearest, arg(0))?,
|
||||
Opcode::IsNull => unimplemented!("IsNull"),
|
||||
Opcode::IsInvalid => unimplemented!("IsInvalid"),
|
||||
Opcode::Bitcast | Opcode::ScalarToVector => {
|
||||
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
|
||||
.into_iter()
|
||||
.map(|x| DataValue::convert(x, ValueConversionKind::Exact(ctrl_ty.lane_type())))
|
||||
@@ -956,12 +942,12 @@ where
|
||||
})
|
||||
}
|
||||
Opcode::Ireduce => assign(DataValueExt::convert(
|
||||
arg(0)?,
|
||||
arg(0),
|
||||
ValueConversionKind::Truncate(ctrl_ty),
|
||||
)?),
|
||||
Opcode::Snarrow | Opcode::Unarrow | Opcode::Uunarrow => {
|
||||
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?;
|
||||
let arg0 = extractlanes(&arg(0), ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1), ctrl_ty)?;
|
||||
let new_type = ctrl_ty.split_lanes().unwrap();
|
||||
let (min, max) = new_type.bounds(inst.opcode() == Opcode::Snarrow);
|
||||
let mut min: DataValue = DataValueExt::int(min as i128, ctrl_ty.lane_type())?;
|
||||
@@ -990,7 +976,7 @@ where
|
||||
assign(vectorizelanes(&new_vec, new_type)?)
|
||||
}
|
||||
Opcode::Bmask => assign({
|
||||
let bool = arg(0)?;
|
||||
let bool = arg(0);
|
||||
let bool_ty = ctrl_ty.as_truthy_pedantic();
|
||||
let lanes = extractlanes(&bool, bool_ty)?
|
||||
.into_iter()
|
||||
@@ -999,25 +985,25 @@ where
|
||||
vectorizelanes(&lanes, ctrl_ty)?
|
||||
}),
|
||||
Opcode::Sextend => assign(DataValueExt::convert(
|
||||
arg(0)?,
|
||||
arg(0),
|
||||
ValueConversionKind::SignExtend(ctrl_ty),
|
||||
)?),
|
||||
Opcode::Uextend => assign(DataValueExt::convert(
|
||||
arg(0)?,
|
||||
arg(0),
|
||||
ValueConversionKind::ZeroExtend(ctrl_ty),
|
||||
)?),
|
||||
Opcode::Fpromote => assign(DataValueExt::convert(
|
||||
arg(0)?,
|
||||
arg(0),
|
||||
ValueConversionKind::Exact(ctrl_ty),
|
||||
)?),
|
||||
Opcode::Fdemote => assign(DataValueExt::convert(
|
||||
arg(0)?,
|
||||
arg(0),
|
||||
ValueConversionKind::RoundNearestEven(ctrl_ty),
|
||||
)?),
|
||||
Opcode::Shuffle => {
|
||||
let mask = imm().into_array()?;
|
||||
let a = DataValueExt::into_array(&arg(0)?)?;
|
||||
let b = DataValueExt::into_array(&arg(1)?)?;
|
||||
let a = DataValueExt::into_array(&arg(0))?;
|
||||
let b = DataValueExt::into_array(&arg(1))?;
|
||||
let mut new = [0u8; 16];
|
||||
for i in 0..mask.len() {
|
||||
if (mask[i] as usize) < a.len() {
|
||||
@@ -1029,8 +1015,8 @@ where
|
||||
assign(DataValueExt::vector(new, types::I8X16)?)
|
||||
}
|
||||
Opcode::Swizzle => {
|
||||
let x = DataValueExt::into_array(&arg(0)?)?;
|
||||
let s = DataValueExt::into_array(&arg(1)?)?;
|
||||
let x = DataValueExt::into_array(&arg(0))?;
|
||||
let s = DataValueExt::into_array(&arg(1))?;
|
||||
let mut new = [0u8; 16];
|
||||
for i in 0..new.len() {
|
||||
if (s[i] as usize) < new.len() {
|
||||
@@ -1042,26 +1028,26 @@ where
|
||||
Opcode::Splat => {
|
||||
let mut new_vector = SimdVec::new();
|
||||
for _ in 0..ctrl_ty.lane_count() {
|
||||
new_vector.push(arg(0)?);
|
||||
new_vector.push(arg(0));
|
||||
}
|
||||
assign(vectorizelanes(&new_vector, ctrl_ty)?)
|
||||
}
|
||||
Opcode::Insertlane => {
|
||||
let idx = imm().into_int()? as usize;
|
||||
let mut vector = extractlanes(&arg(0)?, ctrl_ty)?;
|
||||
vector[idx] = arg(1)?;
|
||||
let mut vector = extractlanes(&arg(0), ctrl_ty)?;
|
||||
vector[idx] = arg(1);
|
||||
assign(vectorizelanes(&vector, ctrl_ty)?)
|
||||
}
|
||||
Opcode::Extractlane => {
|
||||
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())
|
||||
}
|
||||
Opcode::VhighBits => {
|
||||
// `ctrl_ty` controls the return type for this, so the input type
|
||||
// must be retrieved via `inst_context`.
|
||||
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;
|
||||
for (i, val) in a.into_iter().enumerate() {
|
||||
let val = val.reverse_bits()?.into_int()?; // MSB -> LSB
|
||||
@@ -1072,13 +1058,13 @@ where
|
||||
Opcode::VanyTrue => {
|
||||
let lane_ty = ctrl_ty.lane_type();
|
||||
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)?)
|
||||
}
|
||||
Opcode::VallTrue => {
|
||||
let lane_ty = ctrl_ty.lane_type();
|
||||
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)?)
|
||||
}
|
||||
Opcode::SwidenLow | Opcode::SwidenHigh | Opcode::UwidenLow | Opcode::UwidenHigh => {
|
||||
@@ -1092,7 +1078,7 @@ where
|
||||
}
|
||||
_ => 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() {
|
||||
Opcode::SwidenLow | Opcode::UwidenLow => vec_iter
|
||||
.take(new_type.lane_count() as usize)
|
||||
@@ -1108,12 +1094,12 @@ where
|
||||
}
|
||||
Opcode::FcvtToUint | Opcode::FcvtToSint => {
|
||||
// NaN check
|
||||
if arg(0)?.is_nan()? {
|
||||
if arg(0).is_nan()? {
|
||||
return Ok(ControlFlow::Trap(CraneliftTrap::User(
|
||||
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 (min, max) = ctrl_ty.bounds(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(
|
||||
&x.into_iter()
|
||||
@@ -1165,7 +1151,7 @@ where
|
||||
}
|
||||
Opcode::FcvtFromUint | Opcode::FcvtFromSint => {
|
||||
let x = extractlanes(
|
||||
&arg(0)?,
|
||||
&arg(0),
|
||||
inst_context.type_of(inst_context.args()[0]).unwrap(),
|
||||
)?;
|
||||
let bits = |x: DataValue| -> ValueResult<u64> {
|
||||
@@ -1189,7 +1175,7 @@ where
|
||||
}
|
||||
Opcode::FcvtLowFromSint => {
|
||||
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(
|
||||
&(x[..(ctrl_ty.lane_count() as usize)]
|
||||
@@ -1212,7 +1198,7 @@ where
|
||||
let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
|
||||
assert_eq!(in_ty, types::F32X4);
|
||||
let out_ty = types::F64X2;
|
||||
let x = extractlanes(&arg(0)?, in_ty)?;
|
||||
let x = extractlanes(&arg(0), in_ty)?;
|
||||
assign(vectorizelanes(
|
||||
&x[..(out_ty.lane_count() as usize)]
|
||||
.into_iter()
|
||||
@@ -1230,7 +1216,7 @@ where
|
||||
let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
|
||||
assert_eq!(in_ty, types::F64X2);
|
||||
let out_ty = types::F32X4;
|
||||
let x = extractlanes(&arg(0)?, in_ty)?;
|
||||
let x = extractlanes(&arg(0), in_ty)?;
|
||||
let x = &mut x
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
@@ -1244,14 +1230,14 @@ where
|
||||
assign(vectorizelanes(x, out_ty)?)
|
||||
}
|
||||
Opcode::Isplit => assign_multiple(&[
|
||||
DataValueExt::convert(arg(0)?, ValueConversionKind::Truncate(types::I64))?,
|
||||
DataValueExt::convert(arg(0)?, ValueConversionKind::ExtractUpper(types::I64))?,
|
||||
DataValueExt::convert(arg(0), ValueConversionKind::Truncate(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 => {
|
||||
let op = inst.atomic_rmw_op().unwrap();
|
||||
let val = arg(1)?;
|
||||
let addr = arg(0)?.into_int()? as u64;
|
||||
let val = arg(1);
|
||||
let addr = arg(0).into_int()? as u64;
|
||||
let mem_flags = inst.memflags().expect("instruction to have memory flags");
|
||||
let loaded = Address::try_from(addr)
|
||||
.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))
|
||||
}
|
||||
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 loaded = Address::try_from(addr)
|
||||
.and_then(|addr| state.checked_load(addr, ctrl_ty, mem_flags));
|
||||
@@ -1294,9 +1280,9 @@ where
|
||||
Ok(v) => v,
|
||||
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_store = arg(2)?;
|
||||
let val_to_store = arg(2);
|
||||
Address::try_from(addr)
|
||||
.and_then(|addr| state.checked_store(addr, val_to_store, mem_flags))
|
||||
.map(|_| loaded_val)
|
||||
@@ -1307,7 +1293,7 @@ where
|
||||
}
|
||||
Opcode::AtomicLoad => {
|
||||
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");
|
||||
// We are doing a regular load here, this isn't actually thread safe.
|
||||
assign_or_memtrap(
|
||||
@@ -1316,8 +1302,8 @@ where
|
||||
)
|
||||
}
|
||||
Opcode::AtomicStore => {
|
||||
let val = arg(0)?;
|
||||
let addr = arg(1)?.into_int()? as u64;
|
||||
let val = arg(0);
|
||||
let addr = arg(1).into_int()? as u64;
|
||||
let mem_flags = inst.memflags().expect("instruction to have memory flags");
|
||||
// We are doing a regular store here, this isn't actually thread safe.
|
||||
continue_or_memtrap(
|
||||
@@ -1332,8 +1318,8 @@ where
|
||||
Opcode::SqmulRoundSat => {
|
||||
let lane_type = ctrl_ty.lane_type();
|
||||
let double_width = ctrl_ty.double_width().unwrap().lane_type();
|
||||
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?;
|
||||
let arg0 = extractlanes(&arg(0), ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1), ctrl_ty)?;
|
||||
let (min, max) = lane_type.bounds(true);
|
||||
let min: DataValue = DataValueExt::int(min as i128, double_width)?;
|
||||
let max: DataValue = DataValueExt::int(max as i128, double_width)?;
|
||||
@@ -1357,12 +1343,9 @@ where
|
||||
.collect::<ValueResult<SimdVec<_>>>()?;
|
||||
assign(vectorizelanes(&new_vec, ctrl_ty)?)
|
||||
}
|
||||
Opcode::IaddPairwise => assign(binary_pairwise(
|
||||
arg(0)?,
|
||||
arg(1)?,
|
||||
ctrl_ty,
|
||||
DataValueExt::add,
|
||||
)?),
|
||||
Opcode::IaddPairwise => {
|
||||
assign(binary_pairwise(arg(0), arg(1), ctrl_ty, DataValueExt::add)?)
|
||||
}
|
||||
Opcode::ExtractVector => {
|
||||
unimplemented!("ExtractVector not supported");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user