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