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

@@ -94,7 +94,6 @@ pub type S390xCallee = Callee<S390xMachineDeps>;
fn in_int_reg(ty: Type) -> bool {
match ty {
types::I8 | types::I16 | types::I32 | types::I64 | types::R64 => true,
types::B1 | types::B8 | types::B16 | types::B32 | types::B64 => true,
_ => false,
}
}

View File

@@ -3129,7 +3129,6 @@
dst))
;; Sign-extend a register from a smaller `Type` into a 32-bit register.
;; This handles both integer and boolean input types (except $B1).
(decl sext32_reg (Type Reg) Reg)
(rule (sext32_reg ty src)
(let ((dst WritableReg (temp_writable_reg $I32))
@@ -3137,7 +3136,6 @@
dst))
;; Zero-extend a register from a smaller `Type` into a 64-bit register.
;; This handles both integer and boolean input types (except $B1).
(decl zext64_reg (Type Reg) Reg)
(rule (zext64_reg ty src)
(let ((dst WritableReg (temp_writable_reg $I64))
@@ -3145,7 +3143,6 @@
dst))
;; Sign-extend a register from a smaller `Type` into a 64-bit register.
;; This handles both integer and boolean input types (except $B1).
(decl sext64_reg (Type Reg) Reg)
(rule (sext64_reg ty src)
(let ((dst WritableReg (temp_writable_reg $I64))
@@ -3477,14 +3474,19 @@
(_ Unit (emit_consumer (emit_cmov_imm ty dst cond imm_true))))
dst))
;; Lower a boolean condition to a boolean type. The value used to represent
;; "true" is -1 for all result types except for $B1, which uses 1.
;; Lower a boolean condition to the values 1/0. This rule is only used in the
;; context of instructions that return $I8 results.
(decl lower_bool (Type ProducesBool) Reg)
(rule (lower_bool $B1 cond) (select_bool_imm $B1 cond 1 0))
(rule (lower_bool $B8 cond) (select_bool_imm $B8 cond -1 0))
(rule (lower_bool $B16 cond) (select_bool_imm $B16 cond -1 0))
(rule (lower_bool $B32 cond) (select_bool_imm $B32 cond -1 0))
(rule (lower_bool $B64 cond) (select_bool_imm $B64 cond -1 0))
(rule (lower_bool $I8 cond) (select_bool_imm $I8 cond 1 0))
;; Lower a boolean condition to the values -1/0.
(decl lower_bool_to_mask (Type ProducesBool) Reg)
(rule 0 (lower_bool_to_mask (fits_in_64 ty) producer)
(select_bool_imm ty producer -1 0))
(rule 1 (lower_bool_to_mask $I128 producer)
(let ((res Reg (lower_bool_to_mask $I64 producer)))
(mov_to_vec128 $I128 res res)))
;; Emit a conditional branch based on a boolean condition.
(decl cond_br_bool (ProducesBool MachLabel MachLabel) SideEffectNoResult)

View File

@@ -397,10 +397,10 @@ impl Inst {
/// Generic constructor for a load (zero-extending where appropriate).
pub fn gen_load(into_reg: Writable<Reg>, mem: MemArg, ty: Type) -> Inst {
match ty {
types::B1 | types::B8 | types::I8 => Inst::Load64ZExt8 { rd: into_reg, mem },
types::B16 | types::I16 => Inst::Load64ZExt16 { rd: into_reg, mem },
types::B32 | types::I32 => Inst::Load64ZExt32 { rd: into_reg, mem },
types::B64 | types::I64 | types::R64 => Inst::Load64 { rd: into_reg, mem },
types::I8 => Inst::Load64ZExt8 { rd: into_reg, mem },
types::I16 => Inst::Load64ZExt16 { rd: into_reg, mem },
types::I32 => Inst::Load64ZExt32 { rd: into_reg, mem },
types::I64 | types::R64 => Inst::Load64 { rd: into_reg, mem },
types::F32 => Inst::VecLoadLaneUndef {
size: 32,
rd: into_reg,
@@ -414,7 +414,7 @@ impl Inst {
lane_imm: 0,
},
_ if ty.is_vector() && ty.bits() == 128 => Inst::VecLoad { rd: into_reg, mem },
types::B128 | types::I128 => Inst::VecLoad { rd: into_reg, mem },
types::I128 => Inst::VecLoad { rd: into_reg, mem },
_ => unimplemented!("gen_load({})", ty),
}
}
@@ -422,10 +422,10 @@ impl Inst {
/// Generic constructor for a store.
pub fn gen_store(mem: MemArg, from_reg: Reg, ty: Type) -> Inst {
match ty {
types::B1 | types::B8 | types::I8 => Inst::Store8 { rd: from_reg, mem },
types::B16 | types::I16 => Inst::Store16 { rd: from_reg, mem },
types::B32 | types::I32 => Inst::Store32 { rd: from_reg, mem },
types::B64 | types::I64 | types::R64 => Inst::Store64 { rd: from_reg, mem },
types::I8 => Inst::Store8 { rd: from_reg, mem },
types::I16 => Inst::Store16 { rd: from_reg, mem },
types::I32 => Inst::Store32 { rd: from_reg, mem },
types::I64 | types::R64 => Inst::Store64 { rd: from_reg, mem },
types::F32 => Inst::VecStoreLane {
size: 32,
rd: from_reg,
@@ -439,7 +439,7 @@ impl Inst {
lane_imm: 0,
},
_ if ty.is_vector() && ty.bits() == 128 => Inst::VecStore { rd: from_reg, mem },
types::B128 | types::I128 => Inst::VecStore { rd: from_reg, mem },
types::I128 => Inst::VecStore { rd: from_reg, mem },
_ => unimplemented!("gen_store({})", ty),
}
}
@@ -1086,7 +1086,7 @@ impl MachInst for Inst {
.only_reg()
.expect("multi-reg values not supported yet");
match ty {
types::I128 | types::B128 => {
types::I128 => {
let mut ret = SmallVec::new();
ret.push(Inst::load_vec_constant(to_reg, value));
ret
@@ -1112,14 +1112,8 @@ impl MachInst for Inst {
));
ret
}
types::I64 | types::B64 | types::R64 => Inst::load_constant64(to_reg, value as u64),
types::B1
| types::I8
| types::B8
| types::I16
| types::B16
| types::I32
| types::B32 => Inst::load_constant32(to_reg, value as u32),
types::I64 | types::R64 => Inst::load_constant64(to_reg, value as u64),
types::I8 | types::I16 | types::I32 => Inst::load_constant32(to_reg, value as u32),
_ => unreachable!(),
}
}
@@ -1140,17 +1134,11 @@ impl MachInst for Inst {
types::I16 => Ok((&[RegClass::Int], &[types::I16])),
types::I32 => Ok((&[RegClass::Int], &[types::I32])),
types::I64 => Ok((&[RegClass::Int], &[types::I64])),
types::B1 => Ok((&[RegClass::Int], &[types::B1])),
types::B8 => Ok((&[RegClass::Int], &[types::B8])),
types::B16 => Ok((&[RegClass::Int], &[types::B16])),
types::B32 => Ok((&[RegClass::Int], &[types::B32])),
types::B64 => Ok((&[RegClass::Int], &[types::B64])),
types::R32 => panic!("32-bit reftype pointer should never be seen on s390x"),
types::R64 => Ok((&[RegClass::Int], &[types::R64])),
types::F32 => Ok((&[RegClass::Float], &[types::F32])),
types::F64 => Ok((&[RegClass::Float], &[types::F64])),
types::I128 => Ok((&[RegClass::Float], &[types::I128])),
types::B128 => Ok((&[RegClass::Float], &[types::B128])),
_ if ty.is_vector() && ty.bits() == 128 => Ok((&[RegClass::Float], &[types::I8X16])),
// FIXME: We don't really have IFLAGS, but need to allow it here
// for now to support the SelectifSpectreGuard instruction.

View File

@@ -16,14 +16,6 @@
(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 `f32const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (f32const (u64_from_ieee32 x)))
@@ -1163,92 +1155,10 @@
(vec_select ty y z x))
;;;; Rules for `breduce` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Rules for `bmask` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Up to 64-bit source type: Always a no-op.
(rule 1 (lower (breduce x @ (value_type (fits_in_64 _ty))))
x)
;; 128-bit source type: Extract the low half.
(rule (lower (breduce x @ (value_type (vr128_ty _ty))))
(vec_extract_lane $I64X2 x 1 (zero_reg)))
;;;; Rules for `bextend` and `bmask` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Use a common helper to type cast bools to either bool or integer types.
(decl cast_bool (Type Value) Reg)
(rule (lower (has_type ty (bextend x)))
(cast_bool ty x))
(rule (lower (has_type ty (bmask x)))
(cast_bool ty x))
;; If the target has the same or a smaller size than the source, it's a no-op.
(rule 8 (cast_bool $B1 x @ (value_type $B1)) x)
(rule 8 (cast_bool $B1 x @ (value_type $B8)) x)
(rule 8 (cast_bool $B8 x @ (value_type $B8)) x)
(rule 8 (cast_bool $I8 x @ (value_type $B8)) x)
(rule 7 (cast_bool (fits_in_16 _ty) x @ (value_type $B16)) x)
(rule 6 (cast_bool (fits_in_32 _ty) x @ (value_type $B32)) x)
(rule 5 (cast_bool (fits_in_64 _ty) x @ (value_type $B64)) x)
(rule 4 (cast_bool (vr128_ty _ty) x @ (value_type $B128)) x)
(rule 5 (cast_bool (fits_in_64 _ty) x @ (value_type $B128))
(vec_extract_lane $I64X2 x 1 (zero_reg)))
;; Single-bit values are sign-extended via a pair of shifts.
(rule 0 (cast_bool (gpr32_ty ty) x @ (value_type $B1))
(ashr_imm $I32 (lshl_imm $I32 x 31) 31))
(rule 1 (cast_bool (gpr64_ty ty) x @ (value_type $B1))
(ashr_imm $I64 (lshl_imm $I64 x 63) 63))
(rule 4 (cast_bool (vr128_ty ty) x @ (value_type $B1))
(let ((gpr Reg (ashr_imm $I64 (lshl_imm $I64 x 63) 63)))
(mov_to_vec128 ty gpr gpr)))
;; Other values are just sign-extended normally.
(rule 0 (cast_bool (gpr32_ty _ty) x @ (value_type $B8))
(sext32_reg $I8 x))
(rule 0 (cast_bool (gpr32_ty _ty) x @ (value_type $B16))
(sext32_reg $I16 x))
(rule 1(cast_bool (gpr64_ty _ty) x @ (value_type $B8))
(sext64_reg $I8 x))
(rule 1(cast_bool (gpr64_ty _ty) x @ (value_type $B16))
(sext64_reg $I16 x))
(rule 1(cast_bool (gpr64_ty _ty) x @ (value_type $B32))
(sext64_reg $I32 x))
(rule 3 (cast_bool (vr128_ty ty) x @ (value_type (gpr32_ty src_ty)))
(let ((x_ext Reg (sext64_reg src_ty x)))
(mov_to_vec128 ty x_ext x_ext)))
(rule 2 (cast_bool (vr128_ty ty) x @ (value_type (gpr64_ty src_ty)))
(mov_to_vec128 ty x x))
;;;; Rules for `bint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Mask with 1 to get a 0/1 result (8- or 16-bit result types).
(rule 5 (lower (has_type (fits_in_16 ty) (bint x @ (value_type (fits_in_64 _)))))
(and_uimm16shifted ty x (uimm16shifted 1 0)))
;; Mask with 1 to get a 0/1 result (32-bit result types).
(rule 4 (lower (has_type (fits_in_32 ty) (bint x @ (value_type (fits_in_64 _)))))
(and_uimm32shifted ty x (uimm32shifted 1 0)))
;; Mask with 1 to get a 0/1 result (64-bit result types).
(rule 3 (lower (has_type (fits_in_64 ty) (bint x @ (value_type (fits_in_64 _)))))
(and_reg ty x (imm ty 1)))
;; Mask with 1 to get a 0/1 result (128-bit result types).
(rule 1 (lower (has_type (vr128_ty ty) (bint x @ (value_type (fits_in_64 _)))))
(let ((x_ext Reg (and_uimm16shifted $I8 x (uimm16shifted 1 0))))
(vec_insert_lane $I8X16 (vec_imm ty 0) x_ext 15 (zero_reg))))
;; Mask with 1 to get a 0/1 result (128-bit source types).
(rule 2 (lower (has_type (fits_in_64 ty) (bint x @ (value_type (vr128_ty _)))))
(let ((x_gpr Reg (vec_extract_lane $I8X16 x 15 (zero_reg))))
(and_uimm16shifted ty x_gpr (uimm16shifted 1 0))))
;; Mask with 1 to get a 0/1 result (128-bit source and result types).
(rule 0 (lower (has_type (vr128_ty ty) (bint x @ (value_type (vr128_ty _)))))
(vec_and ty x (vec_imm ty 1)))
(lower_bool_to_mask ty (value_nonzero x)))
;;;; Rules for `bitrev` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1864,7 +1774,7 @@
(rule 1 (lower (insertlane x @ (value_type ty)
y @ (value_type in_ty)
(u8_from_uimm8 idx)))
(if (ty_int_bool_ref_scalar_64 in_ty))
(if (ty_int_ref_scalar_64 in_ty))
(vec_insert_lane ty x y (be_lane_idx ty idx) (zero_reg)))
;; Insert vector lane from floating-point register.
@@ -1980,7 +1890,7 @@
;; Extract vector lane to general-purpose register.
(rule 1 (lower (has_type out_ty
(extractlane x @ (value_type ty) (u8_from_uimm8 idx))))
(if (ty_int_bool_ref_scalar_64 out_ty))
(if (ty_int_ref_scalar_64 out_ty))
(vec_extract_lane ty x (be_lane_idx ty idx) (zero_reg)))
;; Extract vector lane to floating-point register.
@@ -2037,7 +1947,7 @@
;; Load replicated value from general-purpose register.
(rule 1 (lower (has_type ty (splat x @ (value_type in_ty))))
(if (ty_int_bool_ref_scalar_64 in_ty))
(if (ty_int_ref_scalar_64 in_ty))
(vec_replicate_lane ty (vec_insert_lane_undef ty x 0 (zero_reg)) 0))
;; Load replicated value from floating-point register.
@@ -2097,7 +2007,7 @@
;; Load scalar value from general-purpose register.
(rule 1 (lower (has_type ty (scalar_to_vector
x @ (value_type in_ty))))
(if (ty_int_bool_ref_scalar_64 in_ty))
(if (ty_int_ref_scalar_64 in_ty))
(vec_insert_lane ty (vec_imm ty 0) x (be_lane_idx ty 0) (zero_reg)))
;; Load scalar value from floating-point register.
@@ -3783,14 +3693,14 @@
;;;; Rules for `is_null` and `is_invalid` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Null references are represented by the constant value 0.
(rule (lower (has_type $B1 (is_null x @ (value_type $R64))))
(lower_bool $B1 (bool (icmps_simm16 $I64 x 0)
(rule (lower (has_type $I8 (is_null x @ (value_type $R64))))
(lower_bool $I8 (bool (icmps_simm16 $I64 x 0)
(intcc_as_cond (IntCC.Equal)))))
;; Invalid references are represented by the constant value -1.
(rule (lower (has_type $B1 (is_invalid x @ (value_type $R64))))
(lower_bool $B1 (bool (icmps_simm16 $I64 x -1)
(rule (lower (has_type $I8 (is_invalid x @ (value_type $R64))))
(lower_bool $I8 (bool (icmps_simm16 $I64 x -1)
(intcc_as_cond (IntCC.Equal)))))
@@ -3798,10 +3708,9 @@
;; Return a `ProducesBool` to capture the fact that the input value is nonzero.
;; In the common case where that input is the result of an `icmp` or `fcmp`
;; instruction (possibly via an intermediate `bint`), directly use that compare.
;; Note that it is not safe to sink memory loads here, see the `icmp` comment.
;; instruction, directly use that compare. Note that it is not safe to sink
;; memory loads here, see the `icmp` comment.
(decl value_nonzero (Value) ProducesBool)
(rule (value_nonzero (bint val)) (value_nonzero val))
(rule (value_nonzero (icmp int_cc x y)) (icmp_val $false int_cc x y))
(rule (value_nonzero (fcmp float_cc x y)) (fcmp_val float_cc x y))
(rule -1 (value_nonzero val @ (value_type (gpr32_ty ty)))

View File

@@ -45,7 +45,6 @@ impl LowerBackend for S390xBackend {
Opcode::Nop
| Opcode::Copy
| Opcode::Iconst
| Opcode::Bconst
| Opcode::F32const
| Opcode::F64const
| Opcode::Vconst
@@ -100,10 +99,7 @@ impl LowerBackend for S390xBackend {
| Opcode::BxorNot
| Opcode::Bitselect
| Opcode::Vselect
| Opcode::Breduce
| Opcode::Bextend
| Opcode::Bmask
| Opcode::Bint
| Opcode::Bitrev
| Opcode::Clz
| Opcode::Cls

View File

@@ -252,7 +252,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
#[inline]
fn gpr32_ty(&mut self, ty: Type) -> Option<Type> {
match ty {
I8 | I16 | I32 | B1 | B8 | B16 | B32 => Some(ty),
I8 | I16 | I32 => Some(ty),
_ => None,
}
}
@@ -260,7 +260,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
#[inline]
fn gpr64_ty(&mut self, ty: Type) -> Option<Type> {
match ty {
I64 | B64 | R64 => Some(ty),
I64 | R64 => Some(ty),
_ => None,
}
}
@@ -268,7 +268,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
#[inline]
fn vr128_ty(&mut self, ty: Type) -> Option<Type> {
match ty {
I128 | B128 => Some(ty),
I128 => Some(ty),
_ if ty.is_vector() && ty.bits() == 128 => Some(ty),
_ => None,
}