]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/cast.rs
Auto merge of #103881 - ChayimFriedman2:patch-2, r=compiler-errors
[rust.git] / compiler / rustc_codegen_cranelift / src / cast.rs
1 //! Various number casting functions
2
3 use crate::prelude::*;
4
5 pub(crate) fn clif_intcast(
6     fx: &mut FunctionCx<'_, '_, '_>,
7     val: Value,
8     to: Type,
9     signed: bool,
10 ) -> Value {
11     let from = fx.bcx.func.dfg.value_type(val);
12     match (from, to) {
13         // equal
14         (_, _) if from == to => val,
15
16         // extend
17         (_, _) if to.wider_or_equal(from) => {
18             if signed {
19                 fx.bcx.ins().sextend(to, val)
20             } else {
21                 fx.bcx.ins().uextend(to, val)
22             }
23         }
24
25         // reduce
26         (_, _) => fx.bcx.ins().ireduce(to, val),
27     }
28 }
29
30 pub(crate) fn clif_int_or_float_cast(
31     fx: &mut FunctionCx<'_, '_, '_>,
32     from: Value,
33     from_signed: bool,
34     to_ty: Type,
35     to_signed: bool,
36 ) -> Value {
37     let from_ty = fx.bcx.func.dfg.value_type(from);
38
39     if from_ty.is_int() && to_ty.is_int() {
40         // int-like -> int-like
41         clif_intcast(
42             fx,
43             from,
44             to_ty,
45             // This is correct as either from_signed == to_signed (=> this is trivially correct)
46             // Or from_clif_ty == to_clif_ty, which means this is a no-op.
47             from_signed,
48         )
49     } else if from_ty.is_int() && to_ty.is_float() {
50         if from_ty == types::I128 {
51             // _______ss__f_
52             // __float  tisf: i128 -> f32
53             // __float  tidf: i128 -> f64
54             // __floatuntisf: u128 -> f32
55             // __floatuntidf: u128 -> f64
56
57             let name = format!(
58                 "__float{sign}ti{flt}f",
59                 sign = if from_signed { "" } else { "un" },
60                 flt = match to_ty {
61                     types::F32 => "s",
62                     types::F64 => "d",
63                     _ => unreachable!("{:?}", to_ty),
64                 },
65             );
66
67             let from_rust_ty = if from_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
68
69             let to_rust_ty = match to_ty {
70                 types::F32 => fx.tcx.types.f32,
71                 types::F64 => fx.tcx.types.f64,
72                 _ => unreachable!(),
73             };
74
75             return fx
76                 .easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty)
77                 .load_scalar(fx);
78         }
79
80         // int-like -> float
81         if from_signed {
82             fx.bcx.ins().fcvt_from_sint(to_ty, from)
83         } else {
84             fx.bcx.ins().fcvt_from_uint(to_ty, from)
85         }
86     } else if from_ty.is_float() && to_ty.is_int() {
87         let val = if to_ty == types::I128 {
88             // _____sssf___
89             // __fix   sfti: f32 -> i128
90             // __fix   dfti: f64 -> i128
91             // __fixunssfti: f32 -> u128
92             // __fixunsdfti: f64 -> u128
93
94             let name = format!(
95                 "__fix{sign}{flt}fti",
96                 sign = if to_signed { "" } else { "uns" },
97                 flt = match from_ty {
98                     types::F32 => "s",
99                     types::F64 => "d",
100                     _ => unreachable!("{:?}", to_ty),
101                 },
102             );
103
104             let from_rust_ty = match from_ty {
105                 types::F32 => fx.tcx.types.f32,
106                 types::F64 => fx.tcx.types.f64,
107                 _ => unreachable!(),
108             };
109
110             let to_rust_ty = if to_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
111
112             fx.easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty)
113                 .load_scalar(fx)
114         } else if to_ty == types::I8 || to_ty == types::I16 {
115             // FIXME implement fcvt_to_*int_sat.i8/i16
116             let val = if to_signed {
117                 fx.bcx.ins().fcvt_to_sint_sat(types::I32, from)
118             } else {
119                 fx.bcx.ins().fcvt_to_uint_sat(types::I32, from)
120             };
121             let (min, max) = match (to_ty, to_signed) {
122                 (types::I8, false) => (0, i64::from(u8::MAX)),
123                 (types::I16, false) => (0, i64::from(u16::MAX)),
124                 (types::I8, true) => (i64::from(i8::MIN), i64::from(i8::MAX)),
125                 (types::I16, true) => (i64::from(i16::MIN), i64::from(i16::MAX)),
126                 _ => unreachable!(),
127             };
128             let min_val = fx.bcx.ins().iconst(types::I32, min);
129             let max_val = fx.bcx.ins().iconst(types::I32, max);
130
131             let val = if to_signed {
132                 let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, min);
133                 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, val, max);
134                 let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, val);
135                 fx.bcx.ins().select(has_overflow, max_val, bottom_capped)
136             } else {
137                 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, val, max);
138                 fx.bcx.ins().select(has_overflow, max_val, val)
139             };
140             fx.bcx.ins().ireduce(to_ty, val)
141         } else if to_signed {
142             fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
143         } else {
144             fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
145         };
146
147         if let Some(false) = fx.tcx.sess.opts.unstable_opts.saturating_float_casts {
148             return val;
149         }
150
151         let is_not_nan = fx.bcx.ins().fcmp(FloatCC::Equal, from, from);
152         let zero = type_zero_value(&mut fx.bcx, to_ty);
153         fx.bcx.ins().select(is_not_nan, val, zero)
154     } else if from_ty.is_float() && to_ty.is_float() {
155         // float -> float
156         match (from_ty, to_ty) {
157             (types::F32, types::F64) => fx.bcx.ins().fpromote(types::F64, from),
158             (types::F64, types::F32) => fx.bcx.ins().fdemote(types::F32, from),
159             _ => from,
160         }
161     } else {
162         unreachable!("cast value from {:?} to {:?}", from_ty, to_ty);
163     }
164 }