Implement float -> small int cast

Also workaround small signed int eq/ne binop clif bug
This commit is contained in:
bjorn3 2019-08-12 17:25:16 +02:00
parent f5b0a68fbf
commit f93cd924ec
6 changed files with 128 additions and 117 deletions

View File

@ -52,6 +52,7 @@ unsafe impl Copy for i8 {}
unsafe impl Copy for i16 {}
unsafe impl Copy for i32 {}
unsafe impl Copy for isize {}
unsafe impl Copy for f32 {}
unsafe impl Copy for char {}
unsafe impl<'a, T: ?Sized> Copy for &'a T {}
unsafe impl<T: ?Sized> Copy for *const T {}
@ -277,6 +278,15 @@ impl PartialEq for usize {
}
}
impl PartialEq for i8 {
fn eq(&self, other: &i8) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &i8) -> bool {
(*self) != (*other)
}
}
impl PartialEq for i32 {
fn eq(&self, other: &i32) -> bool {
(*self) == (*other)
@ -344,6 +354,14 @@ impl Neg for isize {
}
}
impl Neg for f32 {
type Output = f32;
fn neg(self) -> f32 {
-self
}
}
pub enum Option<T> {
Some(T),
None,

View File

@ -250,4 +250,10 @@ fn main() {
unsafe { assert_eq!(ABC as usize, 0); }
&mut (|| Some(0 as *const ())) as &mut dyn FnMut() -> Option<*const ()>;
let f = 1000.0;
assert_eq!(f as u8, 255);
let f2 = -1000.0;
assert_eq!(f2 as i8, -128);
assert_eq!(f2 as u8, 0);
}

View File

@ -334,11 +334,7 @@ fn trans_stmt<'a, 'tcx: 'a>(
let lhs = trans_operand(fx, lhs);
let rhs = trans_operand(fx, rhs);
let signed = match ty.sty {
ty::Uint(_) => false,
ty::Int(_) => true,
_ => unimplemented!("checked binop {:?} for {:?}", bin_op, ty),
};
let signed = type_sign(ty);
let res = if !fx.tcx.sess.overflow_checks() {
let val = trans_int_binop(fx, *bin_op, lhs, rhs, lhs.layout().ty, signed).load_scalar(fx);
@ -444,14 +440,7 @@ fn trans_stmt<'a, 'tcx: 'a>(
let to_clif_ty = fx.clif_type(to_ty).unwrap();
let from = operand.load_scalar(fx);
let signed = match from_ty.sty {
ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) | ty::Char | ty::Uint(..) | ty::Bool => false,
ty::Int(..) => true,
ty::Float(..) => false, // `signed` is unused for floats
_ => panic!("{}", from_ty),
};
let res = clif_int_or_float_cast(fx, from, to_clif_ty, signed);
let res = clif_int_or_float_cast(fx, from, type_sign(from_ty), to_clif_ty, type_sign(to_ty));
lval.write_cvalue(fx, CValue::by_val(res, dest_layout));
}
}
@ -809,6 +798,21 @@ pub fn trans_int_binop<'a, 'tcx: 'a>(
return res;
}
let (lhs, rhs) = if
(bin_op == BinOp::Eq || bin_op == BinOp::Ne)
&& (lhs.layout().ty.sty == fx.tcx.types.i8.sty || lhs.layout().ty.sty == fx.tcx.types.i16.sty)
{
// FIXME(CraneStation/cranelift#896) icmp_imm.i8/i16 with eq/ne for signed ints is implemented wrong.
let lhs = lhs.load_scalar(fx);
let rhs = rhs.load_scalar(fx);
(
CValue::by_val(fx.bcx.ins().sextend(types::I32, lhs), fx.layout_of(fx.tcx.types.i32)),
CValue::by_val(fx.bcx.ins().sextend(types::I32, rhs), fx.layout_of(fx.tcx.types.i32)),
)
} else {
(lhs, rhs)
};
binop_match! {
fx, bin_op, signed, lhs, rhs, out_ty, "int/uint";
Add (_) iadd;

View File

@ -49,8 +49,9 @@ pub fn clif_intcast<'a, 'tcx: 'a>(
pub fn clif_int_or_float_cast(
fx: &mut FunctionCx<'_, '_, impl Backend>,
from: Value,
from_signed: bool,
to_ty: Type,
signed: bool,
to_signed: bool,
) -> Value {
let from_ty = fx.bcx.func.dfg.value_type(from);
@ -60,21 +61,56 @@ pub fn clif_int_or_float_cast(
fx,
from,
to_ty,
signed,
from_signed, // FIXME is this correct?
)
} else if from_ty.is_int() && to_ty.is_float() {
// int-like -> float
if signed {
if from_signed {
fx.bcx.ins().fcvt_from_sint(to_ty, from)
} else {
fx.bcx.ins().fcvt_from_uint(to_ty, from)
}
} else if from_ty.is_float() && to_ty.is_int() {
// float -> int-like
if signed {
fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
if to_ty == types::I8 || to_ty == types::I16 {
// FIXME implement fcbt_to_*int_sat.i8/i16
let val = if to_signed {
fx.bcx.ins().fcvt_to_sint_sat(types::I32, from)
} else {
fx.bcx.ins().fcvt_to_uint_sat(types::I32, from)
};
let (min, max) = type_min_max_value(to_ty, to_signed);
let min_val = fx.bcx.ins().iconst(types::I32, min);
let max_val = fx.bcx.ins().iconst(types::I32, max);
let val = if to_signed {
let has_underflow = fx.bcx.ins().icmp_imm(
IntCC::SignedLessThan,
val,
min,
);
let has_overflow = fx.bcx.ins().icmp_imm(
IntCC::SignedGreaterThan,
val,
max,
);
let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, val);
fx.bcx.ins().select(has_overflow, max_val, bottom_capped)
} else {
let has_overflow = fx.bcx.ins().icmp_imm(
IntCC::UnsignedGreaterThan,
val,
max,
);
fx.bcx.ins().select(has_overflow, max_val, val)
};
fx.bcx.ins().ireduce(to_ty, val)
} else {
fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
if to_signed {
fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
} else {
fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
}
}
} else if from_ty.is_float() && to_ty.is_float() {
// float -> float

View File

@ -116,74 +116,46 @@ pub fn resolve_value_imm(func: &Function, val: Value) -> Option<u128> {
}
}
pub fn type_min_max_value<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (i64, i64) {
use syntax::ast::UintTy::*;
use syntax::ast::IntTy::*;
let uint_usize_cvt = |uint| {
match uint {
UintTy::Usize => match pointer_ty(tcx) {
types::I16 => UintTy::U16,
types::I32 => UintTy::U32,
types::I64 => UintTy::U64,
ty => unreachable!("{:?}", ty),
}
_ => uint,
}
};
let int_isize_cvt = |int| {
match int {
IntTy::Isize => match pointer_ty(tcx) {
types::I16 => IntTy::I16,
types::I32 => IntTy::I32,
types::I64 => IntTy::I64,
ty => unreachable!("{:?}", ty),
}
_ => int,
}
};
let min = match ty.sty {
ty::Uint(uint) => match uint_usize_cvt(uint) {
U8 | U16 | U32 | U64 => 0i64,
U128 => unimplemented!(),
Usize => unreachable!(),
}
ty::Int(int) => match int_isize_cvt(int) {
I8 => i8::min_value() as i64,
I16 => i16::min_value() as i64,
I32 => i32::min_value() as i64,
I64 => i64::min_value(),
I128 => unimplemented!(),
Isize => unreachable!(),
}
pub fn type_min_max_value(ty: Type, signed: bool) -> (i64, i64) {
assert!(ty.is_int());
let min = match (ty, signed) {
(types::I8 , false)
| (types::I16, false)
| (types::I32, false)
| (types::I64, false) => 0i64,
(types::I8, true) => i8::min_value() as i64,
(types::I16, true) => i16::min_value() as i64,
(types::I32, true) => i32::min_value() as i64,
(types::I64, true) => i64::min_value(),
(types::I128, _) => unimplemented!(),
_ => unreachable!(),
};
let max = match ty.sty {
ty::Uint(uint) => match uint_usize_cvt(uint) {
U8 => u8::max_value() as i64,
U16 => u16::max_value() as i64,
U32 => u32::max_value() as i64,
U64 => u64::max_value() as i64,
U128 => unimplemented!(),
Usize => unreachable!(),
}
ty::Int(int) => match int_isize_cvt(int) {
I8 => i8::max_value() as i64,
I16 => i16::max_value() as i64,
I32 => i32::max_value() as i64,
I64 => i64::max_value(),
I128 => unimplemented!(),
Isize => unreachable!(),
}
let max = match (ty, signed) {
(types::I8, false) => u8::max_value() as i64,
(types::I16, false) => u16::max_value() as i64,
(types::I32, false) => u32::max_value() as i64,
(types::I64, false) => u64::max_value() as i64,
(types::I8, true) => i8::max_value() as i64,
(types::I16, true) => i16::max_value() as i64,
(types::I32, true) => i32::max_value() as i64,
(types::I64, true) => i64::max_value(),
(types::I128, _) => unimplemented!(),
_ => unreachable!(),
};
(min, max)
}
pub fn type_sign(ty: Ty<'_>) -> bool {
match ty.sty {
ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) | ty::Char | ty::Uint(..) | ty::Bool => false,
ty::Int(..) => true,
ty::Float(..) => false, // `signed` is unused for floats
_ => panic!("{}", ty),
}
}
pub struct FunctionCx<'a, 'tcx: 'a, B: Backend> {
// FIXME use a reference to `CodegenCx` instead of `tcx`, `module` and `constants` and `caches`
pub tcx: TyCtxt<'tcx>,

View File

@ -473,19 +473,13 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
_ => unimplemented!("intrinsic {}", intrinsic),
};
let signed = match T.sty {
ty::Uint(_) => false,
ty::Int(_) => true,
_ => unimplemented!("{} for {:?}", intrinsic, T),
};
let res = crate::base::trans_checked_int_binop(
fx,
bin_op,
x,
y,
ret.layout().ty,
signed,
type_sign(T),
);
ret.write_cvalue(fx, res);
};
@ -497,25 +491,14 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
"overflowing_mul" => BinOp::Mul,
_ => unimplemented!("intrinsic {}", intrinsic),
};
let res = match T.sty {
ty::Uint(_) => crate::base::trans_int_binop(
fx,
bin_op,
x,
y,
ret.layout().ty,
false,
),
ty::Int(_) => crate::base::trans_int_binop(
fx,
bin_op,
x,
y,
ret.layout().ty,
true,
),
_ => panic!(),
};
let res = crate::base::trans_int_binop(
fx,
bin_op,
x,
y,
ret.layout().ty,
type_sign(T),
);
ret.write_cvalue(fx, res);
};
_ if intrinsic.starts_with("saturating_"), <T> (c x, c y) {
@ -527,11 +510,7 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
_ => unimplemented!("intrinsic {}", intrinsic),
};
let signed = match T.sty {
ty::Uint(_) => false,
ty::Int(_) => true,
_ => unimplemented!("{} for {:?}", intrinsic, T),
};
let signed = type_sign(T);
let checked_res = crate::base::trans_checked_int_binop(
fx,
@ -548,7 +527,7 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
// `select.i8` is not implemented by Cranelift.
let has_overflow = fx.bcx.ins().uextend(types::I32, has_overflow);
let (min, max) = type_min_max_value(fx.tcx, T);
let (min, max) = type_min_max_value(clif_ty, signed);
let min = fx.bcx.ins().iconst(clif_ty, min);
let max = fx.bcx.ins().iconst(clif_ty, max);
@ -879,18 +858,14 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
let ret_lane_ty = fx.clif_type(ret_lane_layout.ty).unwrap();
let signed = match lane_layout.ty.sty {
ty::Uint(..) => false,
ty::Int(..) => true,
ty::Float(..) => false, // `signed` is unused for floats
_ => panic!("{}", lane_layout.ty),
};
let from_signed = type_sign(lane_layout.ty);
let to_signed = type_sign(ret_lane_layout.ty);
for lane in 0..lane_count {
let lane = mir::Field::new(lane.try_into().unwrap());
let a_lane = a.value_field(fx, lane).load_scalar(fx);
let res = clif_int_or_float_cast(fx, a_lane, ret_lane_ty, signed);
let res = clif_int_or_float_cast(fx, a_lane, from_signed, ret_lane_ty, to_signed);
ret.place_field(fx, lane).write_cvalue(fx, CValue::by_val(res, ret_lane_layout));
}
};