cranelift: Add big and little endian memory accesses to interpreter (#5893)
* Added `mem_flags` parameter to `State::checked_{load,store}` as the means
for determining the endianness, typically derived from an instruction.
* Added `native_endianness` property to `InterpreterState` as fallback when
determining endianness, such as in cases where there are no memory flags
avaiable or set.
* Added `to_be` and `to_le` methods to `DataValue`.
* Added `AtomicCas` and `AtomicRmw` to list of instructions with retrievable
memory flags for `InstructionData::memflags`.
* Enabled `atomic-{cas,rmw}-subword-{big,little}.clif` for interpreter run
tests.
This commit is contained in:
committed by
GitHub
parent
9984e959cd
commit
db8fe0108f
@@ -101,12 +101,55 @@ impl DataValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a [DataValue] to a slice.
|
fn swap_bytes(self) -> Self {
|
||||||
|
match self {
|
||||||
|
DataValue::I8(i) => DataValue::I8(i.swap_bytes()),
|
||||||
|
DataValue::I16(i) => DataValue::I16(i.swap_bytes()),
|
||||||
|
DataValue::I32(i) => DataValue::I32(i.swap_bytes()),
|
||||||
|
DataValue::I64(i) => DataValue::I64(i.swap_bytes()),
|
||||||
|
DataValue::I128(i) => DataValue::I128(i.swap_bytes()),
|
||||||
|
DataValue::U8(i) => DataValue::U8(i.swap_bytes()),
|
||||||
|
DataValue::U16(i) => DataValue::U16(i.swap_bytes()),
|
||||||
|
DataValue::U32(i) => DataValue::U32(i.swap_bytes()),
|
||||||
|
DataValue::U64(i) => DataValue::U64(i.swap_bytes()),
|
||||||
|
DataValue::U128(i) => DataValue::U128(i.swap_bytes()),
|
||||||
|
DataValue::F32(f) => DataValue::F32(Ieee32::with_bits(f.bits().swap_bytes())),
|
||||||
|
DataValue::F64(f) => DataValue::F64(Ieee64::with_bits(f.bits().swap_bytes())),
|
||||||
|
DataValue::V128(mut v) => {
|
||||||
|
v.reverse();
|
||||||
|
DataValue::V128(v)
|
||||||
|
}
|
||||||
|
DataValue::V64(mut v) => {
|
||||||
|
v.reverse();
|
||||||
|
DataValue::V64(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts `self` to big endian from target's endianness.
|
||||||
|
pub fn to_be(self) -> Self {
|
||||||
|
if cfg!(target_endian = "big") {
|
||||||
|
self
|
||||||
|
} else {
|
||||||
|
self.swap_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts `self` to little endian from target's endianness.
|
||||||
|
pub fn to_le(self) -> Self {
|
||||||
|
if cfg!(target_endian = "little") {
|
||||||
|
self
|
||||||
|
} else {
|
||||||
|
self.swap_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a [DataValue] to a slice in native-endian byte order.
|
||||||
///
|
///
|
||||||
/// # Panics:
|
/// # Panics:
|
||||||
///
|
///
|
||||||
/// Panics if the slice does not have enough space to accommodate the [DataValue]
|
/// Panics if the slice does not have enough space to accommodate the [DataValue]
|
||||||
pub fn write_to_slice(&self, dst: &mut [u8]) {
|
pub fn write_to_slice_ne(&self, dst: &mut [u8]) {
|
||||||
match self {
|
match self {
|
||||||
DataValue::I8(i) => dst[..1].copy_from_slice(&i.to_ne_bytes()[..]),
|
DataValue::I8(i) => dst[..1].copy_from_slice(&i.to_ne_bytes()[..]),
|
||||||
DataValue::I16(i) => dst[..2].copy_from_slice(&i.to_ne_bytes()[..]),
|
DataValue::I16(i) => dst[..2].copy_from_slice(&i.to_ne_bytes()[..]),
|
||||||
@@ -121,12 +164,30 @@ impl DataValue {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a [DataValue] from a slice using a given [Type].
|
/// Write a [DataValue] to a slice in big-endian byte order.
|
||||||
///
|
///
|
||||||
/// # Panics:
|
/// # Panics:
|
||||||
///
|
///
|
||||||
/// Panics if the slice does not have enough space to accommodate the [DataValue]
|
/// Panics if the slice does not have enough space to accommodate the [DataValue]
|
||||||
pub fn read_from_slice(src: &[u8], ty: Type) -> Self {
|
pub fn write_to_slice_be(&self, dst: &mut [u8]) {
|
||||||
|
self.clone().to_be().write_to_slice_ne(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a [DataValue] to a slice in little-endian byte order.
|
||||||
|
///
|
||||||
|
/// # Panics:
|
||||||
|
///
|
||||||
|
/// Panics if the slice does not have enough space to accommodate the [DataValue]
|
||||||
|
pub fn write_to_slice_le(&self, dst: &mut [u8]) {
|
||||||
|
self.clone().to_le().write_to_slice_ne(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a [DataValue] from a slice using a given [Type] with native-endian byte order.
|
||||||
|
///
|
||||||
|
/// # Panics:
|
||||||
|
///
|
||||||
|
/// Panics if the slice does not have enough space to accommodate the [DataValue]
|
||||||
|
pub fn read_from_slice_ne(src: &[u8], ty: Type) -> Self {
|
||||||
match ty {
|
match ty {
|
||||||
types::I8 => DataValue::I8(i8::from_ne_bytes(src[..1].try_into().unwrap())),
|
types::I8 => DataValue::I8(i8::from_ne_bytes(src[..1].try_into().unwrap())),
|
||||||
types::I16 => DataValue::I16(i16::from_ne_bytes(src[..2].try_into().unwrap())),
|
types::I16 => DataValue::I16(i16::from_ne_bytes(src[..2].try_into().unwrap())),
|
||||||
@@ -152,15 +213,33 @@ impl DataValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a [DataValue] to a memory location.
|
/// Read a [DataValue] from a slice using a given [Type] in big-endian byte order.
|
||||||
pub unsafe fn write_value_to(&self, p: *mut u128) {
|
///
|
||||||
let size = self.ty().bytes() as usize;
|
/// # Panics:
|
||||||
self.write_to_slice(std::slice::from_raw_parts_mut(p as *mut u8, size));
|
///
|
||||||
|
/// Panics if the slice does not have enough space to accommodate the [DataValue]
|
||||||
|
pub fn read_from_slice_be(src: &[u8], ty: Type) -> Self {
|
||||||
|
DataValue::read_from_slice_ne(src, ty).to_be()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a [DataValue] from a memory location using a given [Type].
|
/// Read a [DataValue] from a slice using a given [Type] in little-endian byte order.
|
||||||
|
///
|
||||||
|
/// # Panics:
|
||||||
|
///
|
||||||
|
/// Panics if the slice does not have enough space to accommodate the [DataValue]
|
||||||
|
pub fn read_from_slice_le(src: &[u8], ty: Type) -> Self {
|
||||||
|
DataValue::read_from_slice_ne(src, ty).to_le()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a [DataValue] to a memory location in native-endian byte order.
|
||||||
|
pub unsafe fn write_value_to(&self, p: *mut u128) {
|
||||||
|
let size = self.ty().bytes() as usize;
|
||||||
|
self.write_to_slice_ne(std::slice::from_raw_parts_mut(p as *mut u8, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a [DataValue] from a memory location using a given [Type] in native-endian byte order.
|
||||||
pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self {
|
pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self {
|
||||||
DataValue::read_from_slice(
|
DataValue::read_from_slice_ne(
|
||||||
std::slice::from_raw_parts(p as *const u8, ty.bytes() as usize),
|
std::slice::from_raw_parts(p as *const u8, ty.bytes() as usize),
|
||||||
ty,
|
ty,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -403,7 +403,9 @@ impl InstructionData {
|
|||||||
&InstructionData::Load { flags, .. }
|
&InstructionData::Load { flags, .. }
|
||||||
| &InstructionData::LoadNoOffset { flags, .. }
|
| &InstructionData::LoadNoOffset { flags, .. }
|
||||||
| &InstructionData::Store { flags, .. }
|
| &InstructionData::Store { flags, .. }
|
||||||
| &InstructionData::StoreNoOffset { flags, .. } => Some(flags),
|
| &InstructionData::StoreNoOffset { flags, .. }
|
||||||
|
| &InstructionData::AtomicCas { flags, .. }
|
||||||
|
| &InstructionData::AtomicRmw { flags, .. } => Some(flags),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
test interpret
|
||||||
test run
|
test run
|
||||||
target s390x
|
target s390x
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
test interpret
|
||||||
test run
|
test run
|
||||||
target s390x
|
target s390x
|
||||||
target aarch64
|
target aarch64
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
test interpret
|
||||||
test run
|
test run
|
||||||
target s390x
|
target s390x
|
||||||
target s390x has_mie2
|
target s390x has_mie2
|
||||||
@@ -431,10 +432,10 @@ block0(v0: i32, v1: i64, v2: i16):
|
|||||||
v6 = load.i32 big v3
|
v6 = load.i32 big v3
|
||||||
return v6
|
return v6
|
||||||
}
|
}
|
||||||
; run: %atomic_rmw_xchg_little_i16(0x12345678, 0, 0x1111) == 0x11115678
|
; run: %atomic_rmw_xchg_big_i16(0x12345678, 0, 0x1111) == 0x11115678
|
||||||
; run: %atomic_rmw_xchg_little_i16(0x12345678, 0, 0xffff) == 0xffff5678
|
; run: %atomic_rmw_xchg_big_i16(0x12345678, 0, 0xffff) == 0xffff5678
|
||||||
; run: %atomic_rmw_xchg_little_i16(0x12345678, 2, 0x1111) == 0x12341111
|
; run: %atomic_rmw_xchg_big_i16(0x12345678, 2, 0x1111) == 0x12341111
|
||||||
; run: %atomic_rmw_xchg_little_i16(0x12345678, 2, 0xffff) == 0x1234ffff
|
; run: %atomic_rmw_xchg_big_i16(0x12345678, 2, 0xffff) == 0x1234ffff
|
||||||
|
|
||||||
|
|
||||||
function %atomic_rmw_xchg_big_i8(i32, i64, i8) -> i32 {
|
function %atomic_rmw_xchg_big_i8(i32, i64, i8) -> i32 {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
test interpret
|
||||||
test run
|
test run
|
||||||
target s390x
|
target s390x
|
||||||
target s390x has_mie2
|
target s390x has_mie2
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ impl fmt::Debug for TestCase {
|
|||||||
let returns = &self.main().signature.returns;
|
let returns = &self.main().signature.returns;
|
||||||
let placeholder_output = returns
|
let placeholder_output = returns
|
||||||
.iter()
|
.iter()
|
||||||
.map(|param| DataValue::read_from_slice(&[0; 16][..], param.value_type))
|
.map(|param| DataValue::read_from_slice_ne(&[0; 16][..], param.value_type))
|
||||||
.map(|val| format!("{}", val))
|
.map(|val| format!("{}", val))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ use crate::step::{step, ControlFlow, StepError};
|
|||||||
use crate::value::{Value, ValueError};
|
use crate::value::{Value, ValueError};
|
||||||
use cranelift_codegen::data_value::DataValue;
|
use cranelift_codegen::data_value::DataValue;
|
||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
ArgumentPurpose, Block, ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, LibCall,
|
ArgumentPurpose, Block, Endianness, ExternalName, FuncRef, Function, GlobalValue,
|
||||||
StackSlot, TrapCode, Type, Value as ValueRef,
|
GlobalValueData, LibCall, MemFlags, StackSlot, TrapCode, Type, Value as ValueRef,
|
||||||
};
|
};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@@ -192,10 +192,16 @@ pub struct InterpreterState<'a> {
|
|||||||
pub frame_offset: usize,
|
pub frame_offset: usize,
|
||||||
pub stack: Vec<u8>,
|
pub stack: Vec<u8>,
|
||||||
pub pinned_reg: DataValue,
|
pub pinned_reg: DataValue,
|
||||||
|
pub native_endianness: Endianness,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for InterpreterState<'_> {
|
impl Default for InterpreterState<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
let native_endianness = if cfg!(target_endian = "little") {
|
||||||
|
Endianness::Little
|
||||||
|
} else {
|
||||||
|
Endianness::Big
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
functions: FunctionStore::default(),
|
functions: FunctionStore::default(),
|
||||||
libcall_handler: |_, _| Err(TrapCode::UnreachableCodeReached),
|
libcall_handler: |_, _| Err(TrapCode::UnreachableCodeReached),
|
||||||
@@ -203,6 +209,7 @@ impl Default for InterpreterState<'_> {
|
|||||||
frame_offset: 0,
|
frame_offset: 0,
|
||||||
stack: Vec::with_capacity(1024),
|
stack: Vec::with_capacity(1024),
|
||||||
pinned_reg: DataValue::U64(0),
|
pinned_reg: DataValue::U64(0),
|
||||||
|
native_endianness,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,7 +315,12 @@ impl<'a> State<'a, DataValue> for InterpreterState<'a> {
|
|||||||
Address::from_parts(size, AddressRegion::Stack, 0, final_offset)
|
Address::from_parts(size, AddressRegion::Stack, 0, final_offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checked_load(&self, addr: Address, ty: Type) -> Result<DataValue, MemoryError> {
|
fn checked_load(
|
||||||
|
&self,
|
||||||
|
addr: Address,
|
||||||
|
ty: Type,
|
||||||
|
mem_flags: MemFlags,
|
||||||
|
) -> Result<DataValue, MemoryError> {
|
||||||
let load_size = ty.bytes() as usize;
|
let load_size = ty.bytes() as usize;
|
||||||
let addr_start = addr.offset as usize;
|
let addr_start = addr.offset as usize;
|
||||||
let addr_end = addr_start + load_size;
|
let addr_end = addr_start + load_size;
|
||||||
@@ -324,10 +336,18 @@ impl<'a> State<'a, DataValue> for InterpreterState<'a> {
|
|||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(DataValue::read_from_slice(src, ty))
|
Ok(match mem_flags.endianness(self.native_endianness) {
|
||||||
|
Endianness::Big => DataValue::read_from_slice_be(src, ty),
|
||||||
|
Endianness::Little => DataValue::read_from_slice_le(src, ty),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checked_store(&mut self, addr: Address, v: DataValue) -> Result<(), MemoryError> {
|
fn checked_store(
|
||||||
|
&mut self,
|
||||||
|
addr: Address,
|
||||||
|
v: DataValue,
|
||||||
|
mem_flags: MemFlags,
|
||||||
|
) -> Result<(), MemoryError> {
|
||||||
let store_size = v.ty().bytes() as usize;
|
let store_size = v.ty().bytes() as usize;
|
||||||
let addr_start = addr.offset as usize;
|
let addr_start = addr.offset as usize;
|
||||||
let addr_end = addr_start + store_size;
|
let addr_end = addr_start + store_size;
|
||||||
@@ -343,7 +363,10 @@ impl<'a> State<'a, DataValue> for InterpreterState<'a> {
|
|||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(v.write_to_slice(dst))
|
Ok(match mem_flags.endianness(self.native_endianness) {
|
||||||
|
Endianness::Big => v.write_to_slice_be(dst),
|
||||||
|
Endianness::Little => v.write_to_slice_le(dst),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_address(
|
fn function_address(
|
||||||
@@ -493,9 +516,10 @@ impl<'a> State<'a, DataValue> for InterpreterState<'a> {
|
|||||||
global_type,
|
global_type,
|
||||||
}) => {
|
}) => {
|
||||||
let mut addr = Address::try_from(current_val)?;
|
let mut addr = Address::try_from(current_val)?;
|
||||||
|
let mem_flags = MemFlags::trusted();
|
||||||
// We can forego bounds checking here since its performed in `checked_load`
|
// We can forego bounds checking here since its performed in `checked_load`
|
||||||
addr.offset += offset as u64;
|
addr.offset += offset as u64;
|
||||||
current_val = self.checked_load(addr, global_type)?;
|
current_val = self.checked_load(addr, global_type, mem_flags)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are done resolving this, return the current value
|
// We are done resolving this, return the current value
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ use crate::address::{Address, AddressSize};
|
|||||||
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::{
|
||||||
ExternalName, FuncRef, Function, GlobalValue, LibCall, Signature, StackSlot, Type, Value,
|
ExternalName, FuncRef, Function, GlobalValue, LibCall, MemFlags, Signature, StackSlot, Type,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
use cranelift_codegen::isa::CallConv;
|
use cranelift_codegen::isa::CallConv;
|
||||||
use cranelift_entity::PrimaryMap;
|
use cranelift_entity::PrimaryMap;
|
||||||
@@ -62,10 +63,20 @@ pub trait State<'a, V> {
|
|||||||
) -> Result<Address, MemoryError>;
|
) -> Result<Address, MemoryError>;
|
||||||
/// Retrieve a value `V` from memory at the given `address`, checking if it belongs either to the
|
/// Retrieve a value `V` from memory at the given `address`, checking if it belongs either to the
|
||||||
/// stack or to one of the heaps; the number of bytes loaded corresponds to the specified [Type].
|
/// stack or to one of the heaps; the number of bytes loaded corresponds to the specified [Type].
|
||||||
fn checked_load(&self, address: Address, ty: Type) -> Result<V, MemoryError>;
|
fn checked_load(
|
||||||
|
&self,
|
||||||
|
address: Address,
|
||||||
|
ty: Type,
|
||||||
|
mem_flags: MemFlags,
|
||||||
|
) -> Result<V, MemoryError>;
|
||||||
/// Store a value `V` into memory at the given `address`, checking if it belongs either to the
|
/// Store a value `V` into memory at the given `address`, checking if it belongs either to the
|
||||||
/// stack or to one of the heaps; the number of bytes stored corresponds to the specified [Type].
|
/// stack or to one of the heaps; the number of bytes stored corresponds to the specified [Type].
|
||||||
fn checked_store(&mut self, address: Address, v: V) -> Result<(), MemoryError>;
|
fn checked_store(
|
||||||
|
&mut self,
|
||||||
|
address: Address,
|
||||||
|
v: V,
|
||||||
|
mem_flags: MemFlags,
|
||||||
|
) -> Result<(), MemoryError>;
|
||||||
|
|
||||||
/// Compute the address of a function given its name.
|
/// Compute the address of a function given its name.
|
||||||
fn function_address(
|
fn function_address(
|
||||||
@@ -182,11 +193,21 @@ where
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checked_load(&self, _addr: Address, _ty: Type) -> Result<V, MemoryError> {
|
fn checked_load(
|
||||||
|
&self,
|
||||||
|
_addr: Address,
|
||||||
|
_ty: Type,
|
||||||
|
_mem_flags: MemFlags,
|
||||||
|
) -> Result<V, MemoryError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checked_store(&mut self, _addr: Address, _v: V) -> Result<(), MemoryError> {
|
fn checked_store(
|
||||||
|
&mut self,
|
||||||
|
_addr: Address,
|
||||||
|
_v: V,
|
||||||
|
_mem_flags: MemFlags,
|
||||||
|
) -> Result<(), MemoryError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use cranelift_codegen::data_value::DataValue;
|
|||||||
use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
|
use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
|
||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
types, AbiParam, AtomicRmwOp, Block, BlockCall, ExternalName, FuncRef, Function,
|
types, AbiParam, AtomicRmwOp, Block, BlockCall, ExternalName, FuncRef, Function,
|
||||||
InstructionData, Opcode, TrapCode, Type, Value as ValueRef,
|
InstructionData, MemFlags, Opcode, TrapCode, Type, Value as ValueRef,
|
||||||
};
|
};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
@@ -482,8 +482,10 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
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(
|
let loaded = assign_or_memtrap(
|
||||||
Address::try_from(addr_value).and_then(|addr| state.checked_load(addr, load_ty)),
|
Address::try_from(addr_value)
|
||||||
|
.and_then(|addr| state.checked_load(addr, load_ty, mem_flags)),
|
||||||
);
|
);
|
||||||
|
|
||||||
match (loaded, kind) {
|
match (loaded, kind) {
|
||||||
@@ -505,33 +507,37 @@ 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 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).and_then(|addr| state.checked_store(addr, reduced)),
|
Address::try_from(addr_value)
|
||||||
|
.and_then(|addr| state.checked_store(addr, reduced, mem_flags)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
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::trusted();
|
||||||
assign_or_memtrap({
|
assign_or_memtrap({
|
||||||
state
|
state
|
||||||
.stack_address(AddressSize::_64, slot, offset)
|
.stack_address(AddressSize::_64, slot, offset)
|
||||||
.and_then(|addr| state.checked_load(addr, load_ty))
|
.and_then(|addr| state.checked_load(addr, load_ty, mem_flags))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
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::trusted();
|
||||||
continue_or_memtrap({
|
continue_or_memtrap({
|
||||||
state
|
state
|
||||||
.stack_address(AddressSize::_64, slot, offset)
|
.stack_address(AddressSize::_64, slot, offset)
|
||||||
.and_then(|addr| state.checked_store(addr, arg))
|
.and_then(|addr| state.checked_store(addr, arg, mem_flags))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Opcode::StackAddr => {
|
Opcode::StackAddr => {
|
||||||
@@ -1238,7 +1244,9 @@ where
|
|||||||
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 loaded = Address::try_from(addr).and_then(|addr| state.checked_load(addr, ctrl_ty));
|
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));
|
||||||
let prev_val = match loaded {
|
let prev_val = match loaded {
|
||||||
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)))),
|
||||||
@@ -1265,13 +1273,15 @@ where
|
|||||||
)
|
)
|
||||||
.and_then(|v| Value::convert(v, ValueConversionKind::ToSigned)),
|
.and_then(|v| Value::convert(v, ValueConversionKind::ToSigned)),
|
||||||
}?;
|
}?;
|
||||||
let stored =
|
let stored = Address::try_from(addr)
|
||||||
Address::try_from(addr).and_then(|addr| state.checked_store(addr, replace));
|
.and_then(|addr| state.checked_store(addr, replace, mem_flags));
|
||||||
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 loaded = Address::try_from(addr).and_then(|addr| state.checked_load(addr, ctrl_ty));
|
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));
|
||||||
let loaded_val = match loaded {
|
let loaded_val = match loaded {
|
||||||
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)))),
|
||||||
@@ -1280,7 +1290,7 @@ where
|
|||||||
let val_to_assign = if Value::eq(&loaded_val, &expected_val)? {
|
let val_to_assign = if Value::eq(&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))
|
.and_then(|addr| state.checked_store(addr, val_to_store, mem_flags))
|
||||||
.map(|_| loaded_val)
|
.map(|_| loaded_val)
|
||||||
} else {
|
} else {
|
||||||
Ok(loaded_val)
|
Ok(loaded_val)
|
||||||
@@ -1290,17 +1300,20 @@ 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");
|
||||||
// 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(
|
||||||
Address::try_from(addr).and_then(|addr| state.checked_load(addr, load_ty)),
|
Address::try_from(addr)
|
||||||
|
.and_then(|addr| state.checked_load(addr, load_ty, mem_flags)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
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");
|
||||||
// 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(
|
||||||
Address::try_from(addr).and_then(|addr| state.checked_store(addr, val)),
|
Address::try_from(addr).and_then(|addr| state.checked_store(addr, val, mem_flags)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Opcode::Fence => {
|
Opcode::Fence => {
|
||||||
|
|||||||
Reference in New Issue
Block a user