cranelift: Remove booleans (#5031)
Remove the boolean types from cranelift, and the associated instructions breduce, bextend, bconst, and bint. Standardize on using 1/0 for the return value from instructions that produce scalar boolean results, and -1/0 for boolean vector elements. Fixes #3205 Co-authored-by: Afonso Bordado <afonso360@users.noreply.github.com> Co-authored-by: Ulrich Weigand <ulrich.weigand@de.ibm.com> Co-authored-by: Chris Fallin <chris@cfallin.org>
This commit is contained in:
@@ -194,7 +194,7 @@ mod tests {
|
||||
ValueRef::from_u32(6),
|
||||
];
|
||||
let values = vec![
|
||||
DataValue::B(true),
|
||||
DataValue::I8(1),
|
||||
DataValue::I8(42),
|
||||
DataValue::F32(Ieee32::from(0.42)),
|
||||
];
|
||||
@@ -214,7 +214,7 @@ mod tests {
|
||||
let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");
|
||||
let mut frame = Frame::new(&func);
|
||||
let old_ssa_value_refs = [ValueRef::from_u32(9), ValueRef::from_u32(10)];
|
||||
let values = vec![DataValue::B(true), DataValue::F64(Ieee64::from(0.0))];
|
||||
let values = vec![DataValue::I8(1), DataValue::F64(Ieee64::from(0.0))];
|
||||
frame.set_all(&old_ssa_value_refs, values.clone());
|
||||
|
||||
// Rename the old SSA values to the new values.
|
||||
@@ -232,7 +232,7 @@ mod tests {
|
||||
let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");
|
||||
let mut frame = Frame::new(&func);
|
||||
let old_ssa_value_refs = [ValueRef::from_u32(1), ValueRef::from_u32(9)];
|
||||
let values = vec![DataValue::B(true), DataValue::F64(Ieee64::from(f64::NAN))];
|
||||
let values = vec![DataValue::I8(1), DataValue::F64(Ieee64::from(f64::NAN))];
|
||||
frame.set_all(&old_ssa_value_refs, values.clone());
|
||||
|
||||
// Rename the old SSA values to the new values.
|
||||
|
||||
@@ -510,7 +510,7 @@ impl<'a> State<'a, DataValue> for InterpreterState<'a> {
|
||||
|
||||
// We start with a sentinel value that will fail if we try to load / add to it
|
||||
// without resolving the base GV First.
|
||||
let mut current_val = DataValue::B(false);
|
||||
let mut current_val = DataValue::I8(0);
|
||||
let mut action_stack = vec![ResolveAction::Resolve(gv)];
|
||||
|
||||
loop {
|
||||
@@ -639,7 +639,7 @@ mod tests {
|
||||
// filetest infrastructure.
|
||||
#[test]
|
||||
fn sanity() {
|
||||
let code = "function %test() -> b1 {
|
||||
let code = "function %test() -> i8 {
|
||||
block0:
|
||||
v0 = iconst.i32 1
|
||||
v1 = iadd_imm v0, 1
|
||||
@@ -657,7 +657,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.unwrap_return();
|
||||
|
||||
assert_eq!(result, vec![DataValue::B(true)])
|
||||
assert_eq!(result, vec![DataValue::I8(1)])
|
||||
}
|
||||
|
||||
// We don't have a way to check for traps with the current filetest infrastructure
|
||||
@@ -750,7 +750,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn fuel() {
|
||||
let code = "function %test() -> b1 {
|
||||
let code = "function %test() -> i8 {
|
||||
block0:
|
||||
v0 = iconst.i32 1
|
||||
v1 = iadd_imm v0, 1
|
||||
@@ -1000,7 +1000,7 @@ mod tests {
|
||||
#[test]
|
||||
fn heap_sanity_test() {
|
||||
let code = "
|
||||
function %heap_load_store(i64 vmctx) -> b1 {
|
||||
function %heap_load_store(i64 vmctx) -> i8 {
|
||||
gv0 = vmctx
|
||||
gv1 = load.i64 notrap aligned gv0+0
|
||||
; gv2/3 do nothing, but makes sure we understand the iadd_imm mechanism
|
||||
@@ -1039,7 +1039,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.unwrap_return();
|
||||
|
||||
assert_eq!(result, vec![DataValue::B(true)])
|
||||
assert_eq!(result, vec![DataValue::I8(1)])
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -29,7 +29,6 @@ fn validate_signature_params(sig: &[AbiParam], args: &[impl Value]) -> bool {
|
||||
// but we don't have enough information there either.
|
||||
//
|
||||
// Ideally the user has run the verifier and caught this properly...
|
||||
(a, b) if a.is_bool() && b.is_bool() => true,
|
||||
(a, b) if a.is_vector() && b.is_vector() => true,
|
||||
(a, b) => a == b,
|
||||
})
|
||||
@@ -108,13 +107,12 @@ where
|
||||
.get(imm)
|
||||
.unwrap()
|
||||
.as_slice();
|
||||
match ctrl_ty.bytes() {
|
||||
match mask.len() {
|
||||
16 => DataValue::V128(mask.try_into().expect("a 16-byte vector mask")),
|
||||
8 => DataValue::V64(mask.try_into().expect("an 8-byte vector mask")),
|
||||
length => panic!("unexpected Shuffle mask length {}", length),
|
||||
length => panic!("unexpected Shuffle mask length {}", mask.len()),
|
||||
}
|
||||
}
|
||||
InstructionData::UnaryBool { imm, .. } => DataValue::from(imm),
|
||||
// 8-bit.
|
||||
InstructionData::BinaryImm8 { imm, .. } | InstructionData::TernaryImm8 { imm, .. } => {
|
||||
DataValue::from(imm as i8) // Note the switch from unsigned to signed.
|
||||
@@ -552,7 +550,6 @@ where
|
||||
Opcode::Iconst => assign(Value::int(imm().into_int()?, ctrl_ty)?),
|
||||
Opcode::F32const => assign(imm()),
|
||||
Opcode::F64const => assign(imm()),
|
||||
Opcode::Bconst => assign(imm()),
|
||||
Opcode::Vconst => assign(imm()),
|
||||
Opcode::Null => unimplemented!("Null"),
|
||||
Opcode::Nop => ControlFlow::Continue,
|
||||
@@ -754,7 +751,7 @@ where
|
||||
Opcode::IaddCout => {
|
||||
let sum = Value::add(arg(0)?, arg(1)?)?;
|
||||
let carry = Value::lt(&sum, &arg(0)?)? && Value::lt(&sum, &arg(1)?)?;
|
||||
assign_multiple(&[sum, Value::bool(carry, types::B1)?])
|
||||
assign_multiple(&[sum, Value::bool(carry, false, types::I8)?])
|
||||
}
|
||||
Opcode::IaddIfcout => unimplemented!("IaddIfcout"),
|
||||
Opcode::IaddCarry => {
|
||||
@@ -763,7 +760,7 @@ where
|
||||
sum = Value::add(sum, Value::int(1, ctrl_ty)?)?
|
||||
}
|
||||
let carry = Value::lt(&sum, &arg(0)?)? && Value::lt(&sum, &arg(1)?)?;
|
||||
assign_multiple(&[sum, Value::bool(carry, types::B1)?])
|
||||
assign_multiple(&[sum, Value::bool(carry, false, types::I8)?])
|
||||
}
|
||||
Opcode::IaddIfcarry => unimplemented!("IaddIfcarry"),
|
||||
Opcode::IsubBin => choose(
|
||||
@@ -775,7 +772,7 @@ where
|
||||
Opcode::IsubBout => {
|
||||
let sum = Value::sub(arg(0)?, arg(1)?)?;
|
||||
let borrow = Value::lt(&arg(0)?, &arg(1)?)?;
|
||||
assign_multiple(&[sum, Value::bool(borrow, types::B1)?])
|
||||
assign_multiple(&[sum, Value::bool(borrow, false, types::I8)?])
|
||||
}
|
||||
Opcode::IsubIfbout => unimplemented!("IsubIfbout"),
|
||||
Opcode::IsubBorrow => {
|
||||
@@ -786,7 +783,7 @@ where
|
||||
};
|
||||
let borrow = Value::lt(&arg(0)?, &rhs)?;
|
||||
let sum = Value::sub(arg(0)?, rhs)?;
|
||||
assign_multiple(&[sum, Value::bool(borrow, types::B1)?])
|
||||
assign_multiple(&[sum, Value::bool(borrow, false, types::I8)?])
|
||||
}
|
||||
Opcode::IsubIfborrow => unimplemented!("IsubIfborrow"),
|
||||
Opcode::Band => binary(Value::and, arg(0)?, arg(1)?)?,
|
||||
@@ -844,6 +841,7 @@ where
|
||||
.map(|(x, y)| {
|
||||
V::bool(
|
||||
fcmp(inst.fp_cond_code().unwrap(), &x, &y).unwrap(),
|
||||
ctrl_ty.is_vector(),
|
||||
ctrl_ty.lane_type().as_bool(),
|
||||
)
|
||||
})
|
||||
@@ -946,19 +944,15 @@ where
|
||||
// return a 1-bit boolean value.
|
||||
Opcode::Trueif => choose(
|
||||
state.has_iflag(inst.cond_code().unwrap()),
|
||||
Value::bool(true, types::B1)?,
|
||||
Value::bool(false, types::B1)?,
|
||||
Value::bool(true, false, types::I8)?,
|
||||
Value::bool(false, false, types::I8)?,
|
||||
),
|
||||
Opcode::Trueff => choose(
|
||||
state.has_fflag(inst.fp_cond_code().unwrap()),
|
||||
Value::bool(true, types::B1)?,
|
||||
Value::bool(false, types::B1)?,
|
||||
Value::bool(true, false, types::I8)?,
|
||||
Value::bool(false, false, types::I8)?,
|
||||
),
|
||||
Opcode::Bitcast
|
||||
| Opcode::RawBitcast
|
||||
| Opcode::ScalarToVector
|
||||
| Opcode::Breduce
|
||||
| Opcode::Bextend => {
|
||||
Opcode::Bitcast | Opcode::RawBitcast | Opcode::ScalarToVector => {
|
||||
let input_ty = inst_context.type_of(inst_context.args()[0]).unwrap();
|
||||
let arg0 = extractlanes(&arg(0)?, input_ty)?;
|
||||
|
||||
@@ -974,11 +968,6 @@ where
|
||||
arg(0)?,
|
||||
ValueConversionKind::Truncate(ctrl_ty),
|
||||
)?),
|
||||
Opcode::Bint => {
|
||||
let bool = arg(0)?.into_bool()?;
|
||||
let int = if bool { 1 } else { 0 };
|
||||
assign(Value::int(int, ctrl_ty)?)
|
||||
}
|
||||
Opcode::Snarrow | Opcode::Unarrow | Opcode::Uunarrow => {
|
||||
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?;
|
||||
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?;
|
||||
@@ -1014,7 +1003,7 @@ where
|
||||
let bool_ty = ctrl_ty.as_bool_pedantic();
|
||||
let lanes = extractlanes(&bool, bool_ty)?
|
||||
.into_iter()
|
||||
.map(|lane| lane.convert(ValueConversionKind::Exact(ctrl_ty.lane_type())))
|
||||
.map(|lane| lane.convert(ValueConversionKind::Mask(ctrl_ty.lane_type())))
|
||||
.collect::<ValueResult<SimdVec<V>>>()?;
|
||||
vectorizelanes(&lanes, ctrl_ty)?
|
||||
}),
|
||||
@@ -1046,7 +1035,7 @@ where
|
||||
new[i] = b[mask[i] as usize - a.len()];
|
||||
} // else leave as 0.
|
||||
}
|
||||
assign(Value::vector(new, ctrl_ty)?)
|
||||
assign(Value::vector(new, types::I8X16)?)
|
||||
}
|
||||
Opcode::Swizzle => {
|
||||
let x = Value::into_array(&arg(0)?)?;
|
||||
@@ -1092,18 +1081,18 @@ where
|
||||
Opcode::Vsplit => unimplemented!("Vsplit"),
|
||||
Opcode::Vconcat => unimplemented!("Vconcat"),
|
||||
Opcode::Vselect => assign(vselect(&arg(0)?, &arg(1)?, &arg(2)?, ctrl_ty)?),
|
||||
Opcode::VanyTrue => assign(fold_vector(
|
||||
arg(0)?,
|
||||
ctrl_ty,
|
||||
V::bool(false, types::B1)?,
|
||||
|acc, lane| acc.or(lane),
|
||||
)?),
|
||||
Opcode::VallTrue => assign(fold_vector(
|
||||
arg(0)?,
|
||||
ctrl_ty,
|
||||
V::bool(true, types::B1)?,
|
||||
|acc, lane| acc.and(lane),
|
||||
)?),
|
||||
Opcode::VanyTrue => {
|
||||
let lane_ty = ctrl_ty.lane_type();
|
||||
let init = V::bool(false, true, lane_ty)?;
|
||||
let any = fold_vector(arg(0)?, ctrl_ty, init.clone(), |acc, lane| acc.or(lane))?;
|
||||
assign(V::bool(!V::eq(&any, &init)?, false, types::I8)?)
|
||||
}
|
||||
Opcode::VallTrue => {
|
||||
let lane_ty = ctrl_ty.lane_type();
|
||||
let init = V::bool(true, true, lane_ty)?;
|
||||
let all = fold_vector(arg(0)?, ctrl_ty, init.clone(), |acc, lane| acc.and(lane))?;
|
||||
assign(V::bool(V::eq(&all, &init)?, false, types::I8)?)
|
||||
}
|
||||
Opcode::SwidenLow | Opcode::SwidenHigh | Opcode::UwidenLow | Opcode::UwidenHigh => {
|
||||
let new_type = ctrl_ty.merge_lanes().unwrap();
|
||||
let conv_type = match inst.opcode() {
|
||||
@@ -1426,6 +1415,7 @@ where
|
||||
&right.clone().convert(ValueConversionKind::ToUnsigned)?,
|
||||
)?,
|
||||
},
|
||||
ctrl_ty.is_vector(),
|
||||
bool_ty,
|
||||
)?)
|
||||
};
|
||||
@@ -1489,10 +1479,10 @@ where
|
||||
}
|
||||
|
||||
let iterations = match lane_type {
|
||||
types::I8 | types::B1 | types::B8 => 1,
|
||||
types::I16 | types::B16 => 2,
|
||||
types::I32 | types::B32 | types::F32 => 4,
|
||||
types::I64 | types::B64 | types::F64 => 8,
|
||||
types::I8 => 1,
|
||||
types::I16 => 2,
|
||||
types::I32 | types::F32 => 4,
|
||||
types::I64 | types::F64 => 8,
|
||||
_ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),
|
||||
};
|
||||
|
||||
@@ -1503,9 +1493,7 @@ where
|
||||
lane += (x[((i * iterations) + j) as usize] as i128) << (8 * j);
|
||||
}
|
||||
|
||||
let lane_val: V = if lane_type.is_bool() {
|
||||
Value::bool(lane != 0, lane_type)?
|
||||
} else if lane_type.is_float() {
|
||||
let lane_val: V = if lane_type.is_float() {
|
||||
Value::float(lane as u64, lane_type)?
|
||||
} else {
|
||||
Value::int(lane, lane_type)?
|
||||
@@ -1528,10 +1516,10 @@ where
|
||||
|
||||
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 | types::F32 => 4,
|
||||
types::I64 | types::B64 | types::F64 => 8,
|
||||
types::I8 => 1,
|
||||
types::I16 => 2,
|
||||
types::I32 | types::F32 => 4,
|
||||
types::I64 | types::F64 => 8,
|
||||
_ => unimplemented!("vectors with lanes wider than 64-bits are currently unsupported."),
|
||||
};
|
||||
let mut result: [u8; 16] = [0; 16];
|
||||
|
||||
@@ -20,7 +20,7 @@ pub trait Value: Clone + From<DataValue> {
|
||||
fn into_float(self) -> ValueResult<f64>;
|
||||
fn is_float(&self) -> bool;
|
||||
fn is_nan(&self) -> ValueResult<bool>;
|
||||
fn bool(b: bool, ty: Type) -> ValueResult<Self>;
|
||||
fn bool(b: bool, vec_elem: bool, ty: Type) -> ValueResult<Self>;
|
||||
fn into_bool(self) -> ValueResult<bool>;
|
||||
fn vector(v: [u8; 16], ty: Type) -> ValueResult<Self>;
|
||||
fn into_array(&self) -> ValueResult<[u8; 16]>;
|
||||
@@ -152,6 +152,8 @@ pub enum ValueConversionKind {
|
||||
/// Converts an integer into a boolean, zero integers are converted into a
|
||||
/// `false`, while other integers are converted into `true`. Booleans are passed through.
|
||||
ToBoolean,
|
||||
/// Converts an integer into either -1 or zero.
|
||||
Mask(Type),
|
||||
}
|
||||
|
||||
/// Helper for creating match expressions over [DataValue].
|
||||
@@ -268,14 +270,39 @@ impl Value for DataValue {
|
||||
}
|
||||
}
|
||||
|
||||
fn bool(b: bool, ty: Type) -> ValueResult<Self> {
|
||||
assert!(ty.is_bool());
|
||||
Ok(DataValue::B(b))
|
||||
fn bool(b: bool, vec_elem: bool, ty: Type) -> ValueResult<Self> {
|
||||
assert!(ty.is_int());
|
||||
macro_rules! make_bool {
|
||||
($ty:ident) => {
|
||||
Ok(DataValue::$ty(if b {
|
||||
if vec_elem {
|
||||
-1
|
||||
} else {
|
||||
1
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
match ty {
|
||||
types::I8 => make_bool!(I8),
|
||||
types::I16 => make_bool!(I16),
|
||||
types::I32 => make_bool!(I32),
|
||||
types::I64 => make_bool!(I64),
|
||||
types::I128 => make_bool!(I128),
|
||||
_ => Err(ValueError::InvalidType(ValueTypeClass::Integer, ty)),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_bool(self) -> ValueResult<bool> {
|
||||
match self {
|
||||
DataValue::B(b) => Ok(b),
|
||||
DataValue::I8(b) => Ok(b != 0),
|
||||
DataValue::I16(b) => Ok(b != 0),
|
||||
DataValue::I32(b) => Ok(b != 0),
|
||||
DataValue::I64(b) => Ok(b != 0),
|
||||
DataValue::I128(b) => Ok(b != 0),
|
||||
_ => Err(ValueError::InvalidType(ValueTypeClass::Boolean, self.ty())),
|
||||
}
|
||||
}
|
||||
@@ -316,16 +343,6 @@ impl Value for DataValue {
|
||||
(DataValue::F32(n), types::I32) => DataValue::I32(n.bits() as i32),
|
||||
(DataValue::F64(n), types::I64) => DataValue::I64(n.bits() as i64),
|
||||
(DataValue::F32(n), types::F64) => DataValue::F64((n.as_f32() as f64).into()),
|
||||
(DataValue::B(b), t) if t.is_bool() => DataValue::B(b),
|
||||
(DataValue::B(b), t) if t.is_int() => {
|
||||
// Bools are represented in memory as all 1's
|
||||
let val = match (b, t) {
|
||||
(true, types::I128) => -1,
|
||||
(true, t) => (1i128 << t.bits()) - 1,
|
||||
_ => 0,
|
||||
};
|
||||
DataValue::int(val, t)?
|
||||
}
|
||||
(dv, t) if (t.is_int() || t.is_float()) && dv.ty() == t => dv,
|
||||
(dv, _) => unimplemented!("conversion: {} -> {:?}", dv.ty(), kind),
|
||||
},
|
||||
@@ -432,10 +449,13 @@ impl Value for DataValue {
|
||||
(s, _) => unimplemented!("conversion: {} -> {:?}", s.ty(), kind),
|
||||
},
|
||||
ValueConversionKind::ToBoolean => match self.ty() {
|
||||
ty if ty.is_bool() => DataValue::B(self.into_bool()?),
|
||||
ty if ty.is_int() => DataValue::B(self.into_int()? != 0),
|
||||
ty if ty.is_int() => DataValue::I8(if self.into_int()? != 0 { 1 } else { 0 }),
|
||||
ty => unimplemented!("conversion: {} -> {:?}", ty, kind),
|
||||
},
|
||||
ValueConversionKind::Mask(ty) => {
|
||||
let b = self.into_bool()?;
|
||||
Self::bool(b, true, ty).unwrap()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -662,11 +682,11 @@ impl Value for DataValue {
|
||||
}
|
||||
|
||||
fn and(self, other: Self) -> ValueResult<Self> {
|
||||
binary_match!(&(self, other); [B, I8, I16, I32, I64, I128, F32, F64])
|
||||
binary_match!(&(self, other); [I8, I16, I32, I64, I128, F32, F64])
|
||||
}
|
||||
|
||||
fn or(self, other: Self) -> ValueResult<Self> {
|
||||
binary_match!(|(self, other); [B, I8, I16, I32, I64, I128, F32, F64])
|
||||
binary_match!(|(self, other); [I8, I16, I32, I64, I128, F32, F64])
|
||||
}
|
||||
|
||||
fn xor(self, other: Self) -> ValueResult<Self> {
|
||||
@@ -674,7 +694,7 @@ impl Value for DataValue {
|
||||
}
|
||||
|
||||
fn not(self) -> ValueResult<Self> {
|
||||
unary_match!(!(self); [B, I8, I16, I32, I64, I128, F32, F64])
|
||||
unary_match!(!(self); [I8, I16, I32, I64, I128, F32, F64])
|
||||
}
|
||||
|
||||
fn count_ones(self) -> ValueResult<Self> {
|
||||
|
||||
Reference in New Issue
Block a user