cranelift: Add basic i128 support in interpreter
This commit is contained in:
committed by
Andrew Brown
parent
084383f60a
commit
a2fb019ba7
@@ -30,7 +30,7 @@ pub enum DataValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DataValue {
|
impl DataValue {
|
||||||
/// Try to cast an immediate integer (a wrapped `i128` on most Cranelift instructions) to the
|
/// Try to cast an immediate integer (a wrapped `i64` on most Cranelift instructions) to the
|
||||||
/// given Cranelift [Type].
|
/// given Cranelift [Type].
|
||||||
pub fn from_integer(imm: i128, ty: Type) -> Result<DataValue, DataValueCastFailure> {
|
pub fn from_integer(imm: i128, ty: Type) -> Result<DataValue, DataValueCastFailure> {
|
||||||
match ty {
|
match ty {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
test interpret
|
||||||
test run
|
test run
|
||||||
target aarch64
|
target aarch64
|
||||||
; target s390x TODO: Not yet implemented on s390x
|
; target s390x TODO: Not yet implemented on s390x
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
test interpret
|
||||||
|
test run
|
||||||
|
target aarch64
|
||||||
|
target s390x
|
||||||
|
target x86_64 machinst
|
||||||
|
|
||||||
|
function %iconcat_isplit(i64, i64) -> i64, i64 {
|
||||||
|
block0(v0: i64, v1: i64):
|
||||||
|
v2 = iconcat v0, v1
|
||||||
|
v3, v4 = isplit v2
|
||||||
|
return v3, v4
|
||||||
|
}
|
||||||
|
; run: %iconcat_isplit(0, 0) == [0, 0]
|
||||||
|
; run: %iconcat_isplit(1, 1) == [1, 1]
|
||||||
|
; run: %iconcat_isplit(0xFFFFFFFF_FFFFFFFF, 0) == [0xFFFFFFFF_FFFFFFFF, 0]
|
||||||
|
; run: %iconcat_isplit(0, 0xFFFFFFFF_FFFFFFFF) == [0, 0xFFFFFFFF_FFFFFFFF]
|
||||||
|
; run: %iconcat_isplit(0x01010101_01010101, 0x02020202_02020202) == [0x01010101_01010101, 0x02020202_02020202]
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
test interpret
|
||||||
test run
|
test run
|
||||||
target aarch64
|
target aarch64
|
||||||
; target s390x TODO: Not yet implemented on s390x
|
; target s390x TODO: Not yet implemented on s390x
|
||||||
|
|||||||
@@ -56,14 +56,14 @@ where
|
|||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
let imm64 = match p.value_type {
|
let imm = match p.value_type {
|
||||||
I8 => self.u.arbitrary::<i8>()? as i64,
|
I8 => self.u.arbitrary::<i8>()? as i128,
|
||||||
I16 => self.u.arbitrary::<i16>()? as i64,
|
I16 => self.u.arbitrary::<i16>()? as i128,
|
||||||
I32 => self.u.arbitrary::<i32>()? as i64,
|
I32 => self.u.arbitrary::<i32>()? as i128,
|
||||||
I64 => self.u.arbitrary::<i64>()?,
|
I64 => self.u.arbitrary::<i64>()? as i128,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
Ok(DataValue::from_integer(imm64, p.value_type)?)
|
Ok(DataValue::from_integer(imm, p.value_type)?)
|
||||||
})
|
})
|
||||||
.collect::<Result<TestCaseInput>>()?;
|
.collect::<Result<TestCaseInput>>()?;
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ where
|
|||||||
// Indicate that the result of a step is to assign a single value to an instruction's results.
|
// Indicate that the result of a step is to assign a single value to an instruction's results.
|
||||||
let assign = |value: V| ControlFlow::Assign(smallvec![value]);
|
let assign = |value: V| ControlFlow::Assign(smallvec![value]);
|
||||||
|
|
||||||
|
// Indicate that the result of a step is to assign multiple values to an instruction's results.
|
||||||
|
let assign_multiple = |values: &[V]| ControlFlow::Assign(SmallVec::from(values));
|
||||||
|
|
||||||
// Similar to `assign` but converts some errors into traps
|
// Similar to `assign` but converts some errors into traps
|
||||||
let assign_or_trap = |value: ValueResult<V>| match value {
|
let assign_or_trap = |value: ValueResult<V>| match value {
|
||||||
Ok(v) => Ok(assign(v)),
|
Ok(v) => Ok(assign(v)),
|
||||||
@@ -147,7 +150,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Helper for summing a sequence of values.
|
// Helper for summing a sequence of values.
|
||||||
fn sum<V: Value>(head: V, tail: SmallVec<[V; 1]>) -> ValueResult<i64> {
|
fn sum<V: Value>(head: V, tail: SmallVec<[V; 1]>) -> ValueResult<i128> {
|
||||||
let mut acc = head;
|
let mut acc = head;
|
||||||
for t in tail {
|
for t in tail {
|
||||||
acc = Value::add(acc, t)?;
|
acc = Value::add(acc, t)?;
|
||||||
@@ -615,8 +618,11 @@ where
|
|||||||
Opcode::FcvtLowFromSint => unimplemented!("FcvtLowFromSint"),
|
Opcode::FcvtLowFromSint => unimplemented!("FcvtLowFromSint"),
|
||||||
Opcode::FvpromoteLow => unimplemented!("FvpromoteLow"),
|
Opcode::FvpromoteLow => unimplemented!("FvpromoteLow"),
|
||||||
Opcode::Fvdemote => unimplemented!("Fvdemote"),
|
Opcode::Fvdemote => unimplemented!("Fvdemote"),
|
||||||
Opcode::Isplit => unimplemented!("Isplit"),
|
Opcode::Isplit => assign_multiple(&[
|
||||||
Opcode::Iconcat => unimplemented!("Iconcat"),
|
Value::convert(arg(0)?, ValueConversionKind::Truncate(types::I64))?,
|
||||||
|
Value::convert(arg(0)?, ValueConversionKind::ExtractUpper(types::I64))?,
|
||||||
|
]),
|
||||||
|
Opcode::Iconcat => assign(Value::concat(arg(0)?, arg(1)?)?),
|
||||||
Opcode::AtomicRmw => unimplemented!("AtomicRmw"),
|
Opcode::AtomicRmw => unimplemented!("AtomicRmw"),
|
||||||
Opcode::AtomicCas => unimplemented!("AtomicCas"),
|
Opcode::AtomicCas => unimplemented!("AtomicCas"),
|
||||||
Opcode::AtomicLoad => unimplemented!("AtomicLoad"),
|
Opcode::AtomicLoad => unimplemented!("AtomicLoad"),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
//! values.
|
//! values.
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::fmt::{self, Display, Formatter};
|
use core::fmt::{self, Display, Formatter};
|
||||||
use cranelift_codegen::data_value::DataValue;
|
use cranelift_codegen::data_value::{DataValue, DataValueCastFailure};
|
||||||
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64};
|
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64};
|
||||||
use cranelift_codegen::ir::{types, Type};
|
use cranelift_codegen::ir::{types, Type};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -14,8 +14,8 @@ pub type ValueResult<T> = Result<T, ValueError>;
|
|||||||
pub trait Value: Clone + From<DataValue> {
|
pub trait Value: Clone + From<DataValue> {
|
||||||
// Identity.
|
// Identity.
|
||||||
fn ty(&self) -> Type;
|
fn ty(&self) -> Type;
|
||||||
fn int(n: i64, ty: Type) -> ValueResult<Self>;
|
fn int(n: i128, ty: Type) -> ValueResult<Self>;
|
||||||
fn into_int(self) -> ValueResult<i64>;
|
fn into_int(self) -> ValueResult<i128>;
|
||||||
fn float(n: u64, ty: Type) -> ValueResult<Self>;
|
fn float(n: u64, ty: Type) -> ValueResult<Self>;
|
||||||
fn into_float(self) -> ValueResult<f64>;
|
fn into_float(self) -> ValueResult<f64>;
|
||||||
fn is_nan(&self) -> ValueResult<bool>;
|
fn is_nan(&self) -> ValueResult<bool>;
|
||||||
@@ -23,6 +23,7 @@ pub trait Value: Clone + From<DataValue> {
|
|||||||
fn into_bool(self) -> ValueResult<bool>;
|
fn into_bool(self) -> ValueResult<bool>;
|
||||||
fn vector(v: [u8; 16], ty: Type) -> ValueResult<Self>;
|
fn vector(v: [u8; 16], ty: Type) -> ValueResult<Self>;
|
||||||
fn convert(self, kind: ValueConversionKind) -> ValueResult<Self>;
|
fn convert(self, kind: ValueConversionKind) -> ValueResult<Self>;
|
||||||
|
fn concat(self, other: Self) -> ValueResult<Self>;
|
||||||
|
|
||||||
// Comparison.
|
// Comparison.
|
||||||
fn eq(&self, other: &Self) -> ValueResult<bool>;
|
fn eq(&self, other: &Self) -> ValueResult<bool>;
|
||||||
@@ -66,6 +67,8 @@ pub enum ValueError {
|
|||||||
InvalidValue(Type),
|
InvalidValue(Type),
|
||||||
#[error("unable to convert to primitive integer")]
|
#[error("unable to convert to primitive integer")]
|
||||||
InvalidInteger(#[from] std::num::TryFromIntError),
|
InvalidInteger(#[from] std::num::TryFromIntError),
|
||||||
|
#[error("unable to cast data value")]
|
||||||
|
InvalidDataValueCast(#[from] DataValueCastFailure),
|
||||||
#[error("performed a division by zero")]
|
#[error("performed a division by zero")]
|
||||||
IntegerDivisionByZero,
|
IntegerDivisionByZero,
|
||||||
}
|
}
|
||||||
@@ -95,6 +98,9 @@ pub enum ValueConversionKind {
|
|||||||
/// Truncate the value to fit into the specified [Type]; e.g. in `i16` to `i8`, `0x1234` becomes
|
/// Truncate the value to fit into the specified [Type]; e.g. in `i16` to `i8`, `0x1234` becomes
|
||||||
/// `0x34`.
|
/// `0x34`.
|
||||||
Truncate(Type),
|
Truncate(Type),
|
||||||
|
/// Similar to Truncate, but extracts from the top of the value; e.g. in a `i32` to `u8`,
|
||||||
|
/// `0x12345678` becomes `0x12`.
|
||||||
|
ExtractUpper(Type),
|
||||||
/// Convert to a larger integer type, extending the sign bit; e.g. in `i8` to `i16`, `0xff`
|
/// Convert to a larger integer type, extending the sign bit; e.g. in `i8` to `i16`, `0xff`
|
||||||
/// becomes `0xffff`.
|
/// becomes `0xffff`.
|
||||||
SignExtend(Type),
|
SignExtend(Type),
|
||||||
@@ -161,7 +167,7 @@ impl Value for DataValue {
|
|||||||
self.ty()
|
self.ty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int(n: i64, ty: Type) -> ValueResult<Self> {
|
fn int(n: i128, ty: Type) -> ValueResult<Self> {
|
||||||
if ty.is_int() && !ty.is_vector() {
|
if ty.is_int() && !ty.is_vector() {
|
||||||
DataValue::from_integer(n, ty).map_err(|_| ValueError::InvalidValue(ty))
|
DataValue::from_integer(n, ty).map_err(|_| ValueError::InvalidValue(ty))
|
||||||
} else {
|
} else {
|
||||||
@@ -169,16 +175,18 @@ impl Value for DataValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_int(self) -> ValueResult<i64> {
|
fn into_int(self) -> ValueResult<i128> {
|
||||||
match self {
|
match self {
|
||||||
DataValue::I8(n) => Ok(n as i64),
|
DataValue::I8(n) => Ok(n as i128),
|
||||||
DataValue::I16(n) => Ok(n as i64),
|
DataValue::I16(n) => Ok(n as i128),
|
||||||
DataValue::I32(n) => Ok(n as i64),
|
DataValue::I32(n) => Ok(n as i128),
|
||||||
DataValue::I64(n) => Ok(n),
|
DataValue::I64(n) => Ok(n as i128),
|
||||||
DataValue::U8(n) => Ok(n as i64),
|
DataValue::I128(n) => Ok(n),
|
||||||
DataValue::U16(n) => Ok(n as i64),
|
DataValue::U8(n) => Ok(n as i128),
|
||||||
DataValue::U32(n) => Ok(n as i64),
|
DataValue::U16(n) => Ok(n as i128),
|
||||||
DataValue::U64(n) => Ok(n as i64),
|
DataValue::U32(n) => Ok(n as i128),
|
||||||
|
DataValue::U64(n) => Ok(n as i128),
|
||||||
|
DataValue::U128(n) => Ok(n as i128),
|
||||||
_ => Err(ValueError::InvalidType(ValueTypeClass::Integer, self.ty())),
|
_ => Err(ValueError::InvalidType(ValueTypeClass::Integer, self.ty())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,15 +235,33 @@ impl Value for DataValue {
|
|||||||
(DataValue::B(b), t) if t.is_bool() => DataValue::B(b),
|
(DataValue::B(b), t) if t.is_bool() => DataValue::B(b),
|
||||||
(dv, _) => unimplemented!("conversion: {} -> {:?}", dv.ty(), kind),
|
(dv, _) => unimplemented!("conversion: {} -> {:?}", dv.ty(), kind),
|
||||||
},
|
},
|
||||||
ValueConversionKind::Truncate(ty) => match (self.ty(), ty) {
|
ValueConversionKind::Truncate(ty) => {
|
||||||
(types::I64, types::I32) => unimplemented!(),
|
assert!(
|
||||||
(types::I64, types::I16) => unimplemented!(),
|
ty.is_int(),
|
||||||
(types::I64, types::I8) => unimplemented!(),
|
"unimplemented conversion: {} -> {:?}",
|
||||||
(types::I32, types::I16) => unimplemented!(),
|
self.ty(),
|
||||||
(types::I32, types::I8) => unimplemented!(),
|
kind
|
||||||
(types::I16, types::I8) => unimplemented!(),
|
);
|
||||||
_ => unimplemented!("conversion: {} -> {:?}", self.ty(), kind),
|
|
||||||
},
|
let mask = (1 << (ty.bytes() * 8)) - 1i128;
|
||||||
|
let truncated = self.into_int()? & mask;
|
||||||
|
Self::from_integer(truncated, ty)?
|
||||||
|
}
|
||||||
|
ValueConversionKind::ExtractUpper(ty) => {
|
||||||
|
assert!(
|
||||||
|
ty.is_int(),
|
||||||
|
"unimplemented conversion: {} -> {:?}",
|
||||||
|
self.ty(),
|
||||||
|
kind
|
||||||
|
);
|
||||||
|
|
||||||
|
let shift_amt = 128 - (ty.bytes() * 8);
|
||||||
|
let mask = (1 << (ty.bytes() * 8)) - 1i128;
|
||||||
|
let shifted_mask = mask << shift_amt;
|
||||||
|
|
||||||
|
let extracted = (self.into_int()? & shifted_mask) >> shift_amt;
|
||||||
|
Self::from_integer(extracted, ty)?
|
||||||
|
}
|
||||||
ValueConversionKind::SignExtend(ty) => match (self.ty(), ty) {
|
ValueConversionKind::SignExtend(ty) => match (self.ty(), ty) {
|
||||||
(types::I8, types::I16) => unimplemented!(),
|
(types::I8, types::I16) => unimplemented!(),
|
||||||
(types::I8, types::I32) => unimplemented!(),
|
(types::I8, types::I32) => unimplemented!(),
|
||||||
@@ -280,6 +306,15 @@ impl Value for DataValue {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn concat(self, other: Self) -> ValueResult<Self> {
|
||||||
|
match (self, other) {
|
||||||
|
(DataValue::I64(lhs), DataValue::I64(rhs)) => Ok(DataValue::I128(
|
||||||
|
(((lhs as u64) as u128) | (((rhs as u64) as u128) << 64)) as i128,
|
||||||
|
)),
|
||||||
|
(lhs, rhs) => unimplemented!("concat: {} -> {}", lhs.ty(), rhs.ty()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn eq(&self, other: &Self) -> ValueResult<bool> {
|
fn eq(&self, other: &Self) -> ValueResult<bool> {
|
||||||
comparison_match!(PartialEq::eq[&self, &other]; [I8, I16, I32, I64, U8, U16, U32, U64, F32, F64])
|
comparison_match!(PartialEq::eq[&self, &other]; [I8, I16, I32, I64, U8, U16, U32, U64, F32, F64])
|
||||||
}
|
}
|
||||||
@@ -303,15 +338,15 @@ impl Value for DataValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add(self, other: Self) -> ValueResult<Self> {
|
fn add(self, other: Self) -> ValueResult<Self> {
|
||||||
binary_match!(wrapping_add(&self, &other); [I8, I16, I32, I64]) // TODO: floats must handle NaNs, +/-0
|
binary_match!(wrapping_add(&self, &other); [I8, I16, I32, I64, I128]) // TODO: floats must handle NaNs, +/-0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sub(self, other: Self) -> ValueResult<Self> {
|
fn sub(self, other: Self) -> ValueResult<Self> {
|
||||||
binary_match!(wrapping_sub(&self, &other); [I8, I16, I32, I64]) // TODO: floats must handle NaNs, +/-0
|
binary_match!(wrapping_sub(&self, &other); [I8, I16, I32, I64, I128]) // TODO: floats must handle NaNs, +/-0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mul(self, other: Self) -> ValueResult<Self> {
|
fn mul(self, other: Self) -> ValueResult<Self> {
|
||||||
binary_match!(wrapping_mul(&self, &other); [I8, I16, I32, I64])
|
binary_match!(wrapping_mul(&self, &other); [I8, I16, I32, I64, I128])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn div(self, other: Self) -> ValueResult<Self> {
|
fn div(self, other: Self) -> ValueResult<Self> {
|
||||||
|
|||||||
Reference in New Issue
Block a user