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

@@ -376,12 +376,6 @@ impl<'a> Lexer<'a> {
"i128" => types::I128,
"f32" => types::F32,
"f64" => types::F64,
"b1" => types::B1,
"b8" => types::B8,
"b16" => types::B16,
"b32" => types::B32,
"b64" => types::B64,
"b128" => types::B128,
"r32" => types::R32,
"r64" => types::R64,
_ => return None,
@@ -628,7 +622,7 @@ mod tests {
fn lex_identifiers() {
let mut lex = Lexer::new(
"v0 v00 vx01 block1234567890 block5234567890 v1x vx1 vxvx4 \
function0 function b1 i32x4 f32x5 \
function0 function i8 i32x4 f32x5 \
iflags fflags iflagss",
);
assert_eq!(
@@ -647,7 +641,7 @@ mod tests {
assert_eq!(lex.next(), token(Token::Identifier("vxvx4"), 1));
assert_eq!(lex.next(), token(Token::Identifier("function0"), 1));
assert_eq!(lex.next(), token(Token::Identifier("function"), 1));
assert_eq!(lex.next(), token(Token::Type(types::B1), 1));
assert_eq!(lex.next(), token(Token::Type(types::I8), 1));
assert_eq!(lex.next(), token(Token::Type(types::I32X4), 1));
assert_eq!(lex.next(), token(Token::Identifier("f32x5"), 1));
assert_eq!(lex.next(), token(Token::Type(types::IFLAGS), 1));

View File

@@ -975,20 +975,6 @@ impl<'a> Parser<'a> {
}
}
// Match and consume a boolean immediate.
fn match_bool(&mut self, err_msg: &str) -> ParseResult<bool> {
if let Some(Token::Identifier(text)) = self.token() {
self.consume();
match text {
"true" => Ok(true),
"false" => Ok(false),
_ => err!(self.loc, err_msg),
}
} else {
err!(self.loc, err_msg)
}
}
// Match and consume an enumerated immediate, like one of the condition codes.
fn match_enum<T: FromStr>(&mut self, err_msg: &str) -> ParseResult<T> {
if let Some(Token::Identifier(text)) = self.token() {
@@ -1053,15 +1039,6 @@ impl<'a> Parser<'a> {
}};
}
fn boolean_to_vec(value: bool, ty: Type) -> Vec<u8> {
let lane_size = ty.bytes() / u32::from(ty.lane_count());
if lane_size < 1 {
panic!("The boolean lane must have a byte size greater than zero.");
}
let value = if value { 0xFF } else { 0 };
vec![value; lane_size as usize]
}
if !ty.is_vector() && !ty.is_dynamic_vector() {
err!(self.loc, "Expected a controlling vector type, not {}", ty)
} else {
@@ -1072,10 +1049,6 @@ impl<'a> Parser<'a> {
I64 => consume!(ty, self.match_imm64("Expected a 64-bit integer")?),
F32 => consume!(ty, self.match_ieee32("Expected a 32-bit float")?),
F64 => consume!(ty, self.match_ieee64("Expected a 64-bit float")?),
b if b.is_bool() => consume!(
ty,
boolean_to_vec(self.match_bool("Expected a boolean")?, ty)
),
_ => return err!(self.loc, "Expected a type of: float, int, bool"),
};
Ok(constant_data)
@@ -2565,14 +2538,14 @@ impl<'a> Parser<'a> {
Ok(RunCommand::Run(invocation, comparison, expected))
} else if sig.params.is_empty()
&& sig.returns.len() == 1
&& sig.returns[0].value_type.is_bool()
&& sig.returns[0].value_type.is_int()
{
// To match the existing run behavior that does not require an explicit
// invocation, we create an invocation from a function like `() -> b*` and
// compare it to `true`.
// invocation, we create an invocation from a function like `() -> i*` and
// require the result to be non-zero.
let invocation = Invocation::new("default", vec![]);
let expected = vec![DataValue::B(true)];
let comparison = Comparison::Equals;
let expected = vec![DataValue::I8(0)];
let comparison = Comparison::NotEquals;
Ok(RunCommand::Run(invocation, comparison, expected))
} else {
Err(self.error("unable to parse the run command"))
@@ -2713,9 +2686,6 @@ impl<'a> Parser<'a> {
return Err(self.error("only 128-bit vectors are currently supported"));
}
}
_ if ty.is_bool() && !ty.is_vector() => {
DataValue::from(self.match_bool("expected a boolean")?)
}
_ => return Err(self.error(&format!("don't know how to parse data values of: {}", ty))),
};
Ok(dv)
@@ -2746,10 +2716,6 @@ impl<'a> Parser<'a> {
opcode,
imm: self.match_ieee64("expected immediate 64-bit float operand")?,
},
InstructionFormat::UnaryBool => InstructionData::UnaryBool {
opcode,
imm: self.match_bool("expected immediate boolean operand")?,
},
InstructionFormat::UnaryConst => {
let constant_handle = if let Some(Token::Constant(_)) = self.token() {
// If handed a `const?`, use that.
@@ -3807,10 +3773,10 @@ mod tests {
can_parse_as_constant_data!("1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16", I8X16);
can_parse_as_constant_data!("0x1.1 0x2.2 0x3.3 0x4.4", F32X4);
can_parse_as_constant_data!("0x0 0x1 0x2 0x3", I32X4);
can_parse_as_constant_data!("true false true false true false true false", B16X8);
can_parse_as_constant_data!("-1 0 -1 0 -1 0 -1 0", I16X8);
can_parse_as_constant_data!("0 -1", I64X2);
can_parse_as_constant_data!("true false", B64X2);
can_parse_as_constant_data!("true true true true true", B32X4); // note that parse_literals_to_constant_data will leave extra tokens unconsumed
can_parse_as_constant_data!("-1 0", I64X2);
can_parse_as_constant_data!("-1 -1 -1 -1 -1", I32X4); // note that parse_literals_to_constant_data will leave extra tokens unconsumed
cannot_parse_as_constant_data!("1 2 3", I32X4);
cannot_parse_as_constant_data!(" ", F32X4);
@@ -3818,8 +3784,8 @@ mod tests {
#[test]
fn parse_constant_from_booleans() {
let c = Parser::new("true false true false")
.parse_literals_to_constant_data(B32X4)
let c = Parser::new("-1 0 -1 0")
.parse_literals_to_constant_data(I32X4)
.unwrap();
assert_eq!(
c.into_vec(),
@@ -3864,18 +3830,18 @@ mod tests {
}
assert_roundtrip("run: %fn0() == 42", &sig(&[], &[I32]));
assert_roundtrip(
"run: %fn0(8, 16, 32, 64) == true",
&sig(&[I8, I16, I32, I64], &[B8]),
"run: %fn0(8, 16, 32, 64) == 1",
&sig(&[I8, I16, I32, I64], &[I8]),
);
assert_roundtrip(
"run: %my_func(true) == 0x0f0e0d0c0b0a09080706050403020100",
&sig(&[B32], &[I8X16]),
"run: %my_func(1) == 0x0f0e0d0c0b0a09080706050403020100",
&sig(&[I32], &[I8X16]),
);
// Verify that default invocations are created when not specified.
assert_eq!(
parse("run", &sig(&[], &[B32])).unwrap().to_string(),
"run: %default() == true"
parse("run", &sig(&[], &[I32])).unwrap().to_string(),
"run: %default() != 0"
);
assert_eq!(
parse("print", &sig(&[], &[F32X4, I16X8]))
@@ -3885,8 +3851,7 @@ mod tests {
);
// Demonstrate some unparseable cases.
assert!(parse("print", &sig(&[I32], &[B32])).is_err());
assert!(parse("run", &sig(&[], &[I32])).is_err());
assert!(parse("print", &sig(&[I32], &[I32])).is_err());
assert!(parse("print:", &sig(&[], &[])).is_err());
assert!(parse("run: ", &sig(&[], &[])).is_err());
}
@@ -3947,8 +3912,6 @@ mod tests {
assert_eq!(parse("1234567", I128).to_string(), "1234567");
assert_eq!(parse("0x32.32", F32).to_string(), "0x1.919000p5");
assert_eq!(parse("0x64.64", F64).to_string(), "0x1.9190000000000p6");
assert_eq!(parse("true", B1).to_string(), "true");
assert_eq!(parse("false", B64).to_string(), "false");
assert_eq!(
parse("[0 1 2 3]", I32X4).to_string(),
"0x00000003000000020000000100000000"