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:
Trevor Elliott
2022-10-17 16:00:27 -07:00
committed by GitHub
parent 766ecb561e
commit 32a7593c94
242 changed files with 7695 additions and 10010 deletions

View File

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

View File

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

View File

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

View File

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