]> git.lizzy.rs Git - rust.git/blob - src/intrinsics/simd.rs
Remove NullOp::Box
[rust.git] / src / intrinsics / simd.rs
1 //! Codegen `extern "platform-intrinsic"` intrinsics.
2
3 use super::*;
4 use crate::prelude::*;
5
6 pub(super) fn codegen_simd_intrinsic_call<'tcx>(
7     fx: &mut FunctionCx<'_, '_, 'tcx>,
8     instance: Instance<'tcx>,
9     args: &[mir::Operand<'tcx>],
10     ret: CPlace<'tcx>,
11     span: Span,
12 ) {
13     let def_id = instance.def_id();
14     let substs = instance.substs;
15
16     let intrinsic = fx.tcx.item_name(def_id);
17
18     intrinsic_match! {
19         fx, intrinsic, substs, args,
20         _ => {
21             fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
22         };
23
24         simd_cast, (c a) {
25             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
26             simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
27                 let ret_lane_ty = fx.clif_type(ret_lane_layout.ty).unwrap();
28
29                 let from_signed = type_sign(lane_layout.ty);
30                 let to_signed = type_sign(ret_lane_layout.ty);
31
32                 let ret_lane = clif_int_or_float_cast(fx, lane, from_signed, ret_lane_ty, to_signed);
33                 CValue::by_val(ret_lane, ret_lane_layout)
34             });
35         };
36
37         simd_eq, (c x, c y) {
38             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
39             simd_cmp!(fx, Equal|Equal(x, y) -> ret);
40         };
41         simd_ne, (c x, c y) {
42             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
43             simd_cmp!(fx, NotEqual|NotEqual(x, y) -> ret);
44         };
45         simd_lt, (c x, c y) {
46             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
47             simd_cmp!(fx, UnsignedLessThan|SignedLessThan|LessThan(x, y) -> ret);
48         };
49         simd_le, (c x, c y) {
50             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
51             simd_cmp!(fx, UnsignedLessThanOrEqual|SignedLessThanOrEqual|LessThanOrEqual(x, y) -> ret);
52         };
53         simd_gt, (c x, c y) {
54             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
55             simd_cmp!(fx, UnsignedGreaterThan|SignedGreaterThan|GreaterThan(x, y) -> ret);
56         };
57         simd_ge, (c x, c y) {
58             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
59             simd_cmp!(
60                 fx,
61                 UnsignedGreaterThanOrEqual|SignedGreaterThanOrEqual|GreaterThanOrEqual
62                 (x, y) -> ret
63             );
64         };
65
66         // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
67         _ if intrinsic.as_str().starts_with("simd_shuffle"), (c x, c y, o idx) {
68             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
69
70             let n: u16 = intrinsic.as_str()["simd_shuffle".len()..].parse().unwrap();
71
72             assert_eq!(x.layout(), y.layout());
73             let layout = x.layout();
74
75             let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
76             let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
77
78             assert_eq!(lane_ty, ret_lane_ty);
79             assert_eq!(u64::from(n), ret_lane_count);
80
81             let total_len = lane_count * 2;
82
83             let indexes = {
84                 use rustc_middle::mir::interpret::*;
85                 let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");
86
87                 let idx_bytes = match idx_const {
88                     ConstValue::ByRef { alloc, offset } => {
89                         let size = Size::from_bytes(4 * ret_lane_count /* size_of([u32; ret_lane_count]) */);
90                         alloc.get_bytes(fx, alloc_range(offset, size)).unwrap()
91                     }
92                     _ => unreachable!("{:?}", idx_const),
93                 };
94
95                 (0..ret_lane_count).map(|i| {
96                     let i = usize::try_from(i).unwrap();
97                     let idx = rustc_middle::mir::interpret::read_target_uint(
98                         fx.tcx.data_layout.endian,
99                         &idx_bytes[4*i.. 4*i + 4],
100                     ).expect("read_target_uint");
101                     u16::try_from(idx).expect("try_from u32")
102                 }).collect::<Vec<u16>>()
103             };
104
105             for &idx in &indexes {
106                 assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len);
107             }
108
109             for (out_idx, in_idx) in indexes.into_iter().enumerate() {
110                 let in_lane = if u64::from(in_idx) < lane_count {
111                     x.value_lane(fx, in_idx.into())
112                 } else {
113                     y.value_lane(fx, u64::from(in_idx) - lane_count)
114                 };
115                 let out_lane = ret.place_lane(fx, u64::try_from(out_idx).unwrap());
116                 out_lane.write_cvalue(fx, in_lane);
117             }
118         };
119
120         simd_insert, (c base, o idx, c val) {
121             // FIXME validate
122             let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
123                 idx_const
124             } else {
125                 fx.tcx.sess.span_fatal(
126                     span,
127                     "Index argument for `simd_insert` is not a constant",
128                 );
129             };
130
131             let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
132             let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
133             if idx >= lane_count.into() {
134                 fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
135             }
136
137             ret.write_cvalue(fx, base);
138             let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap()));
139             ret_lane.write_cvalue(fx, val);
140         };
141
142         simd_extract, (c v, o idx) {
143             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
144             let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
145                 idx_const
146             } else {
147                 fx.tcx.sess.span_warn(
148                     span,
149                     "Index argument for `simd_extract` is not a constant",
150                 );
151                 let res = crate::trap::trap_unimplemented_ret_value(
152                     fx,
153                     ret.layout(),
154                     "Index argument for `simd_extract` is not a constant",
155                 );
156                 ret.write_cvalue(fx, res);
157                 return;
158             };
159
160             let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
161             let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
162             if idx >= lane_count.into() {
163                 fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
164             }
165
166             let ret_lane = v.value_lane(fx, idx.try_into().unwrap());
167             ret.write_cvalue(fx, ret_lane);
168         };
169
170         simd_neg, (c a) {
171             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
172             simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
173                 let ret_lane = match lane_layout.ty.kind() {
174                     ty::Int(_) => fx.bcx.ins().ineg(lane),
175                     ty::Float(_) => fx.bcx.ins().fneg(lane),
176                     _ => unreachable!(),
177                 };
178                 CValue::by_val(ret_lane, ret_lane_layout)
179             });
180         };
181
182         simd_fabs, (c a) {
183             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
184             simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
185                 let ret_lane = fx.bcx.ins().fabs(lane);
186                 CValue::by_val(ret_lane, ret_lane_layout)
187             });
188         };
189
190         simd_fsqrt, (c a) {
191             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
192             simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
193                 let ret_lane = fx.bcx.ins().sqrt(lane);
194                 CValue::by_val(ret_lane, ret_lane_layout)
195             });
196         };
197
198         simd_add, (c x, c y) {
199             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
200             simd_int_flt_binop!(fx, iadd|fadd(x, y) -> ret);
201         };
202         simd_sub, (c x, c y) {
203             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
204             simd_int_flt_binop!(fx, isub|fsub(x, y) -> ret);
205         };
206         simd_mul, (c x, c y) {
207             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
208             simd_int_flt_binop!(fx, imul|fmul(x, y) -> ret);
209         };
210         simd_div, (c x, c y) {
211             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
212             simd_int_flt_binop!(fx, udiv|sdiv|fdiv(x, y) -> ret);
213         };
214         simd_rem, (c x, c y) {
215             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
216             simd_pair_for_each_lane(fx, x, y, ret, |fx, lane_layout, ret_lane_layout, x_lane, y_lane| {
217                 let res_lane = match lane_layout.ty.kind() {
218                     ty::Uint(_) => fx.bcx.ins().urem(x_lane, y_lane),
219                     ty::Int(_) => fx.bcx.ins().srem(x_lane, y_lane),
220                     ty::Float(FloatTy::F32) => fx.lib_call(
221                         "fmodf",
222                         vec![AbiParam::new(types::F32), AbiParam::new(types::F32)],
223                         vec![AbiParam::new(types::F32)],
224                         &[x_lane, y_lane],
225                     )[0],
226                     ty::Float(FloatTy::F64) => fx.lib_call(
227                         "fmod",
228                         vec![AbiParam::new(types::F64), AbiParam::new(types::F64)],
229                         vec![AbiParam::new(types::F64)],
230                         &[x_lane, y_lane],
231                     )[0],
232                     _ => unreachable!("{:?}", lane_layout.ty),
233                 };
234                 CValue::by_val(res_lane, ret_lane_layout)
235             });
236         };
237         simd_shl, (c x, c y) {
238             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
239             simd_int_binop!(fx, ishl(x, y) -> ret);
240         };
241         simd_shr, (c x, c y) {
242             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
243             simd_int_binop!(fx, ushr|sshr(x, y) -> ret);
244         };
245         simd_and, (c x, c y) {
246             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
247             simd_int_binop!(fx, band(x, y) -> ret);
248         };
249         simd_or, (c x, c y) {
250             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
251             simd_int_binop!(fx, bor(x, y) -> ret);
252         };
253         simd_xor, (c x, c y) {
254             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
255             simd_int_binop!(fx, bxor(x, y) -> ret);
256         };
257
258         simd_fma, (c a, c b, c c) {
259             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
260             assert_eq!(a.layout(), b.layout());
261             assert_eq!(a.layout(), c.layout());
262             let layout = a.layout();
263
264             let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
265             let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
266             assert_eq!(lane_count, ret_lane_count);
267             let ret_lane_layout = fx.layout_of(ret_lane_ty);
268
269             for lane in 0..lane_count {
270                 let a_lane = a.value_lane(fx, lane).load_scalar(fx);
271                 let b_lane = b.value_lane(fx, lane).load_scalar(fx);
272                 let c_lane = c.value_lane(fx, lane).load_scalar(fx);
273
274                 let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
275                 let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);
276
277                 ret.place_lane(fx, lane).write_cvalue(fx, res_lane);
278             }
279         };
280
281         simd_fmin, (c x, c y) {
282             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
283             simd_flt_binop!(fx, fmin(x, y) -> ret);
284         };
285         simd_fmax, (c x, c y) {
286             validate_simd_type!(fx, intrinsic, span, x.layout().ty);
287             simd_flt_binop!(fx, fmax(x, y) -> ret);
288         };
289
290         simd_round, (c a) {
291             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
292             simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
293                 let res_lane = match lane_layout.ty.kind() {
294                     ty::Float(FloatTy::F32) => fx.lib_call(
295                         "roundf",
296                         vec![AbiParam::new(types::F32)],
297                         vec![AbiParam::new(types::F32)],
298                         &[lane],
299                     )[0],
300                     ty::Float(FloatTy::F64) => fx.lib_call(
301                         "round",
302                         vec![AbiParam::new(types::F64)],
303                         vec![AbiParam::new(types::F64)],
304                         &[lane],
305                     )[0],
306                     _ => unreachable!("{:?}", lane_layout.ty),
307                 };
308                 CValue::by_val(res_lane, ret_lane_layout)
309             });
310         };
311         simd_ceil, (c a) {
312             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
313             simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
314                 let ret_lane = fx.bcx.ins().ceil(lane);
315                 CValue::by_val(ret_lane, ret_lane_layout)
316             });
317         };
318         simd_floor, (c a) {
319             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
320             simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
321                 let ret_lane = fx.bcx.ins().floor(lane);
322                 CValue::by_val(ret_lane, ret_lane_layout)
323             });
324         };
325         simd_trunc, (c a) {
326             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
327             simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
328                 let ret_lane = fx.bcx.ins().trunc(lane);
329                 CValue::by_val(ret_lane, ret_lane_layout)
330             });
331         };
332
333         simd_reduce_add_ordered | simd_reduce_add_unordered, (c v, v acc) {
334             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
335             simd_reduce(fx, v, Some(acc), ret, |fx, lane_layout, a, b| {
336                 if lane_layout.ty.is_floating_point() {
337                     fx.bcx.ins().fadd(a, b)
338                 } else {
339                     fx.bcx.ins().iadd(a, b)
340                 }
341             });
342         };
343
344         simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v, v acc) {
345             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
346             simd_reduce(fx, v, Some(acc), ret, |fx, lane_layout, a, b| {
347                 if lane_layout.ty.is_floating_point() {
348                     fx.bcx.ins().fmul(a, b)
349                 } else {
350                     fx.bcx.ins().imul(a, b)
351                 }
352             });
353         };
354
355         simd_reduce_all, (c v) {
356             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
357             simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().band(a, b));
358         };
359
360         simd_reduce_any, (c v) {
361             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
362             simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().bor(a, b));
363         };
364
365         simd_reduce_and, (c v) {
366             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
367             simd_reduce(fx, v, None, ret, |fx, _layout, a, b| fx.bcx.ins().band(a, b));
368         };
369
370         simd_reduce_or, (c v) {
371             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
372             simd_reduce(fx, v, None, ret, |fx, _layout, a, b| fx.bcx.ins().bor(a, b));
373         };
374
375         simd_reduce_xor, (c v) {
376             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
377             simd_reduce(fx, v, None, ret, |fx, _layout, a, b| fx.bcx.ins().bxor(a, b));
378         };
379
380         simd_reduce_min, (c v) {
381             // FIXME support floats
382             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
383             simd_reduce(fx, v, None, ret, |fx, layout, a, b| {
384                 let lt = fx.bcx.ins().icmp(if layout.ty.is_signed() {
385                     IntCC::SignedLessThan
386                 } else {
387                     IntCC::UnsignedLessThan
388                 }, a, b);
389                 fx.bcx.ins().select(lt, a, b)
390             });
391         };
392
393         simd_reduce_max, (c v) {
394             // FIXME support floats
395             validate_simd_type!(fx, intrinsic, span, v.layout().ty);
396             simd_reduce(fx, v, None, ret, |fx, layout, a, b| {
397                 let gt = fx.bcx.ins().icmp(if layout.ty.is_signed() {
398                     IntCC::SignedGreaterThan
399                 } else {
400                     IntCC::UnsignedGreaterThan
401                 }, a, b);
402                 fx.bcx.ins().select(gt, a, b)
403             });
404         };
405
406         simd_select, (c m, c a, c b) {
407             validate_simd_type!(fx, intrinsic, span, m.layout().ty);
408             validate_simd_type!(fx, intrinsic, span, a.layout().ty);
409             assert_eq!(a.layout(), b.layout());
410
411             let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx);
412             let lane_layout = fx.layout_of(lane_ty);
413
414             for lane in 0..lane_count {
415                 let m_lane = m.value_lane(fx, lane).load_scalar(fx);
416                 let a_lane = a.value_lane(fx, lane).load_scalar(fx);
417                 let b_lane = b.value_lane(fx, lane).load_scalar(fx);
418
419                 let m_lane = fx.bcx.ins().icmp_imm(IntCC::Equal, m_lane, 0);
420                 let res_lane = CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout);
421
422                 ret.place_lane(fx, lane).write_cvalue(fx, res_lane);
423             }
424         };
425
426         // simd_saturating_*
427         // simd_bitmask
428         // simd_scatter
429         // simd_gather
430     }
431 }