• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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