cranelift: Add basic i128 support in interpreter

This commit is contained in:
Afonso Bordado
2021-07-23 12:15:00 +01:00
committed by Andrew Brown
parent 084383f60a
commit a2fb019ba7
7 changed files with 95 additions and 35 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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>>()?;

View File

@@ -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"),

View File

@@ -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> {