]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/cast.rs
Auto merge of #81625 - jonas-schievink:rollup-mshpp2n, r=jonas-schievink
[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<'_, '_, impl Module>,
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         (_, types::I128) => {
18             let lo = if from == types::I64 {
19                 val
20             } else if signed {
21                 fx.bcx.ins().sextend(types::I64, val)
22             } else {
23                 fx.bcx.ins().uextend(types::I64, val)
24             };
25             let hi = if signed {
26                 fx.bcx.ins().sshr_imm(lo, 63)
27             } else {
28                 fx.bcx.ins().iconst(types::I64, 0)
29             };
30             fx.bcx.ins().iconcat(lo, hi)
31         }
32         (_, _) if to.wider_or_equal(from) => {
33             if signed {
34                 fx.bcx.ins().sextend(to, val)
35             } else {
36                 fx.bcx.ins().uextend(to, val)
37             }
38         }
39
40         // reduce
41         (types::I128, _) => {
42             let (lsb, _msb) = fx.bcx.ins().isplit(val);
43             if to == types::I64 {
44                 lsb
45             } else {
46                 fx.bcx.ins().ireduce(to, lsb)
47             }
48         }
49         (_, _) => fx.bcx.ins().ireduce(to, val),
50     }
51 }
52
53 pub(crate) fn clif_int_or_float_cast(
54     fx: &mut FunctionCx<'_, '_, impl Module>,
55     from: Value,
56     from_signed: bool,
57     to_ty: Type,
58     to_signed: bool,
59 ) -> Value {
60     let from_ty = fx.bcx.func.dfg.value_type(from);
61
62     if from_ty.is_int() && to_ty.is_int() {
63         // int-like -> int-like
64         clif_intcast(
65             fx,
66             from,
67             to_ty,
68             // This is correct as either from_signed == to_signed (=> this is trivially correct)
69             // Or from_clif_ty == to_clif_ty, which means this is a no-op.
70             from_signed,
71         )
72     } else if from_ty.is_int() && to_ty.is_float() {
73         if from_ty == types::I128 {
74             // _______ss__f_
75             // __float  tisf: i128 -> f32
76             // __float  tidf: i128 -> f64
77             // __floatuntisf: u128 -> f32
78             // __floatuntidf: u128 -> f64
79
80             let name = format!(
81                 "__float{sign}ti{flt}f",
82                 sign = if from_signed { "" } else { "un" },
83                 flt = match to_ty {
84                     types::F32 => "s",
85                     types::F64 => "d",
86                     _ => unreachable!("{:?}", to_ty),
87                 },
88             );
89
90             let from_rust_ty = if from_signed {
91                 fx.tcx.types.i128
92             } else {
93                 fx.tcx.types.u128
94             };
95
96             let to_rust_ty = match to_ty {
97                 types::F32 => fx.tcx.types.f32,
98                 types::F64 => fx.tcx.types.f64,
99                 _ => unreachable!(),
100             };
101
102             return fx
103                 .easy_call(
104                     &name,
105                     &[CValue::by_val(from, fx.layout_of(from_rust_ty))],
106                     to_rust_ty,
107                 )
108                 .load_scalar(fx);
109         }
110
111         // int-like -> float
112         if from_signed {
113             fx.bcx.ins().fcvt_from_sint(to_ty, from)
114         } else {
115             fx.bcx.ins().fcvt_from_uint(to_ty, from)
116         }
117     } else if from_ty.is_float() && to_ty.is_int() {
118         if to_ty == types::I128 {
119             // _____sssf___
120             // __fix   sfti: f32 -> i128
121             // __fix   dfti: f64 -> i128
122             // __fixunssfti: f32 -> u128
123             // __fixunsdfti: f64 -> u128
124
125             let name = format!(
126                 "__fix{sign}{flt}fti",
127                 sign = if to_signed { "" } else { "uns" },
128                 flt = match from_ty {
129                     types::F32 => "s",
130                     types::F64 => "d",
131                     _ => unreachable!("{:?}", to_ty),
132                 },
133             );
134
135             let from_rust_ty = match from_ty {
136                 types::F32 => fx.tcx.types.f32,
137                 types::F64 => fx.tcx.types.f64,
138                 _ => unreachable!(),
139             };
140
141             let to_rust_ty = if to_signed {
142                 fx.tcx.types.i128
143             } else {
144                 fx.tcx.types.u128
145             };
146
147             return fx
148                 .easy_call(
149                     &name,
150                     &[CValue::by_val(from, fx.layout_of(from_rust_ty))],
151                     to_rust_ty,
152                 )
153                 .load_scalar(fx);
154         }
155
156         // float -> int-like
157         if to_ty == types::I8 || to_ty == types::I16 {
158             // FIXME implement fcvt_to_*int_sat.i8/i16
159             let val = if to_signed {
160                 fx.bcx.ins().fcvt_to_sint_sat(types::I32, from)
161             } else {
162                 fx.bcx.ins().fcvt_to_uint_sat(types::I32, from)
163             };
164             let (min, max) = match (to_ty, to_signed) {
165                 (types::I8, false) => (0, i64::from(u8::MAX)),
166                 (types::I16, false) => (0, i64::from(u16::MAX)),
167                 (types::I8, true) => (i64::from(i8::MIN), i64::from(i8::MAX)),
168                 (types::I16, true) => (i64::from(i16::MIN), i64::from(i16::MAX)),
169                 _ => unreachable!(),
170             };
171             let min_val = fx.bcx.ins().iconst(types::I32, min);
172             let max_val = fx.bcx.ins().iconst(types::I32, max);
173
174             let val = if to_signed {
175                 let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, min);
176                 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, val, max);
177                 let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, val);
178                 fx.bcx.ins().select(has_overflow, max_val, bottom_capped)
179             } else {
180                 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, val, max);
181                 fx.bcx.ins().select(has_overflow, max_val, val)
182             };
183             fx.bcx.ins().ireduce(to_ty, val)
184         } else if to_signed {
185             fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
186         } else {
187             fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
188         }
189     } else if from_ty.is_float() && to_ty.is_float() {
190         // float -> float
191         match (from_ty, to_ty) {
192             (types::F32, types::F64) => fx.bcx.ins().fpromote(types::F64, from),
193             (types::F64, types::F32) => fx.bcx.ins().fdemote(types::F32, from),
194             _ => from,
195         }
196     } else {
197         unreachable!("cast value from {:?} to {:?}", from_ty, to_ty);
198     }
199 }