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 {
|
||||
/// 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].
|
||||
pub fn from_integer(imm: i128, ty: Type) -> Result<DataValue, DataValueCastFailure> {
|
||||
match ty {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
test interpret
|
||||
test run
|
||||
target aarch64
|
||||
; 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
|
||||
target aarch64
|
||||
; target s390x TODO: Not yet implemented on s390x
|
||||
|
||||
@@ -56,14 +56,14 @@ where
|
||||
.params
|
||||
.iter()
|
||||
.map(|p| {
|
||||
let imm64 = match p.value_type {
|
||||
I8 => self.u.arbitrary::<i8>()? as i64,
|
||||
I16 => self.u.arbitrary::<i16>()? as i64,
|
||||
I32 => self.u.arbitrary::<i32>()? as i64,
|
||||
I64 => self.u.arbitrary::<i64>()?,
|
||||
let imm = match p.value_type {
|
||||
I8 => self.u.arbitrary::<i8>()? as i128,
|
||||
I16 => self.u.arbitrary::<i16>()? as i128,
|
||||
I32 => self.u.arbitrary::<i32>()? as i128,
|
||||
I64 => self.u.arbitrary::<i64>()? as i128,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(DataValue::from_integer(imm64, p.value_type)?)
|
||||
Ok(DataValue::from_integer(imm, p.value_type)?)
|
||||
})
|
||||
.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.
|
||||
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
|
||||
let assign_or_trap = |value: ValueResult<V>| match value {
|
||||
Ok(v) => Ok(assign(v)),
|
||||
@@ -147,7 +150,7 @@ where
|
||||
};
|
||||
|
||||
// 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;
|
||||
for t in tail {
|
||||
acc = Value::add(acc, t)?;
|
||||
@@ -615,8 +618,11 @@ where
|
||||
Opcode::FcvtLowFromSint => unimplemented!("FcvtLowFromSint"),
|
||||
Opcode::FvpromoteLow => unimplemented!("FvpromoteLow"),
|
||||
Opcode::Fvdemote => unimplemented!("Fvdemote"),
|
||||
Opcode::Isplit => unimplemented!("Isplit"),
|
||||
Opcode::Iconcat => unimplemented!("Iconcat"),
|
||||
Opcode::Isplit => assign_multiple(&[
|
||||
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::AtomicCas => unimplemented!("AtomicCas"),
|
||||
Opcode::AtomicLoad => unimplemented!("AtomicLoad"),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! values.
|
||||
use core::convert::TryFrom;
|
||||
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::{types, Type};
|
||||
use thiserror::Error;
|
||||
@@ -14,8 +14,8 @@ pub type ValueResult<T> = Result<T, ValueError>;
|
||||
pub trait Value: Clone + From<DataValue> {
|
||||
// Identity.
|
||||
fn ty(&self) -> Type;
|
||||
fn int(n: i64, ty: Type) -> ValueResult<Self>;
|
||||
fn into_int(self) -> ValueResult<i64>;
|
||||
fn int(n: i128, ty: Type) -> ValueResult<Self>;
|
||||
fn into_int(self) -> ValueResult<i128>;
|
||||
fn float(n: u64, ty: Type) -> ValueResult<Self>;
|
||||
fn into_float(self) -> ValueResult<f64>;
|
||||
fn is_nan(&self) -> ValueResult<bool>;
|
||||
@@ -23,6 +23,7 @@ pub trait Value: Clone + From<DataValue> {
|
||||
fn into_bool(self) -> ValueResult<bool>;
|
||||
fn vector(v: [u8; 16], ty: Type) -> ValueResult<Self>;
|
||||
fn convert(self, kind: ValueConversionKind) -> ValueResult<Self>;
|
||||
fn concat(self, other: Self) -> ValueResult<Self>;
|
||||
|
||||
// Comparison.
|
||||
fn eq(&self, other: &Self) -> ValueResult<bool>;
|
||||
@@ -66,6 +67,8 @@ pub enum ValueError {
|
||||
InvalidValue(Type),
|
||||
#[error("unable to convert to primitive integer")]
|
||||
InvalidInteger(#[from] std::num::TryFromIntError),
|
||||
#[error("unable to cast data value")]
|
||||
InvalidDataValueCast(#[from] DataValueCastFailure),
|
||||
#[error("performed a division by zero")]
|
||||
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
|
||||
/// `0x34`.
|
||||
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`
|
||||
/// becomes `0xffff`.
|
||||
SignExtend(Type),
|
||||
@@ -161,7 +167,7 @@ impl Value for DataValue {
|
||||
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() {
|
||||
DataValue::from_integer(n, ty).map_err(|_| ValueError::InvalidValue(ty))
|
||||
} else {
|
||||
@@ -169,16 +175,18 @@ impl Value for DataValue {
|
||||
}
|
||||
}
|
||||
|
||||
fn into_int(self) -> ValueResult<i64> {
|
||||
fn into_int(self) -> ValueResult<i128> {
|
||||
match self {
|
||||
DataValue::I8(n) => Ok(n as i64),
|
||||
DataValue::I16(n) => Ok(n as i64),
|
||||
DataValue::I32(n) => Ok(n as i64),
|
||||
DataValue::I64(n) => Ok(n),
|
||||
DataValue::U8(n) => Ok(n as i64),
|
||||
DataValue::U16(n) => Ok(n as i64),
|
||||
DataValue::U32(n) => Ok(n as i64),
|
||||
DataValue::U64(n) => Ok(n as i64),
|
||||
DataValue::I8(n) => Ok(n as i128),
|
||||
DataValue::I16(n) => Ok(n as i128),
|
||||
DataValue::I32(n) => Ok(n as i128),
|
||||
DataValue::I64(n) => Ok(n as i128),
|
||||
DataValue::I128(n) => Ok(n),
|
||||
DataValue::U8(n) => Ok(n as i128),
|
||||
DataValue::U16(n) => Ok(n as i128),
|
||||
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())),
|
||||
}
|
||||
}
|
||||
@@ -227,15 +235,33 @@ impl Value for DataValue {
|
||||
(DataValue::B(b), t) if t.is_bool() => DataValue::B(b),
|
||||
(dv, _) => unimplemented!("conversion: {} -> {:?}", dv.ty(), kind),
|
||||
},
|
||||
ValueConversionKind::Truncate(ty) => match (self.ty(), ty) {
|
||||
(types::I64, types::I32) => unimplemented!(),
|
||||
(types::I64, types::I16) => unimplemented!(),
|
||||
(types::I64, types::I8) => unimplemented!(),
|
||||
(types::I32, types::I16) => unimplemented!(),
|
||||
(types::I32, types::I8) => unimplemented!(),
|
||||
(types::I16, types::I8) => unimplemented!(),
|
||||
_ => unimplemented!("conversion: {} -> {:?}", self.ty(), kind),
|
||||
},
|
||||
ValueConversionKind::Truncate(ty) => {
|
||||
assert!(
|
||||
ty.is_int(),
|
||||
"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) {
|
||||
(types::I8, types::I16) => 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> {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
|
||||
Reference in New Issue
Block a user