From a2fb019ba7381966d762a4726230e69f2cbdf438 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Fri, 23 Jul 2021 12:15:00 +0100 Subject: [PATCH] cranelift: Add basic i128 support in interpreter --- cranelift/codegen/src/data_value.rs | 2 +- .../filetests/runtests/i128-arithmetic.clif | 1 + .../filetests/runtests/i128-concat-split.clif | 17 ++++ .../filetests/runtests/i128-const.clif | 1 + cranelift/fuzzgen/src/lib.rs | 12 +-- cranelift/interpreter/src/step.rs | 12 ++- cranelift/interpreter/src/value.rs | 85 +++++++++++++------ 7 files changed, 95 insertions(+), 35 deletions(-) create mode 100644 cranelift/filetests/filetests/runtests/i128-concat-split.clif diff --git a/cranelift/codegen/src/data_value.rs b/cranelift/codegen/src/data_value.rs index 844b1aa553..370f2858d0 100644 --- a/cranelift/codegen/src/data_value.rs +++ b/cranelift/codegen/src/data_value.rs @@ -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 { match ty { diff --git a/cranelift/filetests/filetests/runtests/i128-arithmetic.clif b/cranelift/filetests/filetests/runtests/i128-arithmetic.clif index f692a4dacc..e5bbd08f6f 100644 --- a/cranelift/filetests/filetests/runtests/i128-arithmetic.clif +++ b/cranelift/filetests/filetests/runtests/i128-arithmetic.clif @@ -1,3 +1,4 @@ +test interpret test run target aarch64 ; target s390x TODO: Not yet implemented on s390x diff --git a/cranelift/filetests/filetests/runtests/i128-concat-split.clif b/cranelift/filetests/filetests/runtests/i128-concat-split.clif new file mode 100644 index 0000000000..8e9330325e --- /dev/null +++ b/cranelift/filetests/filetests/runtests/i128-concat-split.clif @@ -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] diff --git a/cranelift/filetests/filetests/runtests/i128-const.clif b/cranelift/filetests/filetests/runtests/i128-const.clif index 7fdda55397..604015a99f 100644 --- a/cranelift/filetests/filetests/runtests/i128-const.clif +++ b/cranelift/filetests/filetests/runtests/i128-const.clif @@ -1,3 +1,4 @@ +test interpret test run target aarch64 ; target s390x TODO: Not yet implemented on s390x diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index ae1b3bb6bb..a18ce78960 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -56,14 +56,14 @@ where .params .iter() .map(|p| { - let imm64 = match p.value_type { - I8 => self.u.arbitrary::()? as i64, - I16 => self.u.arbitrary::()? as i64, - I32 => self.u.arbitrary::()? as i64, - I64 => self.u.arbitrary::()?, + let imm = match p.value_type { + I8 => self.u.arbitrary::()? as i128, + I16 => self.u.arbitrary::()? as i128, + I32 => self.u.arbitrary::()? as i128, + I64 => self.u.arbitrary::()? as i128, _ => unreachable!(), }; - Ok(DataValue::from_integer(imm64, p.value_type)?) + Ok(DataValue::from_integer(imm, p.value_type)?) }) .collect::>()?; diff --git a/cranelift/interpreter/src/step.rs b/cranelift/interpreter/src/step.rs index 3e2e346fc4..9c0763872b 100644 --- a/cranelift/interpreter/src/step.rs +++ b/cranelift/interpreter/src/step.rs @@ -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| match value { Ok(v) => Ok(assign(v)), @@ -147,7 +150,7 @@ where }; // Helper for summing a sequence of values. - fn sum(head: V, tail: SmallVec<[V; 1]>) -> ValueResult { + fn sum(head: V, tail: SmallVec<[V; 1]>) -> ValueResult { 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"), diff --git a/cranelift/interpreter/src/value.rs b/cranelift/interpreter/src/value.rs index 5c7528ac23..ff64436fc9 100644 --- a/cranelift/interpreter/src/value.rs +++ b/cranelift/interpreter/src/value.rs @@ -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 = Result; pub trait Value: Clone + From { // Identity. fn ty(&self) -> Type; - fn int(n: i64, ty: Type) -> ValueResult; - fn into_int(self) -> ValueResult; + fn int(n: i128, ty: Type) -> ValueResult; + fn into_int(self) -> ValueResult; fn float(n: u64, ty: Type) -> ValueResult; fn into_float(self) -> ValueResult; fn is_nan(&self) -> ValueResult; @@ -23,6 +23,7 @@ pub trait Value: Clone + From { fn into_bool(self) -> ValueResult; fn vector(v: [u8; 16], ty: Type) -> ValueResult; fn convert(self, kind: ValueConversionKind) -> ValueResult; + fn concat(self, other: Self) -> ValueResult; // Comparison. fn eq(&self, other: &Self) -> ValueResult; @@ -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 { + fn int(n: i128, ty: Type) -> ValueResult { 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 { + fn into_int(self) -> ValueResult { 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 { + 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 { 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 { - 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 { - 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 { - 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 {