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 i16 {}
unsafe impl Copy for i32 {} unsafe impl Copy for i32 {}
unsafe impl Copy for isize {} unsafe impl Copy for isize {}
unsafe impl Copy for f32 {}
unsafe impl Copy for char {} unsafe impl Copy for char {}
unsafe impl<'a, T: ?Sized> Copy for &'a T {} unsafe impl<'a, T: ?Sized> Copy for &'a T {}
unsafe impl<T: ?Sized> Copy for *const 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 { impl PartialEq for i32 {
fn eq(&self, other: &i32) -> bool { fn eq(&self, other: &i32) -> bool {
(*self) == (*other) (*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> { pub enum Option<T> {
Some(T), Some(T),
None, None,

View File

@ -250,4 +250,10 @@ fn main() {
unsafe { assert_eq!(ABC as usize, 0); } unsafe { assert_eq!(ABC as usize, 0); }
&mut (|| Some(0 as *const ())) as &mut dyn FnMut() -> Option<*const ()>; &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 lhs = trans_operand(fx, lhs);
let rhs = trans_operand(fx, rhs); let rhs = trans_operand(fx, rhs);
let signed = match ty.sty { let signed = type_sign(ty);
ty::Uint(_) => false,
ty::Int(_) => true,
_ => unimplemented!("checked binop {:?} for {:?}", bin_op, ty),
};
let res = if !fx.tcx.sess.overflow_checks() { 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); 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 to_clif_ty = fx.clif_type(to_ty).unwrap();
let from = operand.load_scalar(fx); let from = operand.load_scalar(fx);
let signed = match from_ty.sty { let res = clif_int_or_float_cast(fx, from, type_sign(from_ty), to_clif_ty, type_sign(to_ty));
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);
lval.write_cvalue(fx, CValue::by_val(res, dest_layout)); lval.write_cvalue(fx, CValue::by_val(res, dest_layout));
} }
} }
@ -809,6 +798,21 @@ pub fn trans_int_binop<'a, 'tcx: 'a>(
return res; 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! { binop_match! {
fx, bin_op, signed, lhs, rhs, out_ty, "int/uint"; fx, bin_op, signed, lhs, rhs, out_ty, "int/uint";
Add (_) iadd; Add (_) iadd;

View File

@ -49,8 +49,9 @@ pub fn clif_intcast<'a, 'tcx: 'a>(
pub fn clif_int_or_float_cast( pub fn clif_int_or_float_cast(
fx: &mut FunctionCx<'_, '_, impl Backend>, fx: &mut FunctionCx<'_, '_, impl Backend>,
from: Value, from: Value,
from_signed: bool,
to_ty: Type, to_ty: Type,
signed: bool, to_signed: bool,
) -> Value { ) -> Value {
let from_ty = fx.bcx.func.dfg.value_type(from); let from_ty = fx.bcx.func.dfg.value_type(from);
@ -60,21 +61,56 @@ pub fn clif_int_or_float_cast(
fx, fx,
from, from,
to_ty, to_ty,
signed, from_signed, // FIXME is this correct?
) )
} else if from_ty.is_int() && to_ty.is_float() { } else if from_ty.is_int() && to_ty.is_float() {
// int-like -> float // int-like -> float
if signed { if from_signed {
fx.bcx.ins().fcvt_from_sint(to_ty, from) fx.bcx.ins().fcvt_from_sint(to_ty, from)
} else { } else {
fx.bcx.ins().fcvt_from_uint(to_ty, from) fx.bcx.ins().fcvt_from_uint(to_ty, from)
} }
} else if from_ty.is_float() && to_ty.is_int() { } else if from_ty.is_float() && to_ty.is_int() {
// float -> int-like // float -> int-like
if signed { if to_ty == types::I8 || to_ty == types::I16 {
fx.bcx.ins().fcvt_to_sint_sat(to_ty, from) // 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 { } 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() { } else if from_ty.is_float() && to_ty.is_float() {
// float -> 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) { pub fn type_min_max_value(ty: Type, signed: bool) -> (i64, i64) {
use syntax::ast::UintTy::*; assert!(ty.is_int());
use syntax::ast::IntTy::*; let min = match (ty, signed) {
(types::I8 , false)
let uint_usize_cvt = |uint| { | (types::I16, false)
match uint { | (types::I32, false)
UintTy::Usize => match pointer_ty(tcx) { | (types::I64, false) => 0i64,
types::I16 => UintTy::U16, (types::I8, true) => i8::min_value() as i64,
types::I32 => UintTy::U32, (types::I16, true) => i16::min_value() as i64,
types::I64 => UintTy::U64, (types::I32, true) => i32::min_value() as i64,
ty => unreachable!("{:?}", ty), (types::I64, true) => i64::min_value(),
} (types::I128, _) => unimplemented!(),
_ => 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!(),
}
_ => unreachable!(), _ => unreachable!(),
}; };
let max = match ty.sty { let max = match (ty, signed) {
ty::Uint(uint) => match uint_usize_cvt(uint) { (types::I8, false) => u8::max_value() as i64,
U8 => u8::max_value() as i64, (types::I16, false) => u16::max_value() as i64,
U16 => u16::max_value() as i64, (types::I32, false) => u32::max_value() as i64,
U32 => u32::max_value() as i64, (types::I64, false) => u64::max_value() as i64,
U64 => u64::max_value() as i64, (types::I8, true) => i8::max_value() as i64,
U128 => unimplemented!(), (types::I16, true) => i16::max_value() as i64,
Usize => unreachable!(), (types::I32, true) => i32::max_value() as i64,
} (types::I64, true) => i64::max_value(),
ty::Int(int) => match int_isize_cvt(int) { (types::I128, _) => unimplemented!(),
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!(),
}
_ => unreachable!(), _ => unreachable!(),
}; };
(min, max) (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> { pub struct FunctionCx<'a, 'tcx: 'a, B: Backend> {
// FIXME use a reference to `CodegenCx` instead of `tcx`, `module` and `constants` and `caches` // FIXME use a reference to `CodegenCx` instead of `tcx`, `module` and `constants` and `caches`
pub tcx: TyCtxt<'tcx>, pub tcx: TyCtxt<'tcx>,

View File

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