]> git.lizzy.rs Git - rust.git/blob - src/cast.rs
Merge commit '15c8d31392b9fbab3b3368b67acc4bbe5983115a' into cranelift-rebase
[rust.git] / 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         (_, 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 { lsb } else { fx.bcx.ins().ireduce(to, lsb) }
44         }
45         (_, _) => fx.bcx.ins().ireduce(to, val),
46     }
47 }
48
49 pub(crate) fn clif_int_or_float_cast(
50     fx: &mut FunctionCx<'_, '_, '_>,
51     from: Value,
52     from_signed: bool,
53     to_ty: Type,
54     to_signed: bool,
55 ) -> Value {
56     let from_ty = fx.bcx.func.dfg.value_type(from);
57
58     if from_ty.is_int() && to_ty.is_int() {
59         // int-like -> int-like
60         clif_intcast(
61             fx,
62             from,
63             to_ty,
64             // This is correct as either from_signed == to_signed (=> this is trivially correct)
65             // Or from_clif_ty == to_clif_ty, which means this is a no-op.
66             from_signed,
67         )
68     } else if from_ty.is_int() && to_ty.is_float() {
69         if from_ty == types::I128 {
70             // _______ss__f_
71             // __float  tisf: i128 -> f32
72             // __float  tidf: i128 -> f64
73             // __floatuntisf: u128 -> f32
74             // __floatuntidf: u128 -> f64
75
76             let name = format!(
77                 "__float{sign}ti{flt}f",
78                 sign = if from_signed { "" } else { "un" },
79                 flt = match to_ty {
80                     types::F32 => "s",
81                     types::F64 => "d",
82                     _ => unreachable!("{:?}", to_ty),
83                 },
84             );
85
86             let from_rust_ty = if from_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
87
88             let to_rust_ty = match to_ty {
89                 types::F32 => fx.tcx.types.f32,
90                 types::F64 => fx.tcx.types.f64,
91                 _ => unreachable!(),
92             };
93
94             return fx
95                 .easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty)
96                 .load_scalar(fx);
97         }
98
99         // int-like -> float
100         if from_signed {
101             fx.bcx.ins().fcvt_from_sint(to_ty, from)
102         } else {
103             fx.bcx.ins().fcvt_from_uint(to_ty, from)
104         }
105     } else if from_ty.is_float() && to_ty.is_int() {
106         if to_ty == types::I128 {
107             // _____sssf___
108             // __fix   sfti: f32 -> i128
109             // __fix   dfti: f64 -> i128
110             // __fixunssfti: f32 -> u128
111             // __fixunsdfti: f64 -> u128
112
113             let name = format!(
114                 "__fix{sign}{flt}fti",
115                 sign = if to_signed { "" } else { "uns" },
116                 flt = match from_ty {
117                     types::F32 => "s",
118                     types::F64 => "d",
119                     _ => unreachable!("{:?}", to_ty),
120                 },
121             );
122
123             let from_rust_ty = match from_ty {
124                 types::F32 => fx.tcx.types.f32,
125                 types::F64 => fx.tcx.types.f64,
126                 _ => unreachable!(),
127             };
128
129             let to_rust_ty = if to_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
130
131             return fx
132                 .easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty)
133                 .load_scalar(fx);
134         }
135
136         // float -> int-like
137         if to_ty == types::I8 || to_ty == types::I16 {
138             // FIXME implement fcvt_to_*int_sat.i8/i16
139             let val = if to_signed {
140                 fx.bcx.ins().fcvt_to_sint_sat(types::I32, from)
141             } else {
142                 fx.bcx.ins().fcvt_to_uint_sat(types::I32, from)
143             };
144             let (min, max) = match (to_ty, to_signed) {
145                 (types::I8, false) => (0, i64::from(u8::MAX)),
146                 (types::I16, false) => (0, i64::from(u16::MAX)),
147                 (types::I8, true) => (i64::from(i8::MIN), i64::from(i8::MAX)),
148                 (types::I16, true) => (i64::from(i16::MIN), i64::from(i16::MAX)),
149                 _ => unreachable!(),
150             };
151             let min_val = fx.bcx.ins().iconst(types::I32, min);
152             let max_val = fx.bcx.ins().iconst(types::I32, max);
153
154             let val = if to_signed {
155                 let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, min);
156                 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, val, max);
157                 let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, val);
158                 fx.bcx.ins().select(has_overflow, max_val, bottom_capped)
159             } else {
160                 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, val, max);
161                 fx.bcx.ins().select(has_overflow, max_val, val)
162             };
163             fx.bcx.ins().ireduce(to_ty, val)
164         } else if to_signed {
165             fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
166         } else {
167             fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
168         }
169     } else if from_ty.is_float() && to_ty.is_float() {
170         // float -> float
171         match (from_ty, to_ty) {
172             (types::F32, types::F64) => fx.bcx.ins().fpromote(types::F64, from),
173             (types::F64, types::F32) => fx.bcx.ins().fdemote(types::F32, from),
174             _ => from,
175         }
176     } else {
177         unreachable!("cast value from {:?} to {:?}", from_ty, to_ty);
178     }
179 }