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