Merge pull request #3332 from afonso360/interp-icmp

cranelift: Add SIMD `icmp` to interpreter
This commit is contained in:
Chris Fallin
2021-09-17 15:13:44 -07:00
committed by GitHub
29 changed files with 1170 additions and 54 deletions

View File

@@ -232,7 +232,9 @@ where
.convert(ValueConversionKind::ToBoolean)?
.into_bool()?,
)?,
Opcode::BrIcmp => branch_when(icmp(inst.cond_code().unwrap(), &arg(0)?, &arg(1)?)?)?,
Opcode::BrIcmp => {
branch_when(icmp(ctrl_ty, inst.cond_code().unwrap(), &arg(0)?, &arg(1)?)?.into_bool()?)?
}
Opcode::Brif => branch_when(state.has_iflag(inst.cond_code().unwrap()))?,
Opcode::Brff => branch_when(state.has_fflag(inst.fp_cond_code().unwrap()))?,
Opcode::BrTable => {
@@ -461,13 +463,17 @@ where
Opcode::Regspill => unimplemented!("Regspill"),
Opcode::Regfill => unimplemented!("Regfill"),
Opcode::Safepoint => unimplemented!("Safepoint"),
Opcode::Icmp => assign(Value::bool(
icmp(inst.cond_code().unwrap(), &arg(0)?, &arg(1)?)?,
ctrl_ty.as_bool(),
Opcode::Icmp => assign(icmp(
ctrl_ty,
inst.cond_code().unwrap(),
&arg(0)?,
&arg(1)?,
)?),
Opcode::IcmpImm => assign(Value::bool(
icmp(inst.cond_code().unwrap(), &arg(0)?, &imm_as_ctrl_ty()?)?,
ctrl_ty.as_bool(),
Opcode::IcmpImm => assign(icmp(
ctrl_ty,
inst.cond_code().unwrap(),
&arg(0)?,
&imm_as_ctrl_ty()?,
)?),
Opcode::Ifcmp | Opcode::IfcmpImm => {
let arg0 = arg(0)?;
@@ -489,7 +495,7 @@ where
IntCC::UnsignedGreaterThan,
IntCC::UnsignedLessThanOrEqual,
] {
if icmp(*f, &arg0, &arg1)? {
if icmp(ctrl_ty, *f, &arg0, &arg1)?.into_bool()? {
state.set_iflag(*f);
}
}
@@ -1010,35 +1016,57 @@ pub enum CraneliftTrap {
}
/// Compare two values using the given integer condition `code`.
fn icmp<V>(code: IntCC, left: &V, right: &V) -> ValueResult<bool>
fn icmp<V>(ctrl_ty: types::Type, code: IntCC, left: &V, right: &V) -> ValueResult<V>
where
V: Value,
{
Ok(match code {
IntCC::Equal => Value::eq(left, right)?,
IntCC::NotEqual => !Value::eq(left, right)?,
IntCC::SignedGreaterThan => Value::gt(left, right)?,
IntCC::SignedGreaterThanOrEqual => Value::ge(left, right)?,
IntCC::SignedLessThan => Value::lt(left, right)?,
IntCC::SignedLessThanOrEqual => Value::le(left, right)?,
IntCC::UnsignedGreaterThan => Value::gt(
&left.clone().convert(ValueConversionKind::ToUnsigned)?,
&right.clone().convert(ValueConversionKind::ToUnsigned)?,
)?,
IntCC::UnsignedGreaterThanOrEqual => Value::ge(
&left.clone().convert(ValueConversionKind::ToUnsigned)?,
&right.clone().convert(ValueConversionKind::ToUnsigned)?,
)?,
IntCC::UnsignedLessThan => Value::lt(
&left.clone().convert(ValueConversionKind::ToUnsigned)?,
&right.clone().convert(ValueConversionKind::ToUnsigned)?,
)?,
IntCC::UnsignedLessThanOrEqual => Value::le(
&left.clone().convert(ValueConversionKind::ToUnsigned)?,
&right.clone().convert(ValueConversionKind::ToUnsigned)?,
)?,
IntCC::Overflow => Value::overflow(left, right)?,
IntCC::NotOverflow => !Value::overflow(left, right)?,
let cmp = |bool_ty: types::Type, code: IntCC, left: &V, right: &V| -> ValueResult<V> {
Ok(Value::bool(
match code {
IntCC::Equal => Value::eq(left, right)?,
IntCC::NotEqual => !Value::eq(left, right)?,
IntCC::SignedGreaterThan => Value::gt(left, right)?,
IntCC::SignedGreaterThanOrEqual => Value::ge(left, right)?,
IntCC::SignedLessThan => Value::lt(left, right)?,
IntCC::SignedLessThanOrEqual => Value::le(left, right)?,
IntCC::UnsignedGreaterThan => Value::gt(
&left.clone().convert(ValueConversionKind::ToUnsigned)?,
&right.clone().convert(ValueConversionKind::ToUnsigned)?,
)?,
IntCC::UnsignedGreaterThanOrEqual => Value::ge(
&left.clone().convert(ValueConversionKind::ToUnsigned)?,
&right.clone().convert(ValueConversionKind::ToUnsigned)?,
)?,
IntCC::UnsignedLessThan => Value::lt(
&left.clone().convert(ValueConversionKind::ToUnsigned)?,
&right.clone().convert(ValueConversionKind::ToUnsigned)?,
)?,
IntCC::UnsignedLessThanOrEqual => Value::le(
&left.clone().convert(ValueConversionKind::ToUnsigned)?,
&right.clone().convert(ValueConversionKind::ToUnsigned)?,
)?,
IntCC::Overflow => Value::overflow(left, right)?,
IntCC::NotOverflow => !Value::overflow(left, right)?,
},
bool_ty,
)?)
};
let dst_ty = ctrl_ty.as_bool();
Ok(if ctrl_ty.is_vector() {
let lane_type = ctrl_ty.lane_type();
let left = extractlanes(left, lane_type)?;
let right = extractlanes(right, lane_type)?;
let res = left
.into_iter()
.zip(right.into_iter())
.map(|(l, r)| cmp(dst_ty.lane_type(), code, &l, &r))
.collect::<ValueResult<SimdVec<V>>>()?;
vectorizelanes(&res, dst_ty)?
} else {
cmp(dst_ty, code, left, right)?
})
}
@@ -1114,18 +1142,23 @@ fn vectorizelanes<V>(x: &[V], vector_type: types::Type) -> ValueResult<V>
where
V: Value,
{
let iterations = match vector_type.lane_type() {
types::I8 => 1,
types::I16 => 2,
types::I32 => 4,
types::I64 => 8,
let lane_type = vector_type.lane_type();
let iterations = match lane_type {
types::I8 | types::B1 | types::B8 => 1,
types::I16 | types::B16 => 2,
types::I32 | types::B32 => 4,
types::I64 | types::B64 => 8,
_ => unimplemented!("Only 128-bit vectors are currently supported."),
};
let mut result: [u8; 16] = [0; 16];
for (i, val) in x.iter().enumerate() {
let val = val.clone().into_int()?;
let lane_val: i128 = val
.clone()
.convert(ValueConversionKind::Exact(lane_type.as_int()))?
.into_int()?;
for j in 0..iterations {
result[(i * iterations) + j] = (val >> (8 * j)) as u8;
result[(i * iterations) + j] = (lane_val >> (8 * j)) as u8;
}
}
Value::vector(result, vector_type)

View File

@@ -271,7 +271,18 @@ impl Value for DataValue {
// TODO a lot to do here: from bmask to ireduce to raw_bitcast...
(DataValue::I64(n), types::I32) => DataValue::I32(i32::try_from(n)?),
(DataValue::I64(n), types::I64) => DataValue::I64(n),
(DataValue::I64(n), types::I128) => DataValue::I128(n as i128),
(DataValue::B(b), t) if t.is_bool() => DataValue::B(b),
(DataValue::B(b), t) if t.is_int() => {
let val = if b {
// Bools are represented in memory as all 1's
(1i128 << t.bits()) - 1
} else {
0
};
DataValue::int(val, t)?
}
(dv, t) if t.is_int() && dv.ty() == t => dv,
(dv, _) => unimplemented!("conversion: {} -> {:?}", dv.ty(), kind),
},
ValueConversionKind::Truncate(ty) => {
@@ -333,6 +344,7 @@ impl Value for DataValue {
DataValue::I16(n) => DataValue::U16(n as u16),
DataValue::I32(n) => DataValue::U32(n as u32),
DataValue::I64(n) => DataValue::U64(n as u64),
DataValue::I128(n) => DataValue::U128(n as u128),
_ => unimplemented!("conversion: {} -> {:?}", self.ty(), kind),
},
ValueConversionKind::ToSigned => match self {
@@ -340,6 +352,7 @@ impl Value for DataValue {
DataValue::U16(n) => DataValue::I16(n as i16),
DataValue::U32(n) => DataValue::I32(n as i32),
DataValue::U64(n) => DataValue::I64(n as i64),
DataValue::U128(n) => DataValue::I128(n as i128),
_ => unimplemented!("conversion: {} -> {:?}", self.ty(), kind),
},
ValueConversionKind::RoundNearestEven(ty) => match (self.ty(), ty) {
@@ -364,11 +377,11 @@ impl Value for DataValue {
}
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, I128, U8, U16, U32, U64, U128, F32, F64])
}
fn gt(&self, other: &Self) -> ValueResult<bool> {
comparison_match!(PartialOrd::gt[&self, &other]; [I8, I16, I32, I64, U8, U16, U32, U64, F32, F64])
comparison_match!(PartialOrd::gt[&self, &other]; [I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, F32, F64])
}
fn uno(&self, other: &Self) -> ValueResult<bool> {
@@ -381,6 +394,7 @@ impl Value for DataValue {
(DataValue::I16(a), DataValue::I16(b)) => a.checked_sub(*b).is_none(),
(DataValue::I32(a), DataValue::I32(b)) => a.checked_sub(*b).is_none(),
(DataValue::I64(a), DataValue::I64(b)) => a.checked_sub(*b).is_none(),
(DataValue::I128(a), DataValue::I128(b)) => a.checked_sub(*b).is_none(),
_ => unimplemented!(),
})
}