1 #[cfg(feature="master")]
2 use gccjit::{ComparisonOp, UnaryOp};
3 use gccjit::ToRValue;
4 use gccjit::{BinaryOp, RValue, Type};
5
6 use rustc_codegen_ssa::base::compare_simd_types;
7 use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
8 #[cfg(feature="master")]
9 use rustc_codegen_ssa::errors::ExpectedPointerMutability;
10 use rustc_codegen_ssa::errors::InvalidMonomorphization;
11 use rustc_codegen_ssa::mir::operand::OperandRef;
12 use rustc_codegen_ssa::mir::place::PlaceRef;
13 use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods};
14 use rustc_hir as hir;
15 use rustc_middle::span_bug;
16 use rustc_middle::ty::layout::HasTyCtxt;
17 use rustc_middle::ty::{self, Ty};
18 use rustc_span::{sym, Span, Symbol};
19 use rustc_target::abi::Align;
20
21 use crate::builder::Builder;
22 #[cfg(feature="master")]
23 use crate::context::CodegenCx;
24 #[cfg(feature="master")]
25 use crate::errors::{InvalidMonomorphizationExpectedSignedUnsigned, InvalidMonomorphizationInsertedType};
26 use crate::errors::{
27 InvalidMonomorphizationExpectedSimd,
28 InvalidMonomorphizationInvalidBitmask,
29 InvalidMonomorphizationInvalidFloatVector, InvalidMonomorphizationMaskType,
30 InvalidMonomorphizationMismatchedLengths, InvalidMonomorphizationNotFloat,
31 InvalidMonomorphizationReturnElement, InvalidMonomorphizationReturnIntegerType,
32 InvalidMonomorphizationReturnLength, InvalidMonomorphizationReturnLengthInputType,
33 InvalidMonomorphizationReturnType, InvalidMonomorphizationSimdShuffle,
34 InvalidMonomorphizationUnrecognized, InvalidMonomorphizationUnsupportedElement,
35 InvalidMonomorphizationUnsupportedOperation,
36 };
37
generic_simd_intrinsic<'a, 'gcc, 'tcx>( bx: &mut Builder<'a, 'gcc, 'tcx>, name: Symbol, callee_ty: Ty<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span, ) -> Result<RValue<'gcc>, ()>38 pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
39 bx: &mut Builder<'a, 'gcc, 'tcx>,
40 name: Symbol,
41 callee_ty: Ty<'tcx>,
42 args: &[OperandRef<'tcx, RValue<'gcc>>],
43 ret_ty: Ty<'tcx>,
44 llret_ty: Type<'gcc>,
45 span: Span,
46 ) -> Result<RValue<'gcc>, ()> {
47 // macros for error handling:
48 macro_rules! return_error {
49 ($err:expr) => {{
50 bx.sess().emit_err($err);
51 return Err(());
52 }};
53 }
54 macro_rules! require {
55 ($cond:expr, $err:expr) => {
56 if !$cond {
57 return_error!($err);
58 }
59 };
60 }
61 macro_rules! require_simd {
62 ($ty: expr, $position: expr) => {
63 require!(
64 $ty.is_simd(),
65 InvalidMonomorphizationExpectedSimd {
66 span,
67 name,
68 position: $position,
69 found_ty: $ty
70 }
71 )
72 };
73 }
74
75 let tcx = bx.tcx();
76 let sig =
77 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), callee_ty.fn_sig(tcx));
78 let arg_tys = sig.inputs();
79
80 if name == sym::simd_select_bitmask {
81 require_simd!(arg_tys[1], "argument");
82 let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
83
84 let expected_int_bits = (len.max(8) - 1).next_power_of_two();
85 let expected_bytes = len / 8 + ((len % 8 > 0) as u64);
86
87 let mask_ty = arg_tys[0];
88 let mut mask = match mask_ty.kind() {
89 ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
90 ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
91 ty::Array(elem, len)
92 if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
93 && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all())
94 == Some(expected_bytes) =>
95 {
96 let place = PlaceRef::alloca(bx, args[0].layout);
97 args[0].val.store(bx, place);
98 let int_ty = bx.type_ix(expected_bytes * 8);
99 let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty));
100 bx.load(int_ty, ptr, Align::ONE)
101 }
102 _ => return_error!(InvalidMonomorphizationInvalidBitmask {
103 span,
104 name,
105 ty: mask_ty,
106 expected_int_bits,
107 expected_bytes
108 }),
109 };
110
111 let arg1 = args[1].immediate();
112 let arg1_type = arg1.get_type();
113 let arg1_vector_type = arg1_type.unqualified().dyncast_vector().expect("vector type");
114 let arg1_element_type = arg1_vector_type.get_element_type();
115
116 // NOTE: since the arguments can be vectors of floats, make sure the mask is a vector of
117 // integer.
118 let mask_element_type = bx.type_ix(arg1_element_type.get_size() as u64 * 8);
119 let vector_mask_type = bx.context.new_vector_type(mask_element_type, arg1_vector_type.get_num_units() as u64);
120
121 let mut elements = vec![];
122 let one = bx.context.new_rvalue_one(mask.get_type());
123 for _ in 0..len {
124 let element = bx.context.new_cast(None, mask & one, mask_element_type);
125 elements.push(element);
126 mask = mask >> one;
127 }
128 let vector_mask = bx.context.new_rvalue_from_vector(None, vector_mask_type, &elements);
129
130 return Ok(bx.vector_select(vector_mask, arg1, args[2].immediate()));
131 }
132
133 // every intrinsic below takes a SIMD vector as its first argument
134 require_simd!(arg_tys[0], "input");
135 let in_ty = arg_tys[0];
136
137 let comparison = match name {
138 sym::simd_eq => Some(hir::BinOpKind::Eq),
139 sym::simd_ne => Some(hir::BinOpKind::Ne),
140 sym::simd_lt => Some(hir::BinOpKind::Lt),
141 sym::simd_le => Some(hir::BinOpKind::Le),
142 sym::simd_gt => Some(hir::BinOpKind::Gt),
143 sym::simd_ge => Some(hir::BinOpKind::Ge),
144 _ => None,
145 };
146
147 let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx());
148 if let Some(cmp_op) = comparison {
149 require_simd!(ret_ty, "return");
150
151 let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
152 require!(
153 in_len == out_len,
154 InvalidMonomorphizationReturnLengthInputType {
155 span,
156 name,
157 in_len,
158 in_ty,
159 ret_ty,
160 out_len
161 }
162 );
163 require!(
164 bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
165 InvalidMonomorphizationReturnIntegerType { span, name, ret_ty, out_ty }
166 );
167
168 let arg1 = args[0].immediate();
169 // NOTE: we get different vector types for the same vector type and libgccjit doesn't
170 // compare them as equal, so bitcast.
171 // FIXME(antoyo): allow comparing vector types as equal in libgccjit.
172 let arg2 = bx.context.new_bitcast(None, args[1].immediate(), arg1.get_type());
173 return Ok(compare_simd_types(
174 bx,
175 arg1,
176 arg2,
177 in_elem,
178 llret_ty,
179 cmp_op,
180 ));
181 }
182
183 if let Some(stripped) = name.as_str().strip_prefix("simd_shuffle") {
184 let n: u64 = if stripped.is_empty() {
185 // Make sure this is actually an array, since typeck only checks the length-suffixed
186 // version of this intrinsic.
187 match args[2].layout.ty.kind() {
188 ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => {
189 len.try_eval_target_usize(bx.cx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else(
190 || span_bug!(span, "could not evaluate shuffle index array length"),
191 )
192 }
193 _ => return_error!(InvalidMonomorphizationSimdShuffle {
194 span,
195 name,
196 ty: args[2].layout.ty
197 }),
198 }
199 } else {
200 stripped.parse().unwrap_or_else(|_| {
201 span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?")
202 })
203 };
204
205 require_simd!(ret_ty, "return");
206
207 let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
208 require!(
209 out_len == n,
210 InvalidMonomorphizationReturnLength { span, name, in_len: n, ret_ty, out_len }
211 );
212 require!(
213 in_elem == out_ty,
214 InvalidMonomorphizationReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
215 );
216
217 let vector = args[2].immediate();
218
219 return Ok(bx.shuffle_vector(args[0].immediate(), args[1].immediate(), vector));
220 }
221
222 #[cfg(feature = "master")]
223 if name == sym::simd_insert {
224 require!(
225 in_elem == arg_tys[2],
226 InvalidMonomorphizationInsertedType { span, name, in_elem, in_ty, out_ty: arg_tys[2] }
227 );
228 let vector = args[0].immediate();
229 let index = args[1].immediate();
230 let value = args[2].immediate();
231 let variable = bx.current_func().new_local(None, vector.get_type(), "new_vector");
232 bx.llbb().add_assignment(None, variable, vector);
233 let lvalue = bx.context.new_vector_access(None, variable.to_rvalue(), index);
234 // TODO(antoyo): if simd_insert is constant, use BIT_REF.
235 bx.llbb().add_assignment(None, lvalue, value);
236 return Ok(variable.to_rvalue());
237 }
238
239 #[cfg(feature = "master")]
240 if name == sym::simd_extract {
241 require!(
242 ret_ty == in_elem,
243 InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
244 );
245 let vector = args[0].immediate();
246 return Ok(bx.context.new_vector_access(None, vector, args[1].immediate()).to_rvalue());
247 }
248
249 if name == sym::simd_select {
250 let m_elem_ty = in_elem;
251 let m_len = in_len;
252 require_simd!(arg_tys[1], "argument");
253 let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
254 require!(
255 m_len == v_len,
256 InvalidMonomorphizationMismatchedLengths { span, name, m_len, v_len }
257 );
258 match m_elem_ty.kind() {
259 ty::Int(_) => {}
260 _ => return_error!(InvalidMonomorphizationMaskType { span, name, ty: m_elem_ty }),
261 }
262 return Ok(bx.vector_select(args[0].immediate(), args[1].immediate(), args[2].immediate()));
263 }
264
265 #[cfg(feature="master")]
266 if name == sym::simd_cast || name == sym::simd_as {
267 require_simd!(ret_ty, "return");
268 let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx());
269 require!(
270 in_len == out_len,
271 InvalidMonomorphizationReturnLengthInputType {
272 span,
273 name,
274 in_len,
275 in_ty,
276 ret_ty,
277 out_len
278 }
279 );
280 // casting cares about nominal type, not just structural type
281 if in_elem == out_elem {
282 return Ok(args[0].immediate());
283 }
284
285 enum Style {
286 Float,
287 Int,
288 Unsupported,
289 }
290
291 let in_style =
292 match in_elem.kind() {
293 ty::Int(_) | ty::Uint(_) => Style::Int,
294 ty::Float(_) => Style::Float,
295 _ => Style::Unsupported,
296 };
297
298 let out_style =
299 match out_elem.kind() {
300 ty::Int(_) | ty::Uint(_) => Style::Int,
301 ty::Float(_) => Style::Float,
302 _ => Style::Unsupported,
303 };
304
305 match (in_style, out_style) {
306 (Style::Unsupported, Style::Unsupported) => {
307 require!(
308 false,
309 InvalidMonomorphization::UnsupportedCast {
310 span,
311 name,
312 in_ty,
313 in_elem,
314 ret_ty,
315 out_elem
316 }
317 );
318 },
319 _ => return Ok(bx.context.convert_vector(None, args[0].immediate(), llret_ty)),
320 }
321 }
322
323 macro_rules! arith_binary {
324 ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
325 $(if name == sym::$name {
326 match in_elem.kind() {
327 $($(ty::$p(_))|* => {
328 return Ok(bx.$call(args[0].immediate(), args[1].immediate()))
329 })*
330 _ => {},
331 }
332 return_error!(InvalidMonomorphizationUnsupportedOperation { span, name, in_ty, in_elem })
333 })*
334 }
335 }
336
337 if name == sym::simd_bitmask {
338 // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a
339 // vector mask and returns the most significant bit (MSB) of each lane in the form
340 // of either:
341 // * an unsigned integer
342 // * an array of `u8`
343 // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits.
344 //
345 // The bit order of the result depends on the byte endianness, LSB-first for little
346 // endian and MSB-first for big endian.
347
348 let vector = args[0].immediate();
349 // TODO(antoyo): dyncast_vector should not require a call to unqualified.
350 let vector_type = vector.get_type().unqualified().dyncast_vector().expect("vector type");
351 let elem_type = vector_type.get_element_type();
352
353 let expected_int_bits = in_len.max(8);
354 let expected_bytes = expected_int_bits / 8 + ((expected_int_bits % 8 > 0) as u64);
355
356 // FIXME(antoyo): that's not going to work for masks bigger than 128 bits.
357 let result_type = bx.type_ix(expected_int_bits);
358 let mut result = bx.context.new_rvalue_zero(result_type);
359
360 let elem_size = elem_type.get_size() * 8;
361 let sign_shift = bx.context.new_rvalue_from_int(elem_type, elem_size as i32 - 1);
362 let one = bx.context.new_rvalue_one(elem_type);
363
364 let mut shift = 0;
365 for i in 0..in_len {
366 let elem = bx.extract_element(vector, bx.context.new_rvalue_from_int(bx.int_type, i as i32));
367 let shifted = elem >> sign_shift;
368 let masked = shifted & one;
369 result = result | (bx.context.new_cast(None, masked, result_type) << bx.context.new_rvalue_from_int(result_type, shift));
370 shift += 1;
371 }
372
373 match ret_ty.kind() {
374 ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {
375 // Zero-extend iN to the bitmask type:
376 return Ok(result);
377 }
378 ty::Array(elem, len)
379 if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
380 && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all())
381 == Some(expected_bytes) =>
382 {
383 // Zero-extend iN to the array length:
384 let ze = bx.zext(result, bx.type_ix(expected_bytes * 8));
385
386 // Convert the integer to a byte array
387 let ptr = bx.alloca(bx.type_ix(expected_bytes * 8), Align::ONE);
388 bx.store(ze, ptr, Align::ONE);
389 let array_ty = bx.type_array(bx.type_i8(), expected_bytes);
390 let ptr = bx.pointercast(ptr, bx.cx.type_ptr_to(array_ty));
391 return Ok(bx.load(array_ty, ptr, Align::ONE));
392 }
393 _ => return_error!(InvalidMonomorphization::CannotReturn {
394 span,
395 name,
396 ret_ty,
397 expected_int_bits,
398 expected_bytes
399 }),
400 }
401 }
402
403 fn simd_simple_float_intrinsic<'gcc, 'tcx>(
404 name: Symbol,
405 in_elem: Ty<'_>,
406 in_ty: Ty<'_>,
407 in_len: u64,
408 bx: &mut Builder<'_, 'gcc, 'tcx>,
409 span: Span,
410 args: &[OperandRef<'tcx, RValue<'gcc>>],
411 ) -> Result<RValue<'gcc>, ()> {
412 macro_rules! return_error {
413 ($err:expr) => {{
414 bx.sess().emit_err($err);
415 return Err(());
416 }};
417 }
418 let (elem_ty_str, elem_ty) =
419 if let ty::Float(f) = in_elem.kind() {
420 let elem_ty = bx.cx.type_float_from_ty(*f);
421 match f.bit_width() {
422 32 => ("f", elem_ty),
423 64 => ("", elem_ty),
424 _ => {
425 return_error!(InvalidMonomorphizationInvalidFloatVector { span, name, elem_ty: f.name_str(), vec_ty: in_ty });
426 }
427 }
428 }
429 else {
430 return_error!(InvalidMonomorphizationNotFloat { span, name, ty: in_ty });
431 };
432
433 let vec_ty = bx.cx.type_vector(elem_ty, in_len);
434
435 let intr_name =
436 match name {
437 sym::simd_ceil => "ceil",
438 sym::simd_fabs => "fabs", // TODO(antoyo): pand with 170141183420855150465331762880109871103
439 sym::simd_fcos => "cos",
440 sym::simd_fexp2 => "exp2",
441 sym::simd_fexp => "exp",
442 sym::simd_flog10 => "log10",
443 sym::simd_flog2 => "log2",
444 sym::simd_flog => "log",
445 sym::simd_floor => "floor",
446 sym::simd_fma => "fma",
447 sym::simd_fpowi => "__builtin_powi",
448 sym::simd_fpow => "pow",
449 sym::simd_fsin => "sin",
450 sym::simd_fsqrt => "sqrt",
451 sym::simd_round => "round",
452 sym::simd_trunc => "trunc",
453 _ => return_error!(InvalidMonomorphizationUnrecognized { span, name })
454 };
455 let builtin_name = format!("{}{}", intr_name, elem_ty_str);
456 let funcs = bx.cx.functions.borrow();
457 let function = funcs.get(&builtin_name).unwrap_or_else(|| panic!("unable to find builtin function {}", builtin_name));
458
459 // TODO(antoyo): add platform-specific behavior here for architectures that have these
460 // intrinsics as instructions (for instance, gpus)
461 let mut vector_elements = vec![];
462 for i in 0..in_len {
463 let index = bx.context.new_rvalue_from_long(bx.ulong_type, i as i64);
464 // we have to treat fpowi specially, since fpowi's second argument is always an i32
465 let arguments = if name == sym::simd_fpowi {
466 vec![
467 bx.extract_element(args[0].immediate(), index).to_rvalue(),
468 args[1].immediate(),
469 ]
470 } else {
471 args.iter()
472 .map(|arg| bx.extract_element(arg.immediate(), index).to_rvalue())
473 .collect()
474 };
475 vector_elements.push(bx.context.new_call(None, *function, &arguments));
476 }
477 let c = bx.context.new_rvalue_from_vector(None, vec_ty, &vector_elements);
478 Ok(c)
479 }
480
481 if std::matches!(
482 name,
483 sym::simd_ceil
484 | sym::simd_fabs
485 | sym::simd_fcos
486 | sym::simd_fexp2
487 | sym::simd_fexp
488 | sym::simd_flog10
489 | sym::simd_flog2
490 | sym::simd_flog
491 | sym::simd_floor
492 | sym::simd_fma
493 | sym::simd_fpow
494 | sym::simd_fpowi
495 | sym::simd_fsin
496 | sym::simd_fsqrt
497 | sym::simd_round
498 | sym::simd_trunc
499 ) {
500 return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args);
501 }
502
503 #[cfg(feature="master")]
504 fn vector_ty<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, elem_ty: Ty<'tcx>, vec_len: u64) -> Type<'gcc> {
505 // FIXME: use cx.layout_of(ty).llvm_type() ?
506 let elem_ty = match *elem_ty.kind() {
507 ty::Int(v) => cx.type_int_from_ty(v),
508 ty::Uint(v) => cx.type_uint_from_ty(v),
509 ty::Float(v) => cx.type_float_from_ty(v),
510 _ => unreachable!(),
511 };
512 cx.type_vector(elem_ty, vec_len)
513 }
514
515 #[cfg(feature="master")]
516 fn gather<'a, 'gcc, 'tcx>(default: RValue<'gcc>, pointers: RValue<'gcc>, mask: RValue<'gcc>, pointer_count: usize, bx: &mut Builder<'a, 'gcc, 'tcx>, in_len: u64, underlying_ty: Ty<'tcx>, invert: bool) -> RValue<'gcc> {
517 let vector_type =
518 if pointer_count > 1 {
519 bx.context.new_vector_type(bx.usize_type, in_len)
520 }
521 else {
522 vector_ty(bx, underlying_ty, in_len)
523 };
524 let elem_type = vector_type.dyncast_vector().expect("vector type").get_element_type();
525
526 let mut values = vec![];
527 for i in 0..in_len {
528 let index = bx.context.new_rvalue_from_long(bx.i32_type, i as i64);
529 let int = bx.context.new_vector_access(None, pointers, index).to_rvalue();
530
531 let ptr_type = elem_type.make_pointer();
532 let ptr = bx.context.new_bitcast(None, int, ptr_type);
533 let value = ptr.dereference(None).to_rvalue();
534 values.push(value);
535 }
536
537 let vector = bx.context.new_rvalue_from_vector(None, vector_type, &values);
538
539 let mut mask_types = vec![];
540 let mut mask_values = vec![];
541 for i in 0..in_len {
542 let index = bx.context.new_rvalue_from_long(bx.i32_type, i as i64);
543 mask_types.push(bx.context.new_field(None, bx.i32_type, "m"));
544 let mask_value = bx.context.new_vector_access(None, mask, index).to_rvalue();
545 let masked = bx.context.new_rvalue_from_int(bx.i32_type, in_len as i32) & mask_value;
546 let value = index + masked;
547 mask_values.push(value);
548 }
549 let mask_type = bx.context.new_struct_type(None, "mask_type", &mask_types);
550 let mask = bx.context.new_struct_constructor(None, mask_type.as_type(), None, &mask_values);
551
552 if invert {
553 bx.shuffle_vector(vector, default, mask)
554 }
555 else {
556 bx.shuffle_vector(default, vector, mask)
557 }
558 }
559
560 #[cfg(feature="master")]
561 if name == sym::simd_gather {
562 // simd_gather(values: <N x T>, pointers: <N x *_ T>,
563 // mask: <N x i{M}>) -> <N x T>
564 // * N: number of elements in the input vectors
565 // * T: type of the element to load
566 // * M: any integer width is supported, will be truncated to i1
567
568 // All types must be simd vector types
569 require_simd!(in_ty, "first");
570 require_simd!(arg_tys[1], "second");
571 require_simd!(arg_tys[2], "third");
572 require_simd!(ret_ty, "return");
573
574 // Of the same length:
575 let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
576 let (out_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx());
577 require!(
578 in_len == out_len,
579 InvalidMonomorphization::SecondArgumentLength {
580 span,
581 name,
582 in_len,
583 in_ty,
584 arg_ty: arg_tys[1],
585 out_len
586 }
587 );
588 require!(
589 in_len == out_len2,
590 InvalidMonomorphization::ThirdArgumentLength {
591 span,
592 name,
593 in_len,
594 in_ty,
595 arg_ty: arg_tys[2],
596 out_len: out_len2
597 }
598 );
599
600 // The return type must match the first argument type
601 require!(
602 ret_ty == in_ty,
603 InvalidMonomorphization::ExpectedReturnType { span, name, in_ty, ret_ty }
604 );
605
606 // This counts how many pointers
607 fn ptr_count(t: Ty<'_>) -> usize {
608 match t.kind() {
609 ty::RawPtr(p) => 1 + ptr_count(p.ty),
610 _ => 0,
611 }
612 }
613
614 // Non-ptr type
615 fn non_ptr(t: Ty<'_>) -> Ty<'_> {
616 match t.kind() {
617 ty::RawPtr(p) => non_ptr(p.ty),
618 _ => t,
619 }
620 }
621
622 // The second argument must be a simd vector with an element type that's a pointer
623 // to the element type of the first argument
624 let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx());
625 let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx());
626 let (pointer_count, underlying_ty) = match element_ty1.kind() {
627 ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)),
628 _ => {
629 require!(
630 false,
631 InvalidMonomorphization::ExpectedElementType {
632 span,
633 name,
634 expected_element: element_ty1,
635 second_arg: arg_tys[1],
636 in_elem,
637 in_ty,
638 mutability: ExpectedPointerMutability::Not,
639 }
640 );
641 unreachable!();
642 }
643 };
644 assert!(pointer_count > 0);
645 assert_eq!(pointer_count - 1, ptr_count(element_ty0));
646 assert_eq!(underlying_ty, non_ptr(element_ty0));
647
648 // The element type of the third argument must be a signed integer type of any width:
649 let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx());
650 match element_ty2.kind() {
651 ty::Int(_) => (),
652 _ => {
653 require!(
654 false,
655 InvalidMonomorphization::ThirdArgElementType {
656 span,
657 name,
658 expected_element: element_ty2,
659 third_arg: arg_tys[2]
660 }
661 );
662 }
663 }
664
665 return Ok(gather(args[0].immediate(), args[1].immediate(), args[2].immediate(), pointer_count, bx, in_len, underlying_ty, false));
666 }
667
668 #[cfg(feature="master")]
669 if name == sym::simd_scatter {
670 // simd_scatter(values: <N x T>, pointers: <N x *mut T>,
671 // mask: <N x i{M}>) -> ()
672 // * N: number of elements in the input vectors
673 // * T: type of the element to load
674 // * M: any integer width is supported, will be truncated to i1
675
676 // All types must be simd vector types
677 require_simd!(in_ty, "first");
678 require_simd!(arg_tys[1], "second");
679 require_simd!(arg_tys[2], "third");
680
681 // Of the same length:
682 let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx());
683 let (element_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx());
684 require!(
685 in_len == element_len1,
686 InvalidMonomorphization::SecondArgumentLength {
687 span,
688 name,
689 in_len,
690 in_ty,
691 arg_ty: arg_tys[1],
692 out_len: element_len1
693 }
694 );
695 require!(
696 in_len == element_len2,
697 InvalidMonomorphization::ThirdArgumentLength {
698 span,
699 name,
700 in_len,
701 in_ty,
702 arg_ty: arg_tys[2],
703 out_len: element_len2
704 }
705 );
706
707 // This counts how many pointers
708 fn ptr_count(t: Ty<'_>) -> usize {
709 match t.kind() {
710 ty::RawPtr(p) => 1 + ptr_count(p.ty),
711 _ => 0,
712 }
713 }
714
715 // Non-ptr type
716 fn non_ptr(t: Ty<'_>) -> Ty<'_> {
717 match t.kind() {
718 ty::RawPtr(p) => non_ptr(p.ty),
719 _ => t,
720 }
721 }
722
723 // The second argument must be a simd vector with an element type that's a pointer
724 // to the element type of the first argument
725 let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx());
726 let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx());
727 let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx());
728 let (pointer_count, underlying_ty) = match element_ty1.kind() {
729 ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => {
730 (ptr_count(element_ty1), non_ptr(element_ty1))
731 }
732 _ => {
733 require!(
734 false,
735 InvalidMonomorphization::ExpectedElementType {
736 span,
737 name,
738 expected_element: element_ty1,
739 second_arg: arg_tys[1],
740 in_elem,
741 in_ty,
742 mutability: ExpectedPointerMutability::Mut,
743 }
744 );
745 unreachable!();
746 }
747 };
748 assert!(pointer_count > 0);
749 assert_eq!(pointer_count - 1, ptr_count(element_ty0));
750 assert_eq!(underlying_ty, non_ptr(element_ty0));
751
752 // The element type of the third argument must be a signed integer type of any width:
753 match element_ty2.kind() {
754 ty::Int(_) => (),
755 _ => {
756 require!(
757 false,
758 InvalidMonomorphization::ThirdArgElementType {
759 span,
760 name,
761 expected_element: element_ty2,
762 third_arg: arg_tys[2]
763 }
764 );
765 }
766 }
767
768 let result = gather(args[0].immediate(), args[1].immediate(), args[2].immediate(), pointer_count, bx, in_len, underlying_ty, true);
769
770 let pointers = args[1].immediate();
771
772 let vector_type =
773 if pointer_count > 1 {
774 bx.context.new_vector_type(bx.usize_type, in_len)
775 }
776 else {
777 vector_ty(bx, underlying_ty, in_len)
778 };
779 let elem_type = vector_type.dyncast_vector().expect("vector type").get_element_type();
780
781 for i in 0..in_len {
782 let index = bx.context.new_rvalue_from_int(bx.int_type, i as i32);
783 let value = bx.context.new_vector_access(None, result, index);
784
785 let int = bx.context.new_vector_access(None, pointers, index).to_rvalue();
786 let ptr_type = elem_type.make_pointer();
787 let ptr = bx.context.new_bitcast(None, int, ptr_type);
788 bx.llbb().add_assignment(None, ptr.dereference(None), value);
789 }
790
791 return Ok(bx.context.new_rvalue_zero(bx.i32_type));
792 }
793
794 arith_binary! {
795 simd_add: Uint, Int => add, Float => fadd;
796 simd_sub: Uint, Int => sub, Float => fsub;
797 simd_mul: Uint, Int => mul, Float => fmul;
798 simd_div: Uint => udiv, Int => sdiv, Float => fdiv;
799 simd_rem: Uint => urem, Int => srem, Float => frem;
800 simd_shl: Uint, Int => shl;
801 simd_shr: Uint => lshr, Int => ashr;
802 simd_and: Uint, Int => and;
803 simd_or: Uint, Int => or; // FIXME(antoyo): calling `or` might not work on vectors.
804 simd_xor: Uint, Int => xor;
805 simd_fmin: Float => vector_fmin;
806 simd_fmax: Float => vector_fmax;
807 }
808
809 macro_rules! arith_unary {
810 ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
811 $(if name == sym::$name {
812 match in_elem.kind() {
813 $($(ty::$p(_))|* => {
814 return Ok(bx.$call(args[0].immediate()))
815 })*
816 _ => {},
817 }
818 return_error!(InvalidMonomorphizationUnsupportedOperation { span, name, in_ty, in_elem })
819 })*
820 }
821 }
822
823 arith_unary! {
824 simd_neg: Int => neg, Float => fneg;
825 }
826
827 #[cfg(feature = "master")]
828 if name == sym::simd_saturating_add || name == sym::simd_saturating_sub {
829 let lhs = args[0].immediate();
830 let rhs = args[1].immediate();
831 let is_add = name == sym::simd_saturating_add;
832 let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _;
833 let (signed, elem_width, elem_ty) =
834 match *in_elem.kind() {
835 ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_int_from_ty(i)),
836 ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_uint_from_ty(i)),
837 _ => {
838 return_error!(InvalidMonomorphizationExpectedSignedUnsigned {
839 span,
840 name,
841 elem_ty: arg_tys[0].simd_size_and_type(bx.tcx()).1,
842 vec_ty: arg_tys[0],
843 });
844 }
845 };
846
847 let result =
848 match (signed, is_add) {
849 (false, true) => {
850 let res = lhs + rhs;
851 let cmp = bx.context.new_comparison(None, ComparisonOp::LessThan, res, lhs);
852 res | cmp
853 },
854 (true, true) => {
855 // Algorithm from: https://codereview.stackexchange.com/questions/115869/saturated-signed-addition
856 // TODO(antoyo): improve using conditional operators if possible.
857 // TODO(antoyo): dyncast_vector should not require a call to unqualified.
858 let arg_type = lhs.get_type().unqualified();
859 // TODO(antoyo): convert lhs and rhs to unsigned.
860 let sum = lhs + rhs;
861 let vector_type = arg_type.dyncast_vector().expect("vector type");
862 let unit = vector_type.get_num_units();
863 let a = bx.context.new_rvalue_from_int(elem_ty, ((elem_width as i32) << 3) - 1);
864 let width = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![a; unit]);
865
866 let xor1 = lhs ^ rhs;
867 let xor2 = lhs ^ sum;
868 let and = bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, xor1) & xor2;
869 let mask = and >> width;
870
871 let one = bx.context.new_rvalue_one(elem_ty);
872 let ones = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![one; unit]);
873 let shift1 = ones << width;
874 let shift2 = sum >> width;
875 let mask_min = shift1 ^ shift2;
876
877 let and1 = bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, mask) & sum;
878 let and2 = mask & mask_min;
879
880 and1 + and2
881 },
882 (false, false) => {
883 let res = lhs - rhs;
884 let cmp = bx.context.new_comparison(None, ComparisonOp::LessThanEquals, res, lhs);
885 res & cmp
886 },
887 (true, false) => {
888 // TODO(antoyo): dyncast_vector should not require a call to unqualified.
889 let arg_type = lhs.get_type().unqualified();
890 // TODO(antoyo): this uses the same algorithm from saturating add, but add the
891 // negative of the right operand. Find a proper subtraction algorithm.
892 let rhs = bx.context.new_unary_op(None, UnaryOp::Minus, arg_type, rhs);
893
894 // TODO(antoyo): convert lhs and rhs to unsigned.
895 let sum = lhs + rhs;
896 let vector_type = arg_type.dyncast_vector().expect("vector type");
897 let unit = vector_type.get_num_units();
898 let a = bx.context.new_rvalue_from_int(elem_ty, ((elem_width as i32) << 3) - 1);
899 let width = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![a; unit]);
900
901 let xor1 = lhs ^ rhs;
902 let xor2 = lhs ^ sum;
903 let and = bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, xor1) & xor2;
904 let mask = and >> width;
905
906 let one = bx.context.new_rvalue_one(elem_ty);
907 let ones = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![one; unit]);
908 let shift1 = ones << width;
909 let shift2 = sum >> width;
910 let mask_min = shift1 ^ shift2;
911
912 let and1 = bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, mask) & sum;
913 let and2 = mask & mask_min;
914
915 and1 + and2
916 }
917 };
918
919 return Ok(result);
920 }
921
922 macro_rules! arith_red {
923 ($name:ident : $vec_op:expr, $float_reduce:ident, $ordered:expr, $op:ident,
924 $identity:expr) => {
925 if name == sym::$name {
926 require!(
927 ret_ty == in_elem,
928 InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
929 );
930 return match in_elem.kind() {
931 ty::Int(_) | ty::Uint(_) => {
932 let r = bx.vector_reduce_op(args[0].immediate(), $vec_op);
933 if $ordered {
934 // if overflow occurs, the result is the
935 // mathematical result modulo 2^n:
936 Ok(bx.$op(args[1].immediate(), r))
937 } else {
938 Ok(bx.vector_reduce_op(args[0].immediate(), $vec_op))
939 }
940 }
941 ty::Float(_) => {
942 if $ordered {
943 // ordered arithmetic reductions take an accumulator
944 let acc = args[1].immediate();
945 Ok(bx.$float_reduce(acc, args[0].immediate()))
946 } else {
947 Ok(bx.vector_reduce_op(args[0].immediate(), $vec_op))
948 }
949 }
950 _ => return_error!(InvalidMonomorphizationUnsupportedElement {
951 span,
952 name,
953 in_ty,
954 elem_ty: in_elem,
955 ret_ty
956 }),
957 };
958 }
959 };
960 }
961
962 arith_red!(
963 simd_reduce_add_unordered: BinaryOp::Plus,
964 vector_reduce_fadd_fast,
965 false,
966 add,
967 0.0 // TODO: Use this argument.
968 );
969 arith_red!(
970 simd_reduce_mul_unordered: BinaryOp::Mult,
971 vector_reduce_fmul_fast,
972 false,
973 mul,
974 1.0
975 );
976 arith_red!(
977 simd_reduce_add_ordered: BinaryOp::Plus,
978 vector_reduce_fadd,
979 true,
980 add,
981 0.0
982 );
983 arith_red!(
984 simd_reduce_mul_ordered: BinaryOp::Mult,
985 vector_reduce_fmul,
986 true,
987 mul,
988 1.0
989 );
990
991
992 macro_rules! minmax_red {
993 ($name:ident: $int_red:ident, $float_red:ident) => {
994 if name == sym::$name {
995 require!(
996 ret_ty == in_elem,
997 InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
998 );
999 return match in_elem.kind() {
1000 ty::Int(_) | ty::Uint(_) => Ok(bx.$int_red(args[0].immediate())),
1001 ty::Float(_) => Ok(bx.$float_red(args[0].immediate())),
1002 _ => return_error!(InvalidMonomorphizationUnsupportedElement { span, name, in_ty, elem_ty: in_elem, ret_ty }),
1003 };
1004 }
1005 };
1006 }
1007
1008 minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin);
1009 minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax);
1010 // TODO(sadlerap): revisit these intrinsics to generate more optimal reductions
1011 minmax_red!(simd_reduce_min_nanless: vector_reduce_min, vector_reduce_fmin);
1012 minmax_red!(simd_reduce_max_nanless: vector_reduce_max, vector_reduce_fmax);
1013
1014 macro_rules! bitwise_red {
1015 ($name:ident : $op:expr, $boolean:expr) => {
1016 if name == sym::$name {
1017 let input = if !$boolean {
1018 require!(
1019 ret_ty == in_elem,
1020 InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
1021 );
1022 args[0].immediate()
1023 } else {
1024 match in_elem.kind() {
1025 ty::Int(_) | ty::Uint(_) => {}
1026 _ => return_error!(InvalidMonomorphizationUnsupportedElement {
1027 span,
1028 name,
1029 in_ty,
1030 elem_ty: in_elem,
1031 ret_ty
1032 }),
1033 }
1034
1035 args[0].immediate()
1036 };
1037 return match in_elem.kind() {
1038 ty::Int(_) | ty::Uint(_) => {
1039 let r = bx.vector_reduce_op(input, $op);
1040 Ok(if !$boolean { r } else { bx.icmp(IntPredicate::IntNE, r, bx.context.new_rvalue_zero(r.get_type())) })
1041 }
1042 _ => return_error!(InvalidMonomorphizationUnsupportedElement {
1043 span,
1044 name,
1045 in_ty,
1046 elem_ty: in_elem,
1047 ret_ty
1048 }),
1049 };
1050 }
1051 };
1052 }
1053
1054 bitwise_red!(simd_reduce_and: BinaryOp::BitwiseAnd, false);
1055 bitwise_red!(simd_reduce_or: BinaryOp::BitwiseOr, false);
1056 bitwise_red!(simd_reduce_xor: BinaryOp::BitwiseXor, false);
1057 bitwise_red!(simd_reduce_all: BinaryOp::BitwiseAnd, true);
1058 bitwise_red!(simd_reduce_any: BinaryOp::BitwiseOr, true);
1059
1060 unimplemented!("simd {}", name);
1061 }
1062