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

@@ -1659,11 +1659,6 @@
(result Reg (alu_rrr (AluOPRRR.Or) tmp_x tmp_y)))
result))
(decl gen_bint (Reg) Reg)
(rule
(gen_bint r)
(alu_rr_imm12 (AluOPRRI.Andi) r (imm12_const 1)))
(decl gen_int_select (Type IntSelectOP ValueRegs ValueRegs) ValueRegs)
(rule
(gen_int_select ty op x y)
@@ -1729,12 +1724,6 @@
(_ Unit (emit (MInst.FcvtToInt is_sat result tmp rs is_signed in_type out_type))))
result))
;;;; in_type out_type
;;;; out_type is returned.
(decl pure valid_bextend_ty (Type Type) Type)
(extern constructor valid_bextend_ty valid_bextend_ty)
;;; some float binary operation
;;; 1. need move into x reister.
;;; 2. do the operation.
@@ -1907,14 +1896,29 @@
(decl lower_brz_or_nz (IntCC ValueRegs VecMachLabel Type) InstOutput)
(extern constructor lower_brz_or_nz lower_brz_or_nz)
;; Normalize a value by masking to its bit-size.
(decl normalize_value (Type ValueRegs) ValueRegs)
(rule (normalize_value $I8 r)
(value_reg (alu_rr_imm12 (AluOPRRI.Andi) r (imm12_const 255))))
(rule (normalize_value $I16 r)
(value_reg (alu_rrr (AluOPRRR.And) r (imm $I16 65535))))
(rule (normalize_value $I32 r)
(value_reg (alu_rr_imm12 (AluOPRRI.Andi) r (imm12_const -1))))
(rule (normalize_value $I64 r) r)
(rule (normalize_value $I128 r) r)
(rule (normalize_value $F32 r) r)
(rule (normalize_value $F64 r) r)
;;;;;
(rule
(lower_branch (brz v @ (value_type ty) _ _) targets)
(lower_brz_or_nz (IntCC.Equal) v targets ty))
(lower_brz_or_nz (IntCC.Equal) (normalize_value ty v) targets ty))
;;;;
(rule
(lower_branch (brnz v @ (value_type ty) _ _) targets)
(lower_brz_or_nz (IntCC.NotEqual) v targets ty))
(lower_brz_or_nz (IntCC.NotEqual) (normalize_value ty v) targets ty))
;;;
(rule
@@ -2082,3 +2086,43 @@
(decl umulh (Reg Reg) Reg)
(rule (umulh a b)
(alu_rrr (AluOPRRR.Mulhu) a b))
;;;; Helpers for bmask ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl lower_bmask (Type Type ValueRegs) ValueRegs)
;; Produces -1 if the 64-bit value is non-zero, and 0 otherwise.
(rule
0
(lower_bmask (fits_in_64 _) (fits_in_64 _) val)
(let ((input Reg val)
(zero Reg (zero_reg))
(ones Reg (load_imm12 -1)))
(value_reg (gen_select_reg (IntCC.Equal) zero input zero ones))))
;; Bitwise-or the two registers that make up the 128-bit value, then recurse as
;; though it was a 64-bit value.
(rule
1
(lower_bmask (fits_in_64 ty) $I128 val)
(let ((lo Reg (value_regs_get val 0))
(hi Reg (value_regs_get val 1))
(combined Reg (alu_rrr (AluOPRRR.Or) lo hi)))
(lower_bmask ty $I64 (value_reg combined))))
;; Conversion of one 64-bit value to a 128-bit one. Duplicate the result of the
;; bmask of the 64-bit value into both result registers of the i128.
(rule
2
(lower_bmask $I128 (fits_in_64 _) val)
(let ((res ValueRegs (lower_bmask $I64 $I64 val)))
(value_regs (value_regs_get res 0) (value_regs_get res 0))))
;; Conversion of one 64-bit value to a 128-bit one. Duplicate the result of
;; bmasking the 128-bit value to a 64-bit value into both registers of the
;; 128-bit result.
(rule
3
(lower_bmask $I128 $I128 val)
(let ((res ValueRegs (lower_bmask $I64 $I128 val)))
(value_regs (value_regs_get res 0) (value_regs_get res 0))))

View File

@@ -1189,10 +1189,8 @@ impl LoadOP {
return if t == F32 { Self::Flw } else { Self::Fld };
}
match t {
B1 | B8 => Self::Lbu,
B16 => Self::Lhu,
B32 | R32 => Self::Lwu,
B64 | R64 | I64 => Self::Ld,
R32 => Self::Lwu,
R64 | I64 => Self::Ld,
I8 => Self::Lb,
I16 => Self::Lh,

View File

@@ -1039,9 +1039,8 @@ impl MachInstEmit for Inst {
&Inst::CondBr {
taken,
not_taken,
kind,
mut kind,
} => {
let mut kind = kind;
kind.rs1 = allocs.next(kind.rs1);
kind.rs2 = allocs.next(kind.rs2);
match taken {
@@ -1385,13 +1384,13 @@ impl MachInstEmit for Inst {
.for_each(|i| i.emit(&[], sink, emit_info, state));
sink.bind_label(label_true);
Inst::load_imm12(rd, Imm12::from_bits(-1)).emit(&[], sink, emit_info, state);
Inst::load_imm12(rd, Imm12::TRUE).emit(&[], sink, emit_info, state);
Inst::Jal {
dest: BranchTarget::offset(Inst::INSTRUCTION_SIZE * 2),
}
.emit(&[], sink, emit_info, state);
sink.bind_label(label_false);
Inst::load_imm12(rd, Imm12::from_bits(0)).emit(&[], sink, emit_info, state);
Inst::load_imm12(rd, Imm12::FALSE).emit(&[], sink, emit_info, state);
}
&Inst::AtomicCas {
offset,

View File

@@ -572,16 +572,6 @@ fn test_riscv64_binemit() {
"lb a0,100(a1)",
0x6458503,
));
insns.push(TestUnit::new(
Inst::Load {
rd: writable_a0(),
op: LoadOP::Lbu,
flags: MemFlags::new(),
from: AMode::RegOffset(a1(), 100, B8),
},
"lbu a0,100(a1)",
0x645c503,
));
insns.push(TestUnit::new(
Inst::Load {
rd: writable_a0(),
@@ -593,17 +583,6 @@ fn test_riscv64_binemit() {
0x6459503,
));
insns.push(TestUnit::new(
Inst::Load {
rd: writable_a0(),
op: LoadOP::Lhu,
flags: MemFlags::new(),
from: AMode::RegOffset(a1(), 100, B16),
},
"lhu a0,100(a1)",
0x645d503,
));
insns.push(TestUnit::new(
Inst::Load {
rd: writable_a0(),
@@ -615,16 +594,6 @@ fn test_riscv64_binemit() {
0x645a503,
));
insns.push(TestUnit::new(
Inst::Load {
rd: writable_a0(),
op: LoadOP::Lwu,
flags: MemFlags::new(),
from: AMode::RegOffset(a1(), 100, B32),
},
"lwu a0,100(a1)",
0x645e503,
));
insns.push(TestUnit::new(
Inst::Load {
rd: writable_a0(),

View File

@@ -12,7 +12,7 @@ pub struct Imm12 {
impl Imm12 {
pub(crate) const FALSE: Self = Self { bits: 0 };
pub(crate) const TRUE: Self = Self { bits: -1 };
pub(crate) const TRUE: Self = Self { bits: 1 };
pub fn maybe_from_u64(val: u64) -> Option<Imm12> {
let sign_bit = 1 << 11;
if val == 0 {

View File

@@ -6,9 +6,7 @@
use crate::binemit::{Addend, CodeOffset, Reloc};
pub use crate::ir::condcodes::IntCC;
use crate::ir::types::{
B1, B128, B16, B32, B64, B8, F32, F64, FFLAGS, I128, I16, I32, I64, I8, IFLAGS, R32, R64,
};
use crate::ir::types::{F32, F64, FFLAGS, I128, I16, I32, I64, I8, IFLAGS, R32, R64};
pub use crate::ir::{ExternalName, MemFlags, Opcode, SourceLoc, Type, ValueLabel};
use crate::isa::CallConv;
@@ -691,14 +689,11 @@ impl MachInst for Inst {
fn gen_constant<F: FnMut(Type) -> Writable<Reg>>(
to_regs: ValueRegs<Writable<Reg>>,
mut value: u128,
value: u128,
ty: Type,
mut alloc_tmp: F,
) -> SmallVec<[Inst; 4]> {
if ty.is_bool() && value != 0 {
value = !0;
}
if (ty.bits() <= 64 && (ty.is_bool() || ty.is_int())) || ty == R32 || ty == R64 {
if (ty.bits() <= 64 && ty.is_int()) || ty == R32 || ty == R64 {
return Inst::load_constant_u64(to_regs.only_reg().unwrap(), value as u64);
};
match ty {
@@ -708,7 +703,7 @@ impl MachInst for Inst {
F64 => {
Inst::load_fp_constant64(to_regs.only_reg().unwrap(), value as u64, alloc_tmp(I64))
}
I128 | B128 => {
I128 => {
let mut insts = SmallInstVec::new();
insts.extend(Inst::load_constant_u64(
to_regs.regs()[0],
@@ -736,17 +731,11 @@ impl MachInst for Inst {
I16 => Ok((&[RegClass::Int], &[I16])),
I32 => Ok((&[RegClass::Int], &[I32])),
I64 => Ok((&[RegClass::Int], &[I64])),
B1 => Ok((&[RegClass::Int], &[B1])),
B8 => Ok((&[RegClass::Int], &[B8])),
B16 => Ok((&[RegClass::Int], &[B16])),
B32 => Ok((&[RegClass::Int], &[B32])),
B64 => Ok((&[RegClass::Int], &[B64])),
R32 => panic!("32-bit reftype pointer should never be seen on riscv64"),
R64 => Ok((&[RegClass::Int], &[R64])),
F32 => Ok((&[RegClass::Float], &[F32])),
F64 => Ok((&[RegClass::Float], &[F64])),
I128 => Ok((&[RegClass::Int, RegClass::Int], &[I64, I64])),
B128 => Ok((&[RegClass::Int, RegClass::Int], &[B64, B64])),
IFLAGS => Ok((&[RegClass::Int], &[IFLAGS])),
FFLAGS => Ok((&[RegClass::Int], &[FFLAGS])),
_ => Err(CodegenError::Unsupported(format!(

View File

@@ -143,7 +143,7 @@ mod tests {
assert_eq!(
format!("{:?}", fde),
"FrameDescriptionEntry { address: Constant(4321), length: 12, lsda: None, instructions: [] }"
"FrameDescriptionEntry { address: Constant(4321), length: 16, lsda: None, instructions: [] }"
);
}

View File

@@ -9,15 +9,6 @@
(rule (lower (has_type ty (iconst (u64_from_imm64 n))))
(imm ty n))
;;;; Rules for `bconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (has_type ty (bconst $false)))
(imm ty 0))
(rule (lower (has_type ty (bconst $true)))
(imm ty 1))
;;;; Rules for `null` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (has_type ty (null)))
@@ -194,8 +185,6 @@
(rule 1 (lower (has_type (fits_in_64 ty) (band (imm12_from_value x) y)))
(alu_rr_imm12 (AluOPRRI.Andi) y x))
(rule (lower (has_type $B128 (band x y)))
(lower_b128_binary (AluOPRRR.And) x y))
(rule (lower (has_type $I128 (band x y)))
(lower_b128_binary (AluOPRRR.And) x y))
@@ -215,8 +204,6 @@
(rule 1 (lower (has_type (fits_in_64 ty) (bor (imm12_from_value x) y)))
(alu_rr_imm12 (AluOPRRI.Ori) y x))
(rule (lower (has_type $B128 (bor x y)))
(lower_b128_binary (AluOPRRR.Or) x y))
(rule (lower (has_type $I128 (bor x y)))
(lower_b128_binary (AluOPRRR.Or) x y))
(rule (lower (has_type $F32 (bor x y)))
@@ -235,8 +222,6 @@
(rule 1 (lower (has_type (fits_in_64 ty) (bxor (imm12_from_value x) y)))
(alu_rr_imm12 (AluOPRRI.Xori) y x))
(rule (lower (has_type $B128 (bxor x y)))
(lower_b128_binary (AluOPRRR.Xor) x y))
(rule (lower (has_type $I128 (bxor x y)))
(lower_b128_binary (AluOPRRR.Xor) x y))
(rule (lower (has_type $F32 (bxor x y)))
@@ -251,8 +236,6 @@
(rule (lower (has_type $I128 (bnot x)))
(bnot_128 x))
(rule (lower (has_type $B128 (bnot x)))
(bnot_128 x))
(rule
(lower (has_type $F32 (bnot x)))
(lower_float_bnot x $F32)
@@ -556,11 +539,6 @@
(rule (lower (has_type ty (copy x)))
(gen_move2 x ty ty))
;;;;; Rules for `breduce`;;;;;;;;;;;;;;;;;
(rule
(lower (has_type ty (breduce x)))
(gen_move2 (value_regs_get x 0) ty ty))
;;;;; Rules for `ireduce`;;;;;;;;;;;;;;;;;
(rule
(lower (has_type ty (ireduce x)))
@@ -623,8 +601,8 @@
;;;;; Rules for `select`;;;;;;;;;
(rule
(lower (has_type ty (select c x y)))
(gen_select ty c x y)
(lower (has_type ty (select c @ (value_type cty) x y)))
(gen_select ty (normalize_value cty c) x y)
)
;;;;; Rules for `bitselect`;;;;;;;;;
@@ -633,15 +611,6 @@
(lower (has_type ty (bitselect c x y)))
(gen_bitselect ty c x y))
;;;;; Rules for `bint`;;;;;;;;;
(rule
(lower (has_type (fits_in_64 ty) (bint (valueregs_2_reg x))))
(gen_bint x))
(rule 1
(lower (has_type $I128 (bint (valueregs_2_reg x))))
(let ((tmp Reg (gen_bint x)))
(value_regs tmp (load_u64_constant 0))))
;;;;; Rules for `isplit`;;;;;;;;;
(rule
(lower (isplit x))
@@ -733,10 +702,6 @@
(rule 1
(lower (has_type $I128 (load flags p offset)))
(gen_load_128 p offset flags))
;;;; for B128
(rule 1
(lower (has_type $B128 (load flags p offset)))
(gen_load_128 p offset flags))
;;;;; Rules for `istore8`;;;;;;;;;
(rule
@@ -762,11 +727,6 @@
(lower (store flags x @ (value_type $I128 ) p offset))
(gen_store_128 p offset flags x))
;;; special for B128
(rule 1
(lower (store flags x @ (value_type $B128 ) p offset))
(gen_store_128 p offset flags x))
(decl gen_icmp (IntCC ValueRegs ValueRegs Type) Reg)
(rule
(gen_icmp cc x y ty)
@@ -923,34 +883,8 @@
;;;;; Rules for `bmask`;;;;;;;;;
(rule
;; because we encode bool all 1s.
;; move is just ok.
(lower (has_type (fits_in_64 ty) (bmask x @ (value_type ity))))
(gen_move2 (value_regs_get x 0) ity ty))
;;; for i128
(rule 1
;; because we encode bool all 1s.
;; move is just ok.
(lower (has_type $I128 (bmask x @ (value_type ity))))
(value_regs (gen_move2 (value_regs_get x 0) $I64 $I64) (gen_move2 (value_regs_get x 0) $I64 $I64)))
;;;;; Rules for `bextend`;;;;;;;;;
(rule
;; because we encode bool all 1s.
;; move is just ok.
(lower (has_type ty (bextend x @ (value_type ity))))
;;extra checks.
(if-let _ (valid_bextend_ty ity ty))
(gen_moves x ity ty))
;;; for B128
(rule 1
;; because we encode bool all 1s.
;; move is just ok.
(lower (has_type ty (bextend x @ (value_type ity))))
;;extra checks.
(if-let $B128 (valid_bextend_ty ity ty))
(value_regs (gen_moves x $I64 $I64) (gen_moves x $I64 $I64)))
(lower (has_type oty (bmask x @ (value_type ity))))
(lower_bmask oty ity x))
;; N.B.: the Ret itself is generated by the ABI.
(rule (lower (return args))

View File

@@ -71,13 +71,6 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
}
}
fn valid_bextend_ty(&mut self, from: Type, to: Type) -> Option<Type> {
if from.is_bool() && to.is_bool() && from.bits() < to.bits() {
Some(to)
} else {
None
}
}
fn lower_br_fcmp(
&mut self,
cc: &FloatCC,
@@ -155,7 +148,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
}
}
fn int_zero_reg(&mut self, ty: Type) -> ValueRegs {
assert!(ty.is_int() || ty.is_bool(), "{:?}", ty);
assert!(ty.is_int(), "{:?}", ty);
if ty.bits() == 128 {
ValueRegs::two(self.zero_reg(), self.zero_reg())
} else {
@@ -190,7 +183,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
Imm12::from_bits(imm.as_i16() & (x as i16))
}
fn alloc_vec_writable(&mut self, ty: Type) -> VecWritableReg {
if ty.is_int() || ty.is_bool() || ty == R32 || ty == R64 {
if ty.is_int() || ty == R32 || ty == R64 {
if ty.bits() <= 64 {
vec![self.temp_writable_reg(I64)]
} else {
@@ -203,26 +196,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
}
}
fn imm(&mut self, ty: Type, mut val: u64) -> Reg {
// Boolean types
// Boolean values are either true or false.
// The b1 type represents an abstract boolean value. It can only exist as an SSA value, and can't be directly stored in memory. It can, however, be converted into an integer with value 0 or 1 by the bint instruction (and converted back with icmp_imm with 0).
// Several larger boolean types are also defined, primarily to be used as SIMD element types. They can be stored in memory, and are represented as either all zero bits or all one bits.
// b1
// b8
// b16
// b32
// b64
// ///////////////////////////////////////////////////////////
// "represented as either all zero bits or all one bits."
// \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
if ty.is_bool() && val != 0 {
// need all be one
val = !0;
}
fn imm(&mut self, ty: Type, val: u64) -> Reg {
let tmp = self.temp_writable_reg(ty);
self.emit_list(&MInst::load_constant_u64(tmp, val));
tmp.to_reg()
@@ -296,7 +270,11 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
rd.to_reg()
}
fn imm12_const(&mut self, val: i32) -> Imm12 {
Imm12::maybe_from_u64(val as u64).unwrap()
if let Some(res) = Imm12::maybe_from_u64(val as u64) {
res
} else {
panic!("Unable to make an Imm12 value from {}", val)
}
}
fn imm12_const_add(&mut self, val: i32, add: i32) -> Imm12 {
Imm12::maybe_from_u64((val + add) as u64).unwrap()
@@ -526,7 +504,7 @@ fn construct_dest<F: std::ops::FnMut(Type) -> WritableReg>(
mut alloc: F,
ty: Type,
) -> WritableValueRegs {
if ty.is_bool() || ty.is_int() {
if ty.is_int() {
if ty.bits() == 128 {
WritableValueRegs::two(alloc(I64), alloc(I64))
} else {