rustc_codegen_llvm/
intrinsic.rs

1use std::assert_matches::assert_matches;
2use std::cmp::Ordering;
3
4use rustc_abi::{Align, BackendRepr, ExternAbi, Float, HasDataLayout, Primitive, Size};
5use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
6use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
7use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization};
8use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
9use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue};
10use rustc_codegen_ssa::traits::*;
11use rustc_hir as hir;
12use rustc_middle::mir::BinOp;
13use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf};
14use rustc_middle::ty::{self, GenericArgsRef, Ty};
15use rustc_middle::{bug, span_bug};
16use rustc_span::{Span, Symbol, sym};
17use rustc_symbol_mangling::mangle_internal_symbol;
18use rustc_target::callconv::{FnAbi, PassMode};
19use rustc_target::spec::{HasTargetSpec, PanicStrategy};
20use tracing::debug;
21
22use crate::abi::{FnAbiLlvmExt, LlvmType};
23use crate::builder::Builder;
24use crate::context::CodegenCx;
25use crate::llvm::{self, Metadata};
26use crate::type_::Type;
27use crate::type_of::LayoutLlvmExt;
28use crate::va_arg::emit_va_arg;
29use crate::value::Value;
30
31fn get_simple_intrinsic<'ll>(
32    cx: &CodegenCx<'ll, '_>,
33    name: Symbol,
34) -> Option<(&'ll Type, &'ll Value)> {
35    let llvm_name = match name {
36        sym::sqrtf16 => "llvm.sqrt.f16",
37        sym::sqrtf32 => "llvm.sqrt.f32",
38        sym::sqrtf64 => "llvm.sqrt.f64",
39        sym::sqrtf128 => "llvm.sqrt.f128",
40
41        sym::powif16 => "llvm.powi.f16.i32",
42        sym::powif32 => "llvm.powi.f32.i32",
43        sym::powif64 => "llvm.powi.f64.i32",
44        sym::powif128 => "llvm.powi.f128.i32",
45
46        sym::sinf16 => "llvm.sin.f16",
47        sym::sinf32 => "llvm.sin.f32",
48        sym::sinf64 => "llvm.sin.f64",
49        sym::sinf128 => "llvm.sin.f128",
50
51        sym::cosf16 => "llvm.cos.f16",
52        sym::cosf32 => "llvm.cos.f32",
53        sym::cosf64 => "llvm.cos.f64",
54        sym::cosf128 => "llvm.cos.f128",
55
56        sym::powf16 => "llvm.pow.f16",
57        sym::powf32 => "llvm.pow.f32",
58        sym::powf64 => "llvm.pow.f64",
59        sym::powf128 => "llvm.pow.f128",
60
61        sym::expf16 => "llvm.exp.f16",
62        sym::expf32 => "llvm.exp.f32",
63        sym::expf64 => "llvm.exp.f64",
64        sym::expf128 => "llvm.exp.f128",
65
66        sym::exp2f16 => "llvm.exp2.f16",
67        sym::exp2f32 => "llvm.exp2.f32",
68        sym::exp2f64 => "llvm.exp2.f64",
69        sym::exp2f128 => "llvm.exp2.f128",
70
71        sym::logf16 => "llvm.log.f16",
72        sym::logf32 => "llvm.log.f32",
73        sym::logf64 => "llvm.log.f64",
74        sym::logf128 => "llvm.log.f128",
75
76        sym::log10f16 => "llvm.log10.f16",
77        sym::log10f32 => "llvm.log10.f32",
78        sym::log10f64 => "llvm.log10.f64",
79        sym::log10f128 => "llvm.log10.f128",
80
81        sym::log2f16 => "llvm.log2.f16",
82        sym::log2f32 => "llvm.log2.f32",
83        sym::log2f64 => "llvm.log2.f64",
84        sym::log2f128 => "llvm.log2.f128",
85
86        sym::fmaf16 => "llvm.fma.f16",
87        sym::fmaf32 => "llvm.fma.f32",
88        sym::fmaf64 => "llvm.fma.f64",
89        sym::fmaf128 => "llvm.fma.f128",
90
91        sym::fmuladdf16 => "llvm.fmuladd.f16",
92        sym::fmuladdf32 => "llvm.fmuladd.f32",
93        sym::fmuladdf64 => "llvm.fmuladd.f64",
94        sym::fmuladdf128 => "llvm.fmuladd.f128",
95
96        sym::fabsf16 => "llvm.fabs.f16",
97        sym::fabsf32 => "llvm.fabs.f32",
98        sym::fabsf64 => "llvm.fabs.f64",
99        sym::fabsf128 => "llvm.fabs.f128",
100
101        sym::minnumf16 => "llvm.minnum.f16",
102        sym::minnumf32 => "llvm.minnum.f32",
103        sym::minnumf64 => "llvm.minnum.f64",
104        sym::minnumf128 => "llvm.minnum.f128",
105
106        sym::maxnumf16 => "llvm.maxnum.f16",
107        sym::maxnumf32 => "llvm.maxnum.f32",
108        sym::maxnumf64 => "llvm.maxnum.f64",
109        sym::maxnumf128 => "llvm.maxnum.f128",
110
111        sym::copysignf16 => "llvm.copysign.f16",
112        sym::copysignf32 => "llvm.copysign.f32",
113        sym::copysignf64 => "llvm.copysign.f64",
114        sym::copysignf128 => "llvm.copysign.f128",
115
116        sym::floorf16 => "llvm.floor.f16",
117        sym::floorf32 => "llvm.floor.f32",
118        sym::floorf64 => "llvm.floor.f64",
119        sym::floorf128 => "llvm.floor.f128",
120
121        sym::ceilf16 => "llvm.ceil.f16",
122        sym::ceilf32 => "llvm.ceil.f32",
123        sym::ceilf64 => "llvm.ceil.f64",
124        sym::ceilf128 => "llvm.ceil.f128",
125
126        sym::truncf16 => "llvm.trunc.f16",
127        sym::truncf32 => "llvm.trunc.f32",
128        sym::truncf64 => "llvm.trunc.f64",
129        sym::truncf128 => "llvm.trunc.f128",
130
131        // We could use any of `rint`, `nearbyint`, or `roundeven`
132        // for this -- they are all identical in semantics when
133        // assuming the default FP environment.
134        // `rint` is what we used for $forever.
135        sym::round_ties_even_f16 => "llvm.rint.f16",
136        sym::round_ties_even_f32 => "llvm.rint.f32",
137        sym::round_ties_even_f64 => "llvm.rint.f64",
138        sym::round_ties_even_f128 => "llvm.rint.f128",
139
140        sym::roundf16 => "llvm.round.f16",
141        sym::roundf32 => "llvm.round.f32",
142        sym::roundf64 => "llvm.round.f64",
143        sym::roundf128 => "llvm.round.f128",
144
145        sym::ptr_mask => "llvm.ptrmask",
146
147        _ => return None,
148    };
149    Some(cx.get_intrinsic(llvm_name))
150}
151
152impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
153    fn codegen_intrinsic_call(
154        &mut self,
155        instance: ty::Instance<'tcx>,
156        fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
157        args: &[OperandRef<'tcx, &'ll Value>],
158        llresult: &'ll Value,
159        span: Span,
160    ) -> Result<(), ty::Instance<'tcx>> {
161        let tcx = self.tcx;
162        let callee_ty = instance.ty(tcx, self.typing_env());
163
164        let ty::FnDef(def_id, fn_args) = *callee_ty.kind() else {
165            bug!("expected fn item type, found {}", callee_ty);
166        };
167
168        let sig = callee_ty.fn_sig(tcx);
169        let sig = tcx.normalize_erasing_late_bound_regions(self.typing_env(), sig);
170        let arg_tys = sig.inputs();
171        let ret_ty = sig.output();
172        let name = tcx.item_name(def_id);
173
174        let llret_ty = self.layout_of(ret_ty).llvm_type(self);
175        let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout);
176
177        let simple = get_simple_intrinsic(self, name);
178        let llval = match name {
179            _ if simple.is_some() => {
180                let (simple_ty, simple_fn) = simple.unwrap();
181                self.call(
182                    simple_ty,
183                    None,
184                    None,
185                    simple_fn,
186                    &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
187                    None,
188                    Some(instance),
189                )
190            }
191            sym::is_val_statically_known => {
192                let intrinsic_type = args[0].layout.immediate_llvm_type(self.cx);
193                let kind = self.type_kind(intrinsic_type);
194                let intrinsic_name = match kind {
195                    TypeKind::Pointer | TypeKind::Integer => {
196                        Some(format!("llvm.is.constant.{intrinsic_type:?}"))
197                    }
198                    // LLVM float types' intrinsic names differ from their type names.
199                    TypeKind::Half => Some(format!("llvm.is.constant.f16")),
200                    TypeKind::Float => Some(format!("llvm.is.constant.f32")),
201                    TypeKind::Double => Some(format!("llvm.is.constant.f64")),
202                    TypeKind::FP128 => Some(format!("llvm.is.constant.f128")),
203                    _ => None,
204                };
205                if let Some(intrinsic_name) = intrinsic_name {
206                    self.call_intrinsic(&intrinsic_name, &[args[0].immediate()])
207                } else {
208                    self.const_bool(false)
209                }
210            }
211            sym::select_unpredictable => {
212                let cond = args[0].immediate();
213                assert_eq!(args[1].layout, args[2].layout);
214                let select = |bx: &mut Self, true_val, false_val| {
215                    let result = bx.select(cond, true_val, false_val);
216                    bx.set_unpredictable(&result);
217                    result
218                };
219                match (args[1].val, args[2].val) {
220                    (OperandValue::Ref(true_val), OperandValue::Ref(false_val)) => {
221                        assert!(true_val.llextra.is_none());
222                        assert!(false_val.llextra.is_none());
223                        assert_eq!(true_val.align, false_val.align);
224                        let ptr = select(self, true_val.llval, false_val.llval);
225                        let selected =
226                            OperandValue::Ref(PlaceValue::new_sized(ptr, true_val.align));
227                        selected.store(self, result);
228                        return Ok(());
229                    }
230                    (OperandValue::Immediate(_), OperandValue::Immediate(_))
231                    | (OperandValue::Pair(_, _), OperandValue::Pair(_, _)) => {
232                        let true_val = args[1].immediate_or_packed_pair(self);
233                        let false_val = args[2].immediate_or_packed_pair(self);
234                        select(self, true_val, false_val)
235                    }
236                    (OperandValue::ZeroSized, OperandValue::ZeroSized) => return Ok(()),
237                    _ => span_bug!(span, "Incompatible OperandValue for select_unpredictable"),
238                }
239            }
240            sym::catch_unwind => {
241                catch_unwind_intrinsic(
242                    self,
243                    args[0].immediate(),
244                    args[1].immediate(),
245                    args[2].immediate(),
246                    llresult,
247                );
248                return Ok(());
249            }
250            sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[]),
251            sym::va_copy => {
252                self.call_intrinsic("llvm.va_copy", &[args[0].immediate(), args[1].immediate()])
253            }
254            sym::va_arg => {
255                match fn_abi.ret.layout.backend_repr {
256                    BackendRepr::Scalar(scalar) => {
257                        match scalar.primitive() {
258                            Primitive::Int(..) => {
259                                if self.cx().size_of(ret_ty).bytes() < 4 {
260                                    // `va_arg` should not be called on an integer type
261                                    // less than 4 bytes in length. If it is, promote
262                                    // the integer to an `i32` and truncate the result
263                                    // back to the smaller type.
264                                    let promoted_result = emit_va_arg(self, args[0], tcx.types.i32);
265                                    self.trunc(promoted_result, llret_ty)
266                                } else {
267                                    emit_va_arg(self, args[0], ret_ty)
268                                }
269                            }
270                            Primitive::Float(Float::F16) => {
271                                bug!("the va_arg intrinsic does not work with `f16`")
272                            }
273                            Primitive::Float(Float::F64) | Primitive::Pointer(_) => {
274                                emit_va_arg(self, args[0], ret_ty)
275                            }
276                            // `va_arg` should never be used with the return type f32.
277                            Primitive::Float(Float::F32) => {
278                                bug!("the va_arg intrinsic does not work with `f32`")
279                            }
280                            Primitive::Float(Float::F128) => {
281                                bug!("the va_arg intrinsic does not work with `f128`")
282                            }
283                        }
284                    }
285                    _ => bug!("the va_arg intrinsic does not work with non-scalar types"),
286                }
287            }
288
289            sym::volatile_load | sym::unaligned_volatile_load => {
290                let tp_ty = fn_args.type_at(0);
291                let ptr = args[0].immediate();
292                let load = if let PassMode::Cast { cast: ty, pad_i32: _ } = &fn_abi.ret.mode {
293                    let llty = ty.llvm_type(self);
294                    self.volatile_load(llty, ptr)
295                } else {
296                    self.volatile_load(self.layout_of(tp_ty).llvm_type(self), ptr)
297                };
298                let align = if name == sym::unaligned_volatile_load {
299                    1
300                } else {
301                    self.align_of(tp_ty).bytes() as u32
302                };
303                unsafe {
304                    llvm::LLVMSetAlignment(load, align);
305                }
306                if !result.layout.is_zst() {
307                    self.store_to_place(load, result.val);
308                }
309                return Ok(());
310            }
311            sym::volatile_store => {
312                let dst = args[0].deref(self.cx());
313                args[1].val.volatile_store(self, dst);
314                return Ok(());
315            }
316            sym::unaligned_volatile_store => {
317                let dst = args[0].deref(self.cx());
318                args[1].val.unaligned_volatile_store(self, dst);
319                return Ok(());
320            }
321            sym::prefetch_read_data
322            | sym::prefetch_write_data
323            | sym::prefetch_read_instruction
324            | sym::prefetch_write_instruction => {
325                let (rw, cache_type) = match name {
326                    sym::prefetch_read_data => (0, 1),
327                    sym::prefetch_write_data => (1, 1),
328                    sym::prefetch_read_instruction => (0, 0),
329                    sym::prefetch_write_instruction => (1, 0),
330                    _ => bug!(),
331                };
332                self.call_intrinsic(
333                    "llvm.prefetch",
334                    &[
335                        args[0].immediate(),
336                        self.const_i32(rw),
337                        args[1].immediate(),
338                        self.const_i32(cache_type),
339                    ],
340                )
341            }
342            sym::carrying_mul_add => {
343                let (size, signed) = fn_args.type_at(0).int_size_and_signed(self.tcx);
344
345                let wide_llty = self.type_ix(size.bits() * 2);
346                let args = args.as_array().unwrap();
347                let [a, b, c, d] = args.map(|a| self.intcast(a.immediate(), wide_llty, signed));
348
349                let wide = if signed {
350                    let prod = self.unchecked_smul(a, b);
351                    let acc = self.unchecked_sadd(prod, c);
352                    self.unchecked_sadd(acc, d)
353                } else {
354                    let prod = self.unchecked_umul(a, b);
355                    let acc = self.unchecked_uadd(prod, c);
356                    self.unchecked_uadd(acc, d)
357                };
358
359                let narrow_llty = self.type_ix(size.bits());
360                let low = self.trunc(wide, narrow_llty);
361                let bits_const = self.const_uint(wide_llty, size.bits());
362                // No need for ashr when signed; LLVM changes it to lshr anyway.
363                let high = self.lshr(wide, bits_const);
364                // FIXME: could be `trunc nuw`, even for signed.
365                let high = self.trunc(high, narrow_llty);
366
367                let pair_llty = self.type_struct(&[narrow_llty, narrow_llty], false);
368                let pair = self.const_poison(pair_llty);
369                let pair = self.insert_value(pair, low, 0);
370                let pair = self.insert_value(pair, high, 1);
371                pair
372            }
373            sym::ctlz
374            | sym::ctlz_nonzero
375            | sym::cttz
376            | sym::cttz_nonzero
377            | sym::ctpop
378            | sym::bswap
379            | sym::bitreverse
380            | sym::rotate_left
381            | sym::rotate_right
382            | sym::saturating_add
383            | sym::saturating_sub => {
384                let ty = arg_tys[0];
385                if !ty.is_integral() {
386                    tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
387                        span,
388                        name,
389                        ty,
390                    });
391                    return Ok(());
392                }
393                let (size, signed) = ty.int_size_and_signed(self.tcx);
394                let width = size.bits();
395                match name {
396                    sym::ctlz | sym::cttz => {
397                        let y = self.const_bool(false);
398                        let ret = self.call_intrinsic(
399                            &format!("llvm.{name}.i{width}"),
400                            &[args[0].immediate(), y],
401                        );
402
403                        self.intcast(ret, llret_ty, false)
404                    }
405                    sym::ctlz_nonzero => {
406                        let y = self.const_bool(true);
407                        let llvm_name = &format!("llvm.ctlz.i{width}");
408                        let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
409                        self.intcast(ret, llret_ty, false)
410                    }
411                    sym::cttz_nonzero => {
412                        let y = self.const_bool(true);
413                        let llvm_name = &format!("llvm.cttz.i{width}");
414                        let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
415                        self.intcast(ret, llret_ty, false)
416                    }
417                    sym::ctpop => {
418                        let ret = self.call_intrinsic(
419                            &format!("llvm.ctpop.i{width}"),
420                            &[args[0].immediate()],
421                        );
422                        self.intcast(ret, llret_ty, false)
423                    }
424                    sym::bswap => {
425                        if width == 8 {
426                            args[0].immediate() // byte swap a u8/i8 is just a no-op
427                        } else {
428                            self.call_intrinsic(
429                                &format!("llvm.bswap.i{width}"),
430                                &[args[0].immediate()],
431                            )
432                        }
433                    }
434                    sym::bitreverse => self.call_intrinsic(
435                        &format!("llvm.bitreverse.i{width}"),
436                        &[args[0].immediate()],
437                    ),
438                    sym::rotate_left | sym::rotate_right => {
439                        let is_left = name == sym::rotate_left;
440                        let val = args[0].immediate();
441                        let raw_shift = args[1].immediate();
442                        // rotate = funnel shift with first two args the same
443                        let llvm_name =
444                            &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width);
445
446                        // llvm expects shift to be the same type as the values, but rust
447                        // always uses `u32`.
448                        let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
449
450                        self.call_intrinsic(llvm_name, &[val, val, raw_shift])
451                    }
452                    sym::saturating_add | sym::saturating_sub => {
453                        let is_add = name == sym::saturating_add;
454                        let lhs = args[0].immediate();
455                        let rhs = args[1].immediate();
456                        let llvm_name = &format!(
457                            "llvm.{}{}.sat.i{}",
458                            if signed { 's' } else { 'u' },
459                            if is_add { "add" } else { "sub" },
460                            width
461                        );
462                        self.call_intrinsic(llvm_name, &[lhs, rhs])
463                    }
464                    _ => bug!(),
465                }
466            }
467
468            sym::raw_eq => {
469                use BackendRepr::*;
470                let tp_ty = fn_args.type_at(0);
471                let layout = self.layout_of(tp_ty).layout;
472                let use_integer_compare = match layout.backend_repr() {
473                    Scalar(_) | ScalarPair(_, _) => true,
474                    SimdVector { .. } => false,
475                    Memory { .. } => {
476                        // For rusty ABIs, small aggregates are actually passed
477                        // as `RegKind::Integer` (see `FnAbi::adjust_for_abi`),
478                        // so we re-use that same threshold here.
479                        layout.size() <= self.data_layout().pointer_size * 2
480                    }
481                };
482
483                let a = args[0].immediate();
484                let b = args[1].immediate();
485                if layout.size().bytes() == 0 {
486                    self.const_bool(true)
487                } else if use_integer_compare {
488                    let integer_ty = self.type_ix(layout.size().bits());
489                    let a_val = self.load(integer_ty, a, layout.align().abi);
490                    let b_val = self.load(integer_ty, b, layout.align().abi);
491                    self.icmp(IntPredicate::IntEQ, a_val, b_val)
492                } else {
493                    let n = self.const_usize(layout.size().bytes());
494                    let cmp = self.call_intrinsic("memcmp", &[a, b, n]);
495                    match self.cx.sess().target.arch.as_ref() {
496                        "avr" | "msp430" => self.icmp(IntPredicate::IntEQ, cmp, self.const_i16(0)),
497                        _ => self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0)),
498                    }
499                }
500            }
501
502            sym::compare_bytes => {
503                // Here we assume that the `memcmp` provided by the target is a NOP for size 0.
504                let cmp = self.call_intrinsic(
505                    "memcmp",
506                    &[args[0].immediate(), args[1].immediate(), args[2].immediate()],
507                );
508                // Some targets have `memcmp` returning `i16`, but the intrinsic is always `i32`.
509                self.sext(cmp, self.type_ix(32))
510            }
511
512            sym::black_box => {
513                args[0].val.store(self, result);
514                let result_val_span = [result.val.llval];
515                // We need to "use" the argument in some way LLVM can't introspect, and on
516                // targets that support it we can typically leverage inline assembly to do
517                // this. LLVM's interpretation of inline assembly is that it's, well, a black
518                // box. This isn't the greatest implementation since it probably deoptimizes
519                // more than we want, but it's so far good enough.
520                //
521                // For zero-sized types, the location pointed to by the result may be
522                // uninitialized. Do not "use" the result in this case; instead just clobber
523                // the memory.
524                let (constraint, inputs): (&str, &[_]) = if result.layout.is_zst() {
525                    ("~{memory}", &[])
526                } else {
527                    ("r,~{memory}", &result_val_span)
528                };
529                crate::asm::inline_asm_call(
530                    self,
531                    "",
532                    constraint,
533                    inputs,
534                    self.type_void(),
535                    &[],
536                    true,
537                    false,
538                    llvm::AsmDialect::Att,
539                    &[span],
540                    false,
541                    None,
542                    None,
543                )
544                .unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`"));
545
546                // We have copied the value to `result` already.
547                return Ok(());
548            }
549
550            _ if name.as_str().starts_with("simd_") => {
551                // Unpack non-power-of-2 #[repr(packed, simd)] arguments.
552                // This gives them the expected layout of a regular #[repr(simd)] vector.
553                let mut loaded_args = Vec::new();
554                for (ty, arg) in arg_tys.iter().zip(args) {
555                    loaded_args.push(
556                        // #[repr(packed, simd)] vectors are passed like arrays (as references,
557                        // with reduced alignment and no padding) rather than as immediates.
558                        // We can use a vector load to fix the layout and turn the argument
559                        // into an immediate.
560                        if ty.is_simd()
561                            && let OperandValue::Ref(place) = arg.val
562                        {
563                            let (size, elem_ty) = ty.simd_size_and_type(self.tcx());
564                            let elem_ll_ty = match elem_ty.kind() {
565                                ty::Float(f) => self.type_float_from_ty(*f),
566                                ty::Int(i) => self.type_int_from_ty(*i),
567                                ty::Uint(u) => self.type_uint_from_ty(*u),
568                                ty::RawPtr(_, _) => self.type_ptr(),
569                                _ => unreachable!(),
570                            };
571                            let loaded =
572                                self.load_from_place(self.type_vector(elem_ll_ty, size), place);
573                            OperandRef::from_immediate_or_packed_pair(self, loaded, arg.layout)
574                        } else {
575                            *arg
576                        },
577                    );
578                }
579
580                let llret_ty = if ret_ty.is_simd()
581                    && let BackendRepr::Memory { .. } = self.layout_of(ret_ty).layout.backend_repr
582                {
583                    let (size, elem_ty) = ret_ty.simd_size_and_type(self.tcx());
584                    let elem_ll_ty = match elem_ty.kind() {
585                        ty::Float(f) => self.type_float_from_ty(*f),
586                        ty::Int(i) => self.type_int_from_ty(*i),
587                        ty::Uint(u) => self.type_uint_from_ty(*u),
588                        ty::RawPtr(_, _) => self.type_ptr(),
589                        _ => unreachable!(),
590                    };
591                    self.type_vector(elem_ll_ty, size)
592                } else {
593                    llret_ty
594                };
595
596                match generic_simd_intrinsic(
597                    self,
598                    name,
599                    callee_ty,
600                    fn_args,
601                    &loaded_args,
602                    ret_ty,
603                    llret_ty,
604                    span,
605                ) {
606                    Ok(llval) => llval,
607                    // If there was an error, just skip this invocation... we'll abort compilation
608                    // anyway, but we can keep codegen'ing to find more errors.
609                    Err(()) => return Ok(()),
610                }
611            }
612
613            _ => {
614                debug!("unknown intrinsic '{}' -- falling back to default body", name);
615                // Call the fallback body instead of generating the intrinsic code
616                return Err(ty::Instance::new(instance.def_id(), instance.args));
617            }
618        };
619
620        if !fn_abi.ret.is_ignore() {
621            if let PassMode::Cast { .. } = &fn_abi.ret.mode {
622                self.store(llval, result.val.llval, result.val.align);
623            } else {
624                OperandRef::from_immediate_or_packed_pair(self, llval, result.layout)
625                    .val
626                    .store(self, result);
627            }
628        }
629        Ok(())
630    }
631
632    fn abort(&mut self) {
633        self.call_intrinsic("llvm.trap", &[]);
634    }
635
636    fn assume(&mut self, val: Self::Value) {
637        if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No {
638            self.call_intrinsic("llvm.assume", &[val]);
639        }
640    }
641
642    fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value {
643        if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No {
644            self.call_intrinsic("llvm.expect.i1", &[cond, self.const_bool(expected)])
645        } else {
646            cond
647        }
648    }
649
650    fn type_test(&mut self, pointer: Self::Value, typeid: Self::Metadata) -> Self::Value {
651        // Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time
652        // optimization pass replaces calls to this intrinsic with code to test type membership.
653        let typeid = self.get_metadata_value(typeid);
654        self.call_intrinsic("llvm.type.test", &[pointer, typeid])
655    }
656
657    fn type_checked_load(
658        &mut self,
659        llvtable: &'ll Value,
660        vtable_byte_offset: u64,
661        typeid: &'ll Metadata,
662    ) -> Self::Value {
663        let typeid = self.get_metadata_value(typeid);
664        let vtable_byte_offset = self.const_i32(vtable_byte_offset as i32);
665        let type_checked_load =
666            self.call_intrinsic("llvm.type.checked.load", &[llvtable, vtable_byte_offset, typeid]);
667        self.extract_value(type_checked_load, 0)
668    }
669
670    fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value {
671        self.call_intrinsic("llvm.va_start", &[va_list])
672    }
673
674    fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value {
675        self.call_intrinsic("llvm.va_end", &[va_list])
676    }
677}
678
679fn catch_unwind_intrinsic<'ll>(
680    bx: &mut Builder<'_, 'll, '_>,
681    try_func: &'ll Value,
682    data: &'ll Value,
683    catch_func: &'ll Value,
684    dest: &'ll Value,
685) {
686    if bx.sess().panic_strategy() == PanicStrategy::Abort {
687        let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
688        bx.call(try_func_ty, None, None, try_func, &[data], None, None);
689        // Return 0 unconditionally from the intrinsic call;
690        // we can never unwind.
691        let ret_align = bx.tcx().data_layout.i32_align.abi;
692        bx.store(bx.const_i32(0), dest, ret_align);
693    } else if wants_msvc_seh(bx.sess()) {
694        codegen_msvc_try(bx, try_func, data, catch_func, dest);
695    } else if wants_wasm_eh(bx.sess()) {
696        codegen_wasm_try(bx, try_func, data, catch_func, dest);
697    } else if bx.sess().target.os == "emscripten" {
698        codegen_emcc_try(bx, try_func, data, catch_func, dest);
699    } else {
700        codegen_gnu_try(bx, try_func, data, catch_func, dest);
701    }
702}
703
704// MSVC's definition of the `rust_try` function.
705//
706// This implementation uses the new exception handling instructions in LLVM
707// which have support in LLVM for SEH on MSVC targets. Although these
708// instructions are meant to work for all targets, as of the time of this
709// writing, however, LLVM does not recommend the usage of these new instructions
710// as the old ones are still more optimized.
711fn codegen_msvc_try<'ll>(
712    bx: &mut Builder<'_, 'll, '_>,
713    try_func: &'ll Value,
714    data: &'ll Value,
715    catch_func: &'ll Value,
716    dest: &'ll Value,
717) {
718    let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
719        bx.set_personality_fn(bx.eh_personality());
720
721        let normal = bx.append_sibling_block("normal");
722        let catchswitch = bx.append_sibling_block("catchswitch");
723        let catchpad_rust = bx.append_sibling_block("catchpad_rust");
724        let catchpad_foreign = bx.append_sibling_block("catchpad_foreign");
725        let caught = bx.append_sibling_block("caught");
726
727        let try_func = llvm::get_param(bx.llfn(), 0);
728        let data = llvm::get_param(bx.llfn(), 1);
729        let catch_func = llvm::get_param(bx.llfn(), 2);
730
731        // We're generating an IR snippet that looks like:
732        //
733        //   declare i32 @rust_try(%try_func, %data, %catch_func) {
734        //      %slot = alloca i8*
735        //      invoke %try_func(%data) to label %normal unwind label %catchswitch
736        //
737        //   normal:
738        //      ret i32 0
739        //
740        //   catchswitch:
741        //      %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller
742        //
743        //   catchpad_rust:
744        //      %tok = catchpad within %cs [%type_descriptor, 8, %slot]
745        //      %ptr = load %slot
746        //      call %catch_func(%data, %ptr)
747        //      catchret from %tok to label %caught
748        //
749        //   catchpad_foreign:
750        //      %tok = catchpad within %cs [null, 64, null]
751        //      call %catch_func(%data, null)
752        //      catchret from %tok to label %caught
753        //
754        //   caught:
755        //      ret i32 1
756        //   }
757        //
758        // This structure follows the basic usage of throw/try/catch in LLVM.
759        // For example, compile this C++ snippet to see what LLVM generates:
760        //
761        //      struct rust_panic {
762        //          rust_panic(const rust_panic&);
763        //          ~rust_panic();
764        //
765        //          void* x[2];
766        //      };
767        //
768        //      int __rust_try(
769        //          void (*try_func)(void*),
770        //          void *data,
771        //          void (*catch_func)(void*, void*) noexcept
772        //      ) {
773        //          try {
774        //              try_func(data);
775        //              return 0;
776        //          } catch(rust_panic& a) {
777        //              catch_func(data, &a);
778        //              return 1;
779        //          } catch(...) {
780        //              catch_func(data, NULL);
781        //              return 1;
782        //          }
783        //      }
784        //
785        // More information can be found in libstd's seh.rs implementation.
786        let ptr_size = bx.tcx().data_layout.pointer_size;
787        let ptr_align = bx.tcx().data_layout.pointer_align.abi;
788        let slot = bx.alloca(ptr_size, ptr_align);
789        let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
790        bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, None);
791
792        bx.switch_to_block(normal);
793        bx.ret(bx.const_i32(0));
794
795        bx.switch_to_block(catchswitch);
796        let cs = bx.catch_switch(None, None, &[catchpad_rust, catchpad_foreign]);
797
798        // We can't use the TypeDescriptor defined in libpanic_unwind because it
799        // might be in another DLL and the SEH encoding only supports specifying
800        // a TypeDescriptor from the current module.
801        //
802        // However this isn't an issue since the MSVC runtime uses string
803        // comparison on the type name to match TypeDescriptors rather than
804        // pointer equality.
805        //
806        // So instead we generate a new TypeDescriptor in each module that uses
807        // `try` and let the linker merge duplicate definitions in the same
808        // module.
809        //
810        // When modifying, make sure that the type_name string exactly matches
811        // the one used in library/panic_unwind/src/seh.rs.
812        let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_ptr());
813        let type_name = bx.const_bytes(b"rust_panic\0");
814        let type_info =
815            bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_ptr()), type_name], false);
816        let tydesc = bx.declare_global(
817            &mangle_internal_symbol(bx.tcx, "__rust_panic_type_info"),
818            bx.val_ty(type_info),
819        );
820
821        llvm::set_linkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
822        if bx.cx.tcx.sess.target.supports_comdat() {
823            llvm::SetUniqueComdat(bx.llmod, tydesc);
824        }
825        llvm::set_initializer(tydesc, type_info);
826
827        // The flag value of 8 indicates that we are catching the exception by
828        // reference instead of by value. We can't use catch by value because
829        // that requires copying the exception object, which we don't support
830        // since our exception object effectively contains a Box.
831        //
832        // Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
833        bx.switch_to_block(catchpad_rust);
834        let flags = bx.const_i32(8);
835        let funclet = bx.catch_pad(cs, &[tydesc, flags, slot]);
836        let ptr = bx.load(bx.type_ptr(), slot, ptr_align);
837        let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
838        bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None);
839        bx.catch_ret(&funclet, caught);
840
841        // The flag value of 64 indicates a "catch-all".
842        bx.switch_to_block(catchpad_foreign);
843        let flags = bx.const_i32(64);
844        let null = bx.const_null(bx.type_ptr());
845        let funclet = bx.catch_pad(cs, &[null, flags, null]);
846        bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet), None);
847        bx.catch_ret(&funclet, caught);
848
849        bx.switch_to_block(caught);
850        bx.ret(bx.const_i32(1));
851    });
852
853    // Note that no invoke is used here because by definition this function
854    // can't panic (that's what it's catching).
855    let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None);
856    let i32_align = bx.tcx().data_layout.i32_align.abi;
857    bx.store(ret, dest, i32_align);
858}
859
860// WASM's definition of the `rust_try` function.
861fn codegen_wasm_try<'ll>(
862    bx: &mut Builder<'_, 'll, '_>,
863    try_func: &'ll Value,
864    data: &'ll Value,
865    catch_func: &'ll Value,
866    dest: &'ll Value,
867) {
868    let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
869        bx.set_personality_fn(bx.eh_personality());
870
871        let normal = bx.append_sibling_block("normal");
872        let catchswitch = bx.append_sibling_block("catchswitch");
873        let catchpad = bx.append_sibling_block("catchpad");
874        let caught = bx.append_sibling_block("caught");
875
876        let try_func = llvm::get_param(bx.llfn(), 0);
877        let data = llvm::get_param(bx.llfn(), 1);
878        let catch_func = llvm::get_param(bx.llfn(), 2);
879
880        // We're generating an IR snippet that looks like:
881        //
882        //   declare i32 @rust_try(%try_func, %data, %catch_func) {
883        //      %slot = alloca i8*
884        //      invoke %try_func(%data) to label %normal unwind label %catchswitch
885        //
886        //   normal:
887        //      ret i32 0
888        //
889        //   catchswitch:
890        //      %cs = catchswitch within none [%catchpad] unwind to caller
891        //
892        //   catchpad:
893        //      %tok = catchpad within %cs [null]
894        //      %ptr = call @llvm.wasm.get.exception(token %tok)
895        //      %sel = call @llvm.wasm.get.ehselector(token %tok)
896        //      call %catch_func(%data, %ptr)
897        //      catchret from %tok to label %caught
898        //
899        //   caught:
900        //      ret i32 1
901        //   }
902        //
903        let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
904        bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, None);
905
906        bx.switch_to_block(normal);
907        bx.ret(bx.const_i32(0));
908
909        bx.switch_to_block(catchswitch);
910        let cs = bx.catch_switch(None, None, &[catchpad]);
911
912        bx.switch_to_block(catchpad);
913        let null = bx.const_null(bx.type_ptr());
914        let funclet = bx.catch_pad(cs, &[null]);
915
916        let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[funclet.cleanuppad()]);
917        let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]);
918
919        let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
920        bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None);
921        bx.catch_ret(&funclet, caught);
922
923        bx.switch_to_block(caught);
924        bx.ret(bx.const_i32(1));
925    });
926
927    // Note that no invoke is used here because by definition this function
928    // can't panic (that's what it's catching).
929    let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None);
930    let i32_align = bx.tcx().data_layout.i32_align.abi;
931    bx.store(ret, dest, i32_align);
932}
933
934// Definition of the standard `try` function for Rust using the GNU-like model
935// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke`
936// instructions).
937//
938// This codegen is a little surprising because we always call a shim
939// function instead of inlining the call to `invoke` manually here. This is done
940// because in LLVM we're only allowed to have one personality per function
941// definition. The call to the `try` intrinsic is being inlined into the
942// function calling it, and that function may already have other personality
943// functions in play. By calling a shim we're guaranteed that our shim will have
944// the right personality function.
945fn codegen_gnu_try<'ll>(
946    bx: &mut Builder<'_, 'll, '_>,
947    try_func: &'ll Value,
948    data: &'ll Value,
949    catch_func: &'ll Value,
950    dest: &'ll Value,
951) {
952    let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
953        // Codegens the shims described above:
954        //
955        //   bx:
956        //      invoke %try_func(%data) normal %normal unwind %catch
957        //
958        //   normal:
959        //      ret 0
960        //
961        //   catch:
962        //      (%ptr, _) = landingpad
963        //      call %catch_func(%data, %ptr)
964        //      ret 1
965        let then = bx.append_sibling_block("then");
966        let catch = bx.append_sibling_block("catch");
967
968        let try_func = llvm::get_param(bx.llfn(), 0);
969        let data = llvm::get_param(bx.llfn(), 1);
970        let catch_func = llvm::get_param(bx.llfn(), 2);
971        let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
972        bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None);
973
974        bx.switch_to_block(then);
975        bx.ret(bx.const_i32(0));
976
977        // Type indicator for the exception being thrown.
978        //
979        // The first value in this tuple is a pointer to the exception object
980        // being thrown. The second value is a "selector" indicating which of
981        // the landing pad clauses the exception's type had been matched to.
982        // rust_try ignores the selector.
983        bx.switch_to_block(catch);
984        let lpad_ty = bx.type_struct(&[bx.type_ptr(), bx.type_i32()], false);
985        let vals = bx.landing_pad(lpad_ty, bx.eh_personality(), 1);
986        let tydesc = bx.const_null(bx.type_ptr());
987        bx.add_clause(vals, tydesc);
988        let ptr = bx.extract_value(vals, 0);
989        let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
990        bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, None);
991        bx.ret(bx.const_i32(1));
992    });
993
994    // Note that no invoke is used here because by definition this function
995    // can't panic (that's what it's catching).
996    let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None);
997    let i32_align = bx.tcx().data_layout.i32_align.abi;
998    bx.store(ret, dest, i32_align);
999}
1000
1001// Variant of codegen_gnu_try used for emscripten where Rust panics are
1002// implemented using C++ exceptions. Here we use exceptions of a specific type
1003// (`struct rust_panic`) to represent Rust panics.
1004fn codegen_emcc_try<'ll>(
1005    bx: &mut Builder<'_, 'll, '_>,
1006    try_func: &'ll Value,
1007    data: &'ll Value,
1008    catch_func: &'ll Value,
1009    dest: &'ll Value,
1010) {
1011    let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
1012        // Codegens the shims described above:
1013        //
1014        //   bx:
1015        //      invoke %try_func(%data) normal %normal unwind %catch
1016        //
1017        //   normal:
1018        //      ret 0
1019        //
1020        //   catch:
1021        //      (%ptr, %selector) = landingpad
1022        //      %rust_typeid = @llvm.eh.typeid.for(@_ZTI10rust_panic)
1023        //      %is_rust_panic = %selector == %rust_typeid
1024        //      %catch_data = alloca { i8*, i8 }
1025        //      %catch_data[0] = %ptr
1026        //      %catch_data[1] = %is_rust_panic
1027        //      call %catch_func(%data, %catch_data)
1028        //      ret 1
1029        let then = bx.append_sibling_block("then");
1030        let catch = bx.append_sibling_block("catch");
1031
1032        let try_func = llvm::get_param(bx.llfn(), 0);
1033        let data = llvm::get_param(bx.llfn(), 1);
1034        let catch_func = llvm::get_param(bx.llfn(), 2);
1035        let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
1036        bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None);
1037
1038        bx.switch_to_block(then);
1039        bx.ret(bx.const_i32(0));
1040
1041        // Type indicator for the exception being thrown.
1042        //
1043        // The first value in this tuple is a pointer to the exception object
1044        // being thrown. The second value is a "selector" indicating which of
1045        // the landing pad clauses the exception's type had been matched to.
1046        bx.switch_to_block(catch);
1047        let tydesc = bx.eh_catch_typeinfo();
1048        let lpad_ty = bx.type_struct(&[bx.type_ptr(), bx.type_i32()], false);
1049        let vals = bx.landing_pad(lpad_ty, bx.eh_personality(), 2);
1050        bx.add_clause(vals, tydesc);
1051        bx.add_clause(vals, bx.const_null(bx.type_ptr()));
1052        let ptr = bx.extract_value(vals, 0);
1053        let selector = bx.extract_value(vals, 1);
1054
1055        // Check if the typeid we got is the one for a Rust panic.
1056        let rust_typeid = bx.call_intrinsic("llvm.eh.typeid.for", &[tydesc]);
1057        let is_rust_panic = bx.icmp(IntPredicate::IntEQ, selector, rust_typeid);
1058        let is_rust_panic = bx.zext(is_rust_panic, bx.type_bool());
1059
1060        // We need to pass two values to catch_func (ptr and is_rust_panic), so
1061        // create an alloca and pass a pointer to that.
1062        let ptr_size = bx.tcx().data_layout.pointer_size;
1063        let ptr_align = bx.tcx().data_layout.pointer_align.abi;
1064        let i8_align = bx.tcx().data_layout.i8_align.abi;
1065        // Required in order for there to be no padding between the fields.
1066        assert!(i8_align <= ptr_align);
1067        let catch_data = bx.alloca(2 * ptr_size, ptr_align);
1068        bx.store(ptr, catch_data, ptr_align);
1069        let catch_data_1 = bx.inbounds_ptradd(catch_data, bx.const_usize(ptr_size.bytes()));
1070        bx.store(is_rust_panic, catch_data_1, i8_align);
1071
1072        let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
1073        bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None, None);
1074        bx.ret(bx.const_i32(1));
1075    });
1076
1077    // Note that no invoke is used here because by definition this function
1078    // can't panic (that's what it's catching).
1079    let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None);
1080    let i32_align = bx.tcx().data_layout.i32_align.abi;
1081    bx.store(ret, dest, i32_align);
1082}
1083
1084// Helper function to give a Block to a closure to codegen a shim function.
1085// This is currently primarily used for the `try` intrinsic functions above.
1086fn gen_fn<'a, 'll, 'tcx>(
1087    cx: &'a CodegenCx<'ll, 'tcx>,
1088    name: &str,
1089    rust_fn_sig: ty::PolyFnSig<'tcx>,
1090    codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
1091) -> (&'ll Type, &'ll Value) {
1092    let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
1093    let llty = fn_abi.llvm_type(cx);
1094    let llfn = cx.declare_fn(name, fn_abi, None);
1095    cx.set_frame_pointer_type(llfn);
1096    cx.apply_target_cpu_attr(llfn);
1097    // FIXME(eddyb) find a nicer way to do this.
1098    llvm::set_linkage(llfn, llvm::Linkage::InternalLinkage);
1099    let llbb = Builder::append_block(cx, llfn, "entry-block");
1100    let bx = Builder::build(cx, llbb);
1101    codegen(bx);
1102    (llty, llfn)
1103}
1104
1105// Helper function used to get a handle to the `__rust_try` function used to
1106// catch exceptions.
1107//
1108// This function is only generated once and is then cached.
1109fn get_rust_try_fn<'a, 'll, 'tcx>(
1110    cx: &'a CodegenCx<'ll, 'tcx>,
1111    codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
1112) -> (&'ll Type, &'ll Value) {
1113    if let Some(llfn) = cx.rust_try_fn.get() {
1114        return llfn;
1115    }
1116
1117    // Define the type up front for the signature of the rust_try function.
1118    let tcx = cx.tcx;
1119    let i8p = Ty::new_mut_ptr(tcx, tcx.types.i8);
1120    // `unsafe fn(*mut i8) -> ()`
1121    let try_fn_ty = Ty::new_fn_ptr(
1122        tcx,
1123        ty::Binder::dummy(tcx.mk_fn_sig(
1124            [i8p],
1125            tcx.types.unit,
1126            false,
1127            hir::Safety::Unsafe,
1128            ExternAbi::Rust,
1129        )),
1130    );
1131    // `unsafe fn(*mut i8, *mut i8) -> ()`
1132    let catch_fn_ty = Ty::new_fn_ptr(
1133        tcx,
1134        ty::Binder::dummy(tcx.mk_fn_sig(
1135            [i8p, i8p],
1136            tcx.types.unit,
1137            false,
1138            hir::Safety::Unsafe,
1139            ExternAbi::Rust,
1140        )),
1141    );
1142    // `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32`
1143    let rust_fn_sig = ty::Binder::dummy(cx.tcx.mk_fn_sig(
1144        [try_fn_ty, i8p, catch_fn_ty],
1145        tcx.types.i32,
1146        false,
1147        hir::Safety::Unsafe,
1148        ExternAbi::Rust,
1149    ));
1150    let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen);
1151    cx.rust_try_fn.set(Some(rust_try));
1152    rust_try
1153}
1154
1155fn generic_simd_intrinsic<'ll, 'tcx>(
1156    bx: &mut Builder<'_, 'll, 'tcx>,
1157    name: Symbol,
1158    callee_ty: Ty<'tcx>,
1159    fn_args: GenericArgsRef<'tcx>,
1160    args: &[OperandRef<'tcx, &'ll Value>],
1161    ret_ty: Ty<'tcx>,
1162    llret_ty: &'ll Type,
1163    span: Span,
1164) -> Result<&'ll Value, ()> {
1165    macro_rules! return_error {
1166        ($diag: expr) => {{
1167            bx.sess().dcx().emit_err($diag);
1168            return Err(());
1169        }};
1170    }
1171
1172    macro_rules! require {
1173        ($cond: expr, $diag: expr) => {
1174            if !$cond {
1175                return_error!($diag);
1176            }
1177        };
1178    }
1179
1180    macro_rules! require_simd {
1181        ($ty: expr, $variant:ident) => {{
1182            require!($ty.is_simd(), InvalidMonomorphization::$variant { span, name, ty: $ty });
1183            $ty.simd_size_and_type(bx.tcx())
1184        }};
1185    }
1186
1187    /// Returns the bitwidth of the `$ty` argument if it is an `Int` type.
1188    macro_rules! require_int_ty {
1189        ($ty: expr, $diag: expr) => {
1190            match $ty {
1191                ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()),
1192                _ => {
1193                    return_error!($diag);
1194                }
1195            }
1196        };
1197    }
1198
1199    /// Returns the bitwidth of the `$ty` argument if it is an `Int` or `Uint` type.
1200    macro_rules! require_int_or_uint_ty {
1201        ($ty: expr, $diag: expr) => {
1202            match $ty {
1203                ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()),
1204                ty::Uint(i) => {
1205                    i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits())
1206                }
1207                _ => {
1208                    return_error!($diag);
1209                }
1210            }
1211        };
1212    }
1213
1214    /// Converts a vector mask, where each element has a bit width equal to the data elements it is used with,
1215    /// down to an i1 based mask that can be used by llvm intrinsics.
1216    ///
1217    /// The rust simd semantics are that each element should either consist of all ones or all zeroes,
1218    /// but this information is not available to llvm. Truncating the vector effectively uses the lowest bit,
1219    /// but codegen for several targets is better if we consider the highest bit by shifting.
1220    ///
1221    /// For x86 SSE/AVX targets this is beneficial since most instructions with mask parameters only consider the highest bit.
1222    /// So even though on llvm level we have an additional shift, in the final assembly there is no shift or truncate and
1223    /// instead the mask can be used as is.
1224    ///
1225    /// For aarch64 and other targets there is a benefit because a mask from the sign bit can be more
1226    /// efficiently converted to an all ones / all zeroes mask by comparing whether each element is negative.
1227    fn vector_mask_to_bitmask<'a, 'll, 'tcx>(
1228        bx: &mut Builder<'a, 'll, 'tcx>,
1229        i_xn: &'ll Value,
1230        in_elem_bitwidth: u64,
1231        in_len: u64,
1232    ) -> &'ll Value {
1233        // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position.
1234        let shift_idx = bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _);
1235        let shift_indices = vec![shift_idx; in_len as _];
1236        let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice()));
1237        // Truncate vector to an <i1 x N>
1238        bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len))
1239    }
1240
1241    let tcx = bx.tcx();
1242    let sig = tcx.normalize_erasing_late_bound_regions(bx.typing_env(), callee_ty.fn_sig(tcx));
1243    let arg_tys = sig.inputs();
1244
1245    // Sanity-check: all vector arguments must be immediates.
1246    if cfg!(debug_assertions) {
1247        for (ty, arg) in arg_tys.iter().zip(args) {
1248            if ty.is_simd() {
1249                assert_matches!(arg.val, OperandValue::Immediate(_));
1250            }
1251        }
1252    }
1253
1254    if name == sym::simd_select_bitmask {
1255        let (len, _) = require_simd!(arg_tys[1], SimdArgument);
1256
1257        let expected_int_bits = len.max(8).next_power_of_two();
1258        let expected_bytes = len.div_ceil(8);
1259
1260        let mask_ty = arg_tys[0];
1261        let mask = match mask_ty.kind() {
1262            ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
1263            ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
1264            ty::Array(elem, len)
1265                if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
1266                    && len
1267                        .try_to_target_usize(bx.tcx)
1268                        .expect("expected monomorphic const in codegen")
1269                        == expected_bytes =>
1270            {
1271                let place = PlaceRef::alloca(bx, args[0].layout);
1272                args[0].val.store(bx, place);
1273                let int_ty = bx.type_ix(expected_bytes * 8);
1274                bx.load(int_ty, place.val.llval, Align::ONE)
1275            }
1276            _ => return_error!(InvalidMonomorphization::InvalidBitmask {
1277                span,
1278                name,
1279                mask_ty,
1280                expected_int_bits,
1281                expected_bytes
1282            }),
1283        };
1284
1285        let i1 = bx.type_i1();
1286        let im = bx.type_ix(len);
1287        let i1xn = bx.type_vector(i1, len);
1288        let m_im = bx.trunc(mask, im);
1289        let m_i1s = bx.bitcast(m_im, i1xn);
1290        return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
1291    }
1292
1293    // every intrinsic below takes a SIMD vector as its first argument
1294    let (in_len, in_elem) = require_simd!(arg_tys[0], SimdInput);
1295    let in_ty = arg_tys[0];
1296
1297    let comparison = match name {
1298        sym::simd_eq => Some(BinOp::Eq),
1299        sym::simd_ne => Some(BinOp::Ne),
1300        sym::simd_lt => Some(BinOp::Lt),
1301        sym::simd_le => Some(BinOp::Le),
1302        sym::simd_gt => Some(BinOp::Gt),
1303        sym::simd_ge => Some(BinOp::Ge),
1304        _ => None,
1305    };
1306
1307    if let Some(cmp_op) = comparison {
1308        let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
1309
1310        require!(
1311            in_len == out_len,
1312            InvalidMonomorphization::ReturnLengthInputType {
1313                span,
1314                name,
1315                in_len,
1316                in_ty,
1317                ret_ty,
1318                out_len
1319            }
1320        );
1321        require!(
1322            bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
1323            InvalidMonomorphization::ReturnIntegerType { span, name, ret_ty, out_ty }
1324        );
1325
1326        return Ok(compare_simd_types(
1327            bx,
1328            args[0].immediate(),
1329            args[1].immediate(),
1330            in_elem,
1331            llret_ty,
1332            cmp_op,
1333        ));
1334    }
1335
1336    if name == sym::simd_shuffle_const_generic {
1337        let idx = fn_args[2].expect_const().to_value().valtree.unwrap_branch();
1338        let n = idx.len() as u64;
1339
1340        let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
1341        require!(
1342            out_len == n,
1343            InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len }
1344        );
1345        require!(
1346            in_elem == out_ty,
1347            InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
1348        );
1349
1350        let total_len = in_len * 2;
1351
1352        let indices: Option<Vec<_>> = idx
1353            .iter()
1354            .enumerate()
1355            .map(|(arg_idx, val)| {
1356                let idx = val.unwrap_leaf().to_i32();
1357                if idx >= i32::try_from(total_len).unwrap() {
1358                    bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
1359                        span,
1360                        name,
1361                        arg_idx: arg_idx as u64,
1362                        total_len: total_len.into(),
1363                    });
1364                    None
1365                } else {
1366                    Some(bx.const_i32(idx))
1367                }
1368            })
1369            .collect();
1370        let Some(indices) = indices else {
1371            return Ok(bx.const_null(llret_ty));
1372        };
1373
1374        return Ok(bx.shuffle_vector(
1375            args[0].immediate(),
1376            args[1].immediate(),
1377            bx.const_vector(&indices),
1378        ));
1379    }
1380
1381    if name == sym::simd_shuffle {
1382        // Make sure this is actually a SIMD vector.
1383        let idx_ty = args[2].layout.ty;
1384        let n: u64 = if idx_ty.is_simd()
1385            && matches!(idx_ty.simd_size_and_type(bx.cx.tcx).1.kind(), ty::Uint(ty::UintTy::U32))
1386        {
1387            idx_ty.simd_size_and_type(bx.cx.tcx).0
1388        } else {
1389            return_error!(InvalidMonomorphization::SimdShuffle { span, name, ty: idx_ty })
1390        };
1391
1392        let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
1393        require!(
1394            out_len == n,
1395            InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len }
1396        );
1397        require!(
1398            in_elem == out_ty,
1399            InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
1400        );
1401
1402        let total_len = u128::from(in_len) * 2;
1403
1404        // Check that the indices are in-bounds.
1405        let indices = args[2].immediate();
1406        for i in 0..n {
1407            let val = bx.const_get_elt(indices, i as u64);
1408            let idx = bx
1409                .const_to_opt_u128(val, true)
1410                .unwrap_or_else(|| bug!("typeck should have already ensured that these are const"));
1411            if idx >= total_len {
1412                return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
1413                    span,
1414                    name,
1415                    arg_idx: i,
1416                    total_len,
1417                });
1418            }
1419        }
1420
1421        return Ok(bx.shuffle_vector(args[0].immediate(), args[1].immediate(), indices));
1422    }
1423
1424    if name == sym::simd_insert {
1425        require!(
1426            in_elem == arg_tys[2],
1427            InvalidMonomorphization::InsertedType {
1428                span,
1429                name,
1430                in_elem,
1431                in_ty,
1432                out_ty: arg_tys[2]
1433            }
1434        );
1435        let idx = bx
1436            .const_to_opt_u128(args[1].immediate(), false)
1437            .expect("typeck should have ensure that this is a const");
1438        if idx >= in_len.into() {
1439            return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
1440                span,
1441                name,
1442                arg_idx: 1,
1443                total_len: in_len.into(),
1444            });
1445        }
1446        return Ok(bx.insert_element(
1447            args[0].immediate(),
1448            args[2].immediate(),
1449            bx.const_i32(idx as i32),
1450        ));
1451    }
1452    if name == sym::simd_extract {
1453        require!(
1454            ret_ty == in_elem,
1455            InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
1456        );
1457        let idx = bx
1458            .const_to_opt_u128(args[1].immediate(), false)
1459            .expect("typeck should have ensure that this is a const");
1460        if idx >= in_len.into() {
1461            return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
1462                span,
1463                name,
1464                arg_idx: 1,
1465                total_len: in_len.into(),
1466            });
1467        }
1468        return Ok(bx.extract_element(args[0].immediate(), bx.const_i32(idx as i32)));
1469    }
1470
1471    if name == sym::simd_select {
1472        let m_elem_ty = in_elem;
1473        let m_len = in_len;
1474        let (v_len, _) = require_simd!(arg_tys[1], SimdArgument);
1475        require!(
1476            m_len == v_len,
1477            InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
1478        );
1479        let in_elem_bitwidth = require_int_ty!(
1480            m_elem_ty.kind(),
1481            InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }
1482        );
1483        let m_i1s = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, m_len);
1484        return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
1485    }
1486
1487    if name == sym::simd_bitmask {
1488        // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a vector mask and
1489        // returns one bit for each lane (which must all be `0` or `!0`) in the form of either:
1490        // * an unsigned integer
1491        // * an array of `u8`
1492        // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits.
1493        //
1494        // The bit order of the result depends on the byte endianness, LSB-first for little
1495        // endian and MSB-first for big endian.
1496        let expected_int_bits = in_len.max(8).next_power_of_two();
1497        let expected_bytes = in_len.div_ceil(8);
1498
1499        // Integer vector <i{in_bitwidth} x in_len>:
1500        let in_elem_bitwidth = require_int_or_uint_ty!(
1501            in_elem.kind(),
1502            InvalidMonomorphization::VectorArgument { span, name, in_ty, in_elem }
1503        );
1504
1505        let i1xn = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, in_len);
1506        // Bitcast <i1 x N> to iN:
1507        let i_ = bx.bitcast(i1xn, bx.type_ix(in_len));
1508
1509        match ret_ty.kind() {
1510            ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {
1511                // Zero-extend iN to the bitmask type:
1512                return Ok(bx.zext(i_, bx.type_ix(expected_int_bits)));
1513            }
1514            ty::Array(elem, len)
1515                if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
1516                    && len
1517                        .try_to_target_usize(bx.tcx)
1518                        .expect("expected monomorphic const in codegen")
1519                        == expected_bytes =>
1520            {
1521                // Zero-extend iN to the array length:
1522                let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8));
1523
1524                // Convert the integer to a byte array
1525                let ptr = bx.alloca(Size::from_bytes(expected_bytes), Align::ONE);
1526                bx.store(ze, ptr, Align::ONE);
1527                let array_ty = bx.type_array(bx.type_i8(), expected_bytes);
1528                return Ok(bx.load(array_ty, ptr, Align::ONE));
1529            }
1530            _ => return_error!(InvalidMonomorphization::CannotReturn {
1531                span,
1532                name,
1533                ret_ty,
1534                expected_int_bits,
1535                expected_bytes
1536            }),
1537        }
1538    }
1539
1540    fn simd_simple_float_intrinsic<'ll, 'tcx>(
1541        name: Symbol,
1542        in_elem: Ty<'_>,
1543        in_ty: Ty<'_>,
1544        in_len: u64,
1545        bx: &mut Builder<'_, 'll, 'tcx>,
1546        span: Span,
1547        args: &[OperandRef<'tcx, &'ll Value>],
1548    ) -> Result<&'ll Value, ()> {
1549        macro_rules! return_error {
1550            ($diag: expr) => {{
1551                bx.sess().dcx().emit_err($diag);
1552                return Err(());
1553            }};
1554        }
1555
1556        let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() {
1557            let elem_ty = bx.cx.type_float_from_ty(*f);
1558            match f.bit_width() {
1559                16 => ("f16", elem_ty),
1560                32 => ("f32", elem_ty),
1561                64 => ("f64", elem_ty),
1562                128 => ("f128", elem_ty),
1563                _ => return_error!(InvalidMonomorphization::FloatingPointVector {
1564                    span,
1565                    name,
1566                    f_ty: *f,
1567                    in_ty,
1568                }),
1569            }
1570        } else {
1571            return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty });
1572        };
1573
1574        let vec_ty = bx.type_vector(elem_ty, in_len);
1575
1576        let (intr_name, fn_ty) = match name {
1577            sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)),
1578            sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)),
1579            sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)),
1580            sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)),
1581            sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)),
1582            sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)),
1583            sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)),
1584            sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)),
1585            sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)),
1586            sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
1587            sym::simd_relaxed_fma => ("fmuladd", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
1588            sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)),
1589            sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)),
1590            sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)),
1591            sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)),
1592            _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }),
1593        };
1594        let llvm_name = &format!("llvm.{intr_name}.v{in_len}{elem_ty_str}");
1595        let f = bx.declare_cfn(llvm_name, llvm::UnnamedAddr::No, fn_ty);
1596        let c = bx.call(
1597            fn_ty,
1598            None,
1599            None,
1600            f,
1601            &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
1602            None,
1603            None,
1604        );
1605        Ok(c)
1606    }
1607
1608    if std::matches!(
1609        name,
1610        sym::simd_ceil
1611            | sym::simd_fabs
1612            | sym::simd_fcos
1613            | sym::simd_fexp2
1614            | sym::simd_fexp
1615            | sym::simd_flog10
1616            | sym::simd_flog2
1617            | sym::simd_flog
1618            | sym::simd_floor
1619            | sym::simd_fma
1620            | sym::simd_fsin
1621            | sym::simd_fsqrt
1622            | sym::simd_relaxed_fma
1623            | sym::simd_round
1624            | sym::simd_trunc
1625    ) {
1626        return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args);
1627    }
1628
1629    // FIXME: use:
1630    //  https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Function.h#L182
1631    //  https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81
1632    fn llvm_vector_str(bx: &Builder<'_, '_, '_>, elem_ty: Ty<'_>, vec_len: u64) -> String {
1633        match *elem_ty.kind() {
1634            ty::Int(v) => format!(
1635                "v{}i{}",
1636                vec_len,
1637                // Normalize to prevent crash if v: IntTy::Isize
1638                v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
1639            ),
1640            ty::Uint(v) => format!(
1641                "v{}i{}",
1642                vec_len,
1643                // Normalize to prevent crash if v: UIntTy::Usize
1644                v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
1645            ),
1646            ty::Float(v) => format!("v{}f{}", vec_len, v.bit_width()),
1647            ty::RawPtr(_, _) => format!("v{}p0", vec_len),
1648            _ => unreachable!(),
1649        }
1650    }
1651
1652    fn llvm_vector_ty<'ll>(cx: &CodegenCx<'ll, '_>, elem_ty: Ty<'_>, vec_len: u64) -> &'ll Type {
1653        let elem_ty = match *elem_ty.kind() {
1654            ty::Int(v) => cx.type_int_from_ty(v),
1655            ty::Uint(v) => cx.type_uint_from_ty(v),
1656            ty::Float(v) => cx.type_float_from_ty(v),
1657            ty::RawPtr(_, _) => cx.type_ptr(),
1658            _ => unreachable!(),
1659        };
1660        cx.type_vector(elem_ty, vec_len)
1661    }
1662
1663    if name == sym::simd_gather {
1664        // simd_gather(values: <N x T>, pointers: <N x *_ T>,
1665        //             mask: <N x i{M}>) -> <N x T>
1666        // * N: number of elements in the input vectors
1667        // * T: type of the element to load
1668        // * M: any integer width is supported, will be truncated to i1
1669
1670        // All types must be simd vector types
1671
1672        // The second argument must be a simd vector with an element type that's a pointer
1673        // to the element type of the first argument
1674        let (_, element_ty0) = require_simd!(in_ty, SimdFirst);
1675        let (out_len, element_ty1) = require_simd!(arg_tys[1], SimdSecond);
1676        // The element type of the third argument must be a signed integer type of any width:
1677        let (out_len2, element_ty2) = require_simd!(arg_tys[2], SimdThird);
1678        require_simd!(ret_ty, SimdReturn);
1679
1680        // Of the same length:
1681        require!(
1682            in_len == out_len,
1683            InvalidMonomorphization::SecondArgumentLength {
1684                span,
1685                name,
1686                in_len,
1687                in_ty,
1688                arg_ty: arg_tys[1],
1689                out_len
1690            }
1691        );
1692        require!(
1693            in_len == out_len2,
1694            InvalidMonomorphization::ThirdArgumentLength {
1695                span,
1696                name,
1697                in_len,
1698                in_ty,
1699                arg_ty: arg_tys[2],
1700                out_len: out_len2
1701            }
1702        );
1703
1704        // The return type must match the first argument type
1705        require!(
1706            ret_ty == in_ty,
1707            InvalidMonomorphization::ExpectedReturnType { span, name, in_ty, ret_ty }
1708        );
1709
1710        require!(
1711            matches!(
1712                *element_ty1.kind(),
1713                ty::RawPtr(p_ty, _) if p_ty == in_elem && p_ty.kind() == element_ty0.kind()
1714            ),
1715            InvalidMonomorphization::ExpectedElementType {
1716                span,
1717                name,
1718                expected_element: element_ty1,
1719                second_arg: arg_tys[1],
1720                in_elem,
1721                in_ty,
1722                mutability: ExpectedPointerMutability::Not,
1723            }
1724        );
1725
1726        let mask_elem_bitwidth = require_int_ty!(
1727            element_ty2.kind(),
1728            InvalidMonomorphization::ThirdArgElementType {
1729                span,
1730                name,
1731                expected_element: element_ty2,
1732                third_arg: arg_tys[2]
1733            }
1734        );
1735
1736        // Alignment of T, must be a constant integer value:
1737        let alignment_ty = bx.type_i32();
1738        let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
1739
1740        // Truncate the mask vector to a vector of i1s:
1741        let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
1742        let mask_ty = bx.type_vector(bx.type_i1(), in_len);
1743
1744        // Type of the vector of pointers:
1745        let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len);
1746        let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len);
1747
1748        // Type of the vector of elements:
1749        let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
1750        let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len);
1751
1752        let llvm_intrinsic =
1753            format!("llvm.masked.gather.{llvm_elem_vec_str}.{llvm_pointer_vec_str}");
1754        let fn_ty = bx.type_func(
1755            &[llvm_pointer_vec_ty, alignment_ty, mask_ty, llvm_elem_vec_ty],
1756            llvm_elem_vec_ty,
1757        );
1758        let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
1759        let v = bx.call(
1760            fn_ty,
1761            None,
1762            None,
1763            f,
1764            &[args[1].immediate(), alignment, mask, args[0].immediate()],
1765            None,
1766            None,
1767        );
1768        return Ok(v);
1769    }
1770
1771    if name == sym::simd_masked_load {
1772        // simd_masked_load(mask: <N x i{M}>, pointer: *_ T, values: <N x T>) -> <N x T>
1773        // * N: number of elements in the input vectors
1774        // * T: type of the element to load
1775        // * M: any integer width is supported, will be truncated to i1
1776        // Loads contiguous elements from memory behind `pointer`, but only for
1777        // those lanes whose `mask` bit is enabled.
1778        // The memory addresses corresponding to the “off” lanes are not accessed.
1779
1780        // The element type of the "mask" argument must be a signed integer type of any width
1781        let mask_ty = in_ty;
1782        let (mask_len, mask_elem) = (in_len, in_elem);
1783
1784        // The second argument must be a pointer matching the element type
1785        let pointer_ty = arg_tys[1];
1786
1787        // The last argument is a passthrough vector providing values for disabled lanes
1788        let values_ty = arg_tys[2];
1789        let (values_len, values_elem) = require_simd!(values_ty, SimdThird);
1790
1791        require_simd!(ret_ty, SimdReturn);
1792
1793        // Of the same length:
1794        require!(
1795            values_len == mask_len,
1796            InvalidMonomorphization::ThirdArgumentLength {
1797                span,
1798                name,
1799                in_len: mask_len,
1800                in_ty: mask_ty,
1801                arg_ty: values_ty,
1802                out_len: values_len
1803            }
1804        );
1805
1806        // The return type must match the last argument type
1807        require!(
1808            ret_ty == values_ty,
1809            InvalidMonomorphization::ExpectedReturnType { span, name, in_ty: values_ty, ret_ty }
1810        );
1811
1812        require!(
1813            matches!(
1814                *pointer_ty.kind(),
1815                ty::RawPtr(p_ty, _) if p_ty == values_elem && p_ty.kind() == values_elem.kind()
1816            ),
1817            InvalidMonomorphization::ExpectedElementType {
1818                span,
1819                name,
1820                expected_element: values_elem,
1821                second_arg: pointer_ty,
1822                in_elem: values_elem,
1823                in_ty: values_ty,
1824                mutability: ExpectedPointerMutability::Not,
1825            }
1826        );
1827
1828        let m_elem_bitwidth = require_int_ty!(
1829            mask_elem.kind(),
1830            InvalidMonomorphization::ThirdArgElementType {
1831                span,
1832                name,
1833                expected_element: values_elem,
1834                third_arg: mask_ty,
1835            }
1836        );
1837
1838        let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
1839        let mask_ty = bx.type_vector(bx.type_i1(), mask_len);
1840
1841        // Alignment of T, must be a constant integer value:
1842        let alignment_ty = bx.type_i32();
1843        let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32);
1844
1845        let llvm_pointer = bx.type_ptr();
1846
1847        // Type of the vector of elements:
1848        let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
1849        let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len);
1850
1851        let llvm_intrinsic = format!("llvm.masked.load.{llvm_elem_vec_str}.p0");
1852        let fn_ty = bx
1853            .type_func(&[llvm_pointer, alignment_ty, mask_ty, llvm_elem_vec_ty], llvm_elem_vec_ty);
1854        let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
1855        let v = bx.call(
1856            fn_ty,
1857            None,
1858            None,
1859            f,
1860            &[args[1].immediate(), alignment, mask, args[2].immediate()],
1861            None,
1862            None,
1863        );
1864        return Ok(v);
1865    }
1866
1867    if name == sym::simd_masked_store {
1868        // simd_masked_store(mask: <N x i{M}>, pointer: *mut T, values: <N x T>) -> ()
1869        // * N: number of elements in the input vectors
1870        // * T: type of the element to load
1871        // * M: any integer width is supported, will be truncated to i1
1872        // Stores contiguous elements to memory behind `pointer`, but only for
1873        // those lanes whose `mask` bit is enabled.
1874        // The memory addresses corresponding to the “off” lanes are not accessed.
1875
1876        // The element type of the "mask" argument must be a signed integer type of any width
1877        let mask_ty = in_ty;
1878        let (mask_len, mask_elem) = (in_len, in_elem);
1879
1880        // The second argument must be a pointer matching the element type
1881        let pointer_ty = arg_tys[1];
1882
1883        // The last argument specifies the values to store to memory
1884        let values_ty = arg_tys[2];
1885        let (values_len, values_elem) = require_simd!(values_ty, SimdThird);
1886
1887        // Of the same length:
1888        require!(
1889            values_len == mask_len,
1890            InvalidMonomorphization::ThirdArgumentLength {
1891                span,
1892                name,
1893                in_len: mask_len,
1894                in_ty: mask_ty,
1895                arg_ty: values_ty,
1896                out_len: values_len
1897            }
1898        );
1899
1900        // The second argument must be a mutable pointer type matching the element type
1901        require!(
1902            matches!(
1903                *pointer_ty.kind(),
1904                ty::RawPtr(p_ty, p_mutbl)
1905                    if p_ty == values_elem && p_ty.kind() == values_elem.kind() && p_mutbl.is_mut()
1906            ),
1907            InvalidMonomorphization::ExpectedElementType {
1908                span,
1909                name,
1910                expected_element: values_elem,
1911                second_arg: pointer_ty,
1912                in_elem: values_elem,
1913                in_ty: values_ty,
1914                mutability: ExpectedPointerMutability::Mut,
1915            }
1916        );
1917
1918        let m_elem_bitwidth = require_int_ty!(
1919            mask_elem.kind(),
1920            InvalidMonomorphization::ThirdArgElementType {
1921                span,
1922                name,
1923                expected_element: values_elem,
1924                third_arg: mask_ty,
1925            }
1926        );
1927
1928        let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
1929        let mask_ty = bx.type_vector(bx.type_i1(), mask_len);
1930
1931        // Alignment of T, must be a constant integer value:
1932        let alignment_ty = bx.type_i32();
1933        let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32);
1934
1935        let ret_t = bx.type_void();
1936
1937        let llvm_pointer = bx.type_ptr();
1938
1939        // Type of the vector of elements:
1940        let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
1941        let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len);
1942
1943        let llvm_intrinsic = format!("llvm.masked.store.{llvm_elem_vec_str}.p0");
1944        let fn_ty = bx.type_func(&[llvm_elem_vec_ty, llvm_pointer, alignment_ty, mask_ty], ret_t);
1945        let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
1946        let v = bx.call(
1947            fn_ty,
1948            None,
1949            None,
1950            f,
1951            &[args[2].immediate(), args[1].immediate(), alignment, mask],
1952            None,
1953            None,
1954        );
1955        return Ok(v);
1956    }
1957
1958    if name == sym::simd_scatter {
1959        // simd_scatter(values: <N x T>, pointers: <N x *mut T>,
1960        //             mask: <N x i{M}>) -> ()
1961        // * N: number of elements in the input vectors
1962        // * T: type of the element to load
1963        // * M: any integer width is supported, will be truncated to i1
1964
1965        // All types must be simd vector types
1966        // The second argument must be a simd vector with an element type that's a pointer
1967        // to the element type of the first argument
1968        let (_, element_ty0) = require_simd!(in_ty, SimdFirst);
1969        let (element_len1, element_ty1) = require_simd!(arg_tys[1], SimdSecond);
1970        let (element_len2, element_ty2) = require_simd!(arg_tys[2], SimdThird);
1971
1972        // Of the same length:
1973        require!(
1974            in_len == element_len1,
1975            InvalidMonomorphization::SecondArgumentLength {
1976                span,
1977                name,
1978                in_len,
1979                in_ty,
1980                arg_ty: arg_tys[1],
1981                out_len: element_len1
1982            }
1983        );
1984        require!(
1985            in_len == element_len2,
1986            InvalidMonomorphization::ThirdArgumentLength {
1987                span,
1988                name,
1989                in_len,
1990                in_ty,
1991                arg_ty: arg_tys[2],
1992                out_len: element_len2
1993            }
1994        );
1995
1996        require!(
1997            matches!(
1998                *element_ty1.kind(),
1999                ty::RawPtr(p_ty, p_mutbl)
2000                    if p_ty == in_elem && p_mutbl.is_mut() && p_ty.kind() == element_ty0.kind()
2001            ),
2002            InvalidMonomorphization::ExpectedElementType {
2003                span,
2004                name,
2005                expected_element: element_ty1,
2006                second_arg: arg_tys[1],
2007                in_elem,
2008                in_ty,
2009                mutability: ExpectedPointerMutability::Mut,
2010            }
2011        );
2012
2013        // The element type of the third argument must be a signed integer type of any width:
2014        let mask_elem_bitwidth = require_int_ty!(
2015            element_ty2.kind(),
2016            InvalidMonomorphization::ThirdArgElementType {
2017                span,
2018                name,
2019                expected_element: element_ty2,
2020                third_arg: arg_tys[2]
2021            }
2022        );
2023
2024        // Alignment of T, must be a constant integer value:
2025        let alignment_ty = bx.type_i32();
2026        let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
2027
2028        // Truncate the mask vector to a vector of i1s:
2029        let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
2030        let mask_ty = bx.type_vector(bx.type_i1(), in_len);
2031
2032        let ret_t = bx.type_void();
2033
2034        // Type of the vector of pointers:
2035        let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len);
2036        let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len);
2037
2038        // Type of the vector of elements:
2039        let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
2040        let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len);
2041
2042        let llvm_intrinsic =
2043            format!("llvm.masked.scatter.{llvm_elem_vec_str}.{llvm_pointer_vec_str}");
2044        let fn_ty =
2045            bx.type_func(&[llvm_elem_vec_ty, llvm_pointer_vec_ty, alignment_ty, mask_ty], ret_t);
2046        let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
2047        let v = bx.call(
2048            fn_ty,
2049            None,
2050            None,
2051            f,
2052            &[args[0].immediate(), args[1].immediate(), alignment, mask],
2053            None,
2054            None,
2055        );
2056        return Ok(v);
2057    }
2058
2059    macro_rules! arith_red {
2060        ($name:ident : $integer_reduce:ident, $float_reduce:ident, $ordered:expr, $op:ident,
2061         $identity:expr) => {
2062            if name == sym::$name {
2063                require!(
2064                    ret_ty == in_elem,
2065                    InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
2066                );
2067                return match in_elem.kind() {
2068                    ty::Int(_) | ty::Uint(_) => {
2069                        let r = bx.$integer_reduce(args[0].immediate());
2070                        if $ordered {
2071                            // if overflow occurs, the result is the
2072                            // mathematical result modulo 2^n:
2073                            Ok(bx.$op(args[1].immediate(), r))
2074                        } else {
2075                            Ok(bx.$integer_reduce(args[0].immediate()))
2076                        }
2077                    }
2078                    ty::Float(f) => {
2079                        let acc = if $ordered {
2080                            // ordered arithmetic reductions take an accumulator
2081                            args[1].immediate()
2082                        } else {
2083                            // unordered arithmetic reductions use the identity accumulator
2084                            match f.bit_width() {
2085                                32 => bx.const_real(bx.type_f32(), $identity),
2086                                64 => bx.const_real(bx.type_f64(), $identity),
2087                                v => return_error!(
2088                                    InvalidMonomorphization::UnsupportedSymbolOfSize {
2089                                        span,
2090                                        name,
2091                                        symbol: sym::$name,
2092                                        in_ty,
2093                                        in_elem,
2094                                        size: v,
2095                                        ret_ty
2096                                    }
2097                                ),
2098                            }
2099                        };
2100                        Ok(bx.$float_reduce(acc, args[0].immediate()))
2101                    }
2102                    _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
2103                        span,
2104                        name,
2105                        symbol: sym::$name,
2106                        in_ty,
2107                        in_elem,
2108                        ret_ty
2109                    }),
2110                };
2111            }
2112        };
2113    }
2114
2115    arith_red!(simd_reduce_add_ordered: vector_reduce_add, vector_reduce_fadd, true, add, -0.0);
2116    arith_red!(simd_reduce_mul_ordered: vector_reduce_mul, vector_reduce_fmul, true, mul, 1.0);
2117    arith_red!(
2118        simd_reduce_add_unordered: vector_reduce_add,
2119        vector_reduce_fadd_reassoc,
2120        false,
2121        add,
2122        -0.0
2123    );
2124    arith_red!(
2125        simd_reduce_mul_unordered: vector_reduce_mul,
2126        vector_reduce_fmul_reassoc,
2127        false,
2128        mul,
2129        1.0
2130    );
2131
2132    macro_rules! minmax_red {
2133        ($name:ident: $int_red:ident, $float_red:ident) => {
2134            if name == sym::$name {
2135                require!(
2136                    ret_ty == in_elem,
2137                    InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
2138                );
2139                return match in_elem.kind() {
2140                    ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)),
2141                    ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)),
2142                    ty::Float(_f) => Ok(bx.$float_red(args[0].immediate())),
2143                    _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
2144                        span,
2145                        name,
2146                        symbol: sym::$name,
2147                        in_ty,
2148                        in_elem,
2149                        ret_ty
2150                    }),
2151                };
2152            }
2153        };
2154    }
2155
2156    minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin);
2157    minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax);
2158
2159    macro_rules! bitwise_red {
2160        ($name:ident : $red:ident, $boolean:expr) => {
2161            if name == sym::$name {
2162                let input = if !$boolean {
2163                    require!(
2164                        ret_ty == in_elem,
2165                        InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
2166                    );
2167                    args[0].immediate()
2168                } else {
2169                    let bitwidth = match in_elem.kind() {
2170                        ty::Int(i) => {
2171                            i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits())
2172                        }
2173                        ty::Uint(i) => {
2174                            i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits())
2175                        }
2176                        _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
2177                            span,
2178                            name,
2179                            symbol: sym::$name,
2180                            in_ty,
2181                            in_elem,
2182                            ret_ty
2183                        }),
2184                    };
2185
2186                    vector_mask_to_bitmask(bx, args[0].immediate(), bitwidth, in_len as _)
2187                };
2188                return match in_elem.kind() {
2189                    ty::Int(_) | ty::Uint(_) => {
2190                        let r = bx.$red(input);
2191                        Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) })
2192                    }
2193                    _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
2194                        span,
2195                        name,
2196                        symbol: sym::$name,
2197                        in_ty,
2198                        in_elem,
2199                        ret_ty
2200                    }),
2201                };
2202            }
2203        };
2204    }
2205
2206    bitwise_red!(simd_reduce_and: vector_reduce_and, false);
2207    bitwise_red!(simd_reduce_or: vector_reduce_or, false);
2208    bitwise_red!(simd_reduce_xor: vector_reduce_xor, false);
2209    bitwise_red!(simd_reduce_all: vector_reduce_and, true);
2210    bitwise_red!(simd_reduce_any: vector_reduce_or, true);
2211
2212    if name == sym::simd_cast_ptr {
2213        let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
2214        require!(
2215            in_len == out_len,
2216            InvalidMonomorphization::ReturnLengthInputType {
2217                span,
2218                name,
2219                in_len,
2220                in_ty,
2221                ret_ty,
2222                out_len
2223            }
2224        );
2225
2226        match in_elem.kind() {
2227            ty::RawPtr(p_ty, _) => {
2228                let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| {
2229                    bx.tcx.normalize_erasing_regions(bx.typing_env(), ty)
2230                });
2231                require!(
2232                    metadata.is_unit(),
2233                    InvalidMonomorphization::CastWidePointer { span, name, ty: in_elem }
2234                );
2235            }
2236            _ => {
2237                return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem })
2238            }
2239        }
2240        match out_elem.kind() {
2241            ty::RawPtr(p_ty, _) => {
2242                let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| {
2243                    bx.tcx.normalize_erasing_regions(bx.typing_env(), ty)
2244                });
2245                require!(
2246                    metadata.is_unit(),
2247                    InvalidMonomorphization::CastWidePointer { span, name, ty: out_elem }
2248                );
2249            }
2250            _ => {
2251                return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem })
2252            }
2253        }
2254
2255        return Ok(args[0].immediate());
2256    }
2257
2258    if name == sym::simd_expose_provenance {
2259        let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
2260        require!(
2261            in_len == out_len,
2262            InvalidMonomorphization::ReturnLengthInputType {
2263                span,
2264                name,
2265                in_len,
2266                in_ty,
2267                ret_ty,
2268                out_len
2269            }
2270        );
2271
2272        match in_elem.kind() {
2273            ty::RawPtr(_, _) => {}
2274            _ => {
2275                return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem })
2276            }
2277        }
2278        match out_elem.kind() {
2279            ty::Uint(ty::UintTy::Usize) => {}
2280            _ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: out_elem }),
2281        }
2282
2283        return Ok(bx.ptrtoint(args[0].immediate(), llret_ty));
2284    }
2285
2286    if name == sym::simd_with_exposed_provenance {
2287        let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
2288        require!(
2289            in_len == out_len,
2290            InvalidMonomorphization::ReturnLengthInputType {
2291                span,
2292                name,
2293                in_len,
2294                in_ty,
2295                ret_ty,
2296                out_len
2297            }
2298        );
2299
2300        match in_elem.kind() {
2301            ty::Uint(ty::UintTy::Usize) => {}
2302            _ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: in_elem }),
2303        }
2304        match out_elem.kind() {
2305            ty::RawPtr(_, _) => {}
2306            _ => {
2307                return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem })
2308            }
2309        }
2310
2311        return Ok(bx.inttoptr(args[0].immediate(), llret_ty));
2312    }
2313
2314    if name == sym::simd_cast || name == sym::simd_as {
2315        let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
2316        require!(
2317            in_len == out_len,
2318            InvalidMonomorphization::ReturnLengthInputType {
2319                span,
2320                name,
2321                in_len,
2322                in_ty,
2323                ret_ty,
2324                out_len
2325            }
2326        );
2327        // casting cares about nominal type, not just structural type
2328        if in_elem == out_elem {
2329            return Ok(args[0].immediate());
2330        }
2331
2332        #[derive(Copy, Clone)]
2333        enum Sign {
2334            Unsigned,
2335            Signed,
2336        }
2337        use Sign::*;
2338
2339        enum Style {
2340            Float,
2341            Int(Sign),
2342            Unsupported,
2343        }
2344
2345        let (in_style, in_width) = match in_elem.kind() {
2346            // vectors of pointer-sized integers should've been
2347            // disallowed before here, so this unwrap is safe.
2348            ty::Int(i) => (
2349                Style::Int(Signed),
2350                i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
2351            ),
2352            ty::Uint(u) => (
2353                Style::Int(Unsigned),
2354                u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
2355            ),
2356            ty::Float(f) => (Style::Float, f.bit_width()),
2357            _ => (Style::Unsupported, 0),
2358        };
2359        let (out_style, out_width) = match out_elem.kind() {
2360            ty::Int(i) => (
2361                Style::Int(Signed),
2362                i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
2363            ),
2364            ty::Uint(u) => (
2365                Style::Int(Unsigned),
2366                u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
2367            ),
2368            ty::Float(f) => (Style::Float, f.bit_width()),
2369            _ => (Style::Unsupported, 0),
2370        };
2371
2372        match (in_style, out_style) {
2373            (Style::Int(sign), Style::Int(_)) => {
2374                return Ok(match in_width.cmp(&out_width) {
2375                    Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty),
2376                    Ordering::Equal => args[0].immediate(),
2377                    Ordering::Less => match sign {
2378                        Sign::Signed => bx.sext(args[0].immediate(), llret_ty),
2379                        Sign::Unsigned => bx.zext(args[0].immediate(), llret_ty),
2380                    },
2381                });
2382            }
2383            (Style::Int(Sign::Signed), Style::Float) => {
2384                return Ok(bx.sitofp(args[0].immediate(), llret_ty));
2385            }
2386            (Style::Int(Sign::Unsigned), Style::Float) => {
2387                return Ok(bx.uitofp(args[0].immediate(), llret_ty));
2388            }
2389            (Style::Float, Style::Int(sign)) => {
2390                return Ok(match (sign, name == sym::simd_as) {
2391                    (Sign::Unsigned, false) => bx.fptoui(args[0].immediate(), llret_ty),
2392                    (Sign::Signed, false) => bx.fptosi(args[0].immediate(), llret_ty),
2393                    (_, true) => bx.cast_float_to_int(
2394                        matches!(sign, Sign::Signed),
2395                        args[0].immediate(),
2396                        llret_ty,
2397                    ),
2398                });
2399            }
2400            (Style::Float, Style::Float) => {
2401                return Ok(match in_width.cmp(&out_width) {
2402                    Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty),
2403                    Ordering::Equal => args[0].immediate(),
2404                    Ordering::Less => bx.fpext(args[0].immediate(), llret_ty),
2405                });
2406            }
2407            _ => { /* Unsupported. Fallthrough. */ }
2408        }
2409        return_error!(InvalidMonomorphization::UnsupportedCast {
2410            span,
2411            name,
2412            in_ty,
2413            in_elem,
2414            ret_ty,
2415            out_elem
2416        });
2417    }
2418    macro_rules! arith_binary {
2419        ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
2420            $(if name == sym::$name {
2421                match in_elem.kind() {
2422                    $($(ty::$p(_))|* => {
2423                        return Ok(bx.$call(args[0].immediate(), args[1].immediate()))
2424                    })*
2425                    _ => {},
2426                }
2427                return_error!(
2428                    InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem }
2429                );
2430            })*
2431        }
2432    }
2433    arith_binary! {
2434        simd_add: Uint, Int => add, Float => fadd;
2435        simd_sub: Uint, Int => sub, Float => fsub;
2436        simd_mul: Uint, Int => mul, Float => fmul;
2437        simd_div: Uint => udiv, Int => sdiv, Float => fdiv;
2438        simd_rem: Uint => urem, Int => srem, Float => frem;
2439        simd_shl: Uint, Int => shl;
2440        simd_shr: Uint => lshr, Int => ashr;
2441        simd_and: Uint, Int => and;
2442        simd_or: Uint, Int => or;
2443        simd_xor: Uint, Int => xor;
2444        simd_fmax: Float => maxnum;
2445        simd_fmin: Float => minnum;
2446
2447    }
2448    macro_rules! arith_unary {
2449        ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
2450            $(if name == sym::$name {
2451                match in_elem.kind() {
2452                    $($(ty::$p(_))|* => {
2453                        return Ok(bx.$call(args[0].immediate()))
2454                    })*
2455                    _ => {},
2456                }
2457                return_error!(
2458                    InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem }
2459                );
2460            })*
2461        }
2462    }
2463    arith_unary! {
2464        simd_neg: Int => neg, Float => fneg;
2465    }
2466
2467    // Unary integer intrinsics
2468    if matches!(
2469        name,
2470        sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_ctpop | sym::simd_cttz
2471    ) {
2472        let vec_ty = bx.cx.type_vector(
2473            match *in_elem.kind() {
2474                ty::Int(i) => bx.cx.type_int_from_ty(i),
2475                ty::Uint(i) => bx.cx.type_uint_from_ty(i),
2476                _ => return_error!(InvalidMonomorphization::UnsupportedOperation {
2477                    span,
2478                    name,
2479                    in_ty,
2480                    in_elem
2481                }),
2482            },
2483            in_len as u64,
2484        );
2485        let intrinsic_name = match name {
2486            sym::simd_bswap => "bswap",
2487            sym::simd_bitreverse => "bitreverse",
2488            sym::simd_ctlz => "ctlz",
2489            sym::simd_ctpop => "ctpop",
2490            sym::simd_cttz => "cttz",
2491            _ => unreachable!(),
2492        };
2493        let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits();
2494        let llvm_intrinsic = &format!("llvm.{}.v{}i{}", intrinsic_name, in_len, int_size,);
2495
2496        return match name {
2497            // byte swap is no-op for i8/u8
2498            sym::simd_bswap if int_size == 8 => Ok(args[0].immediate()),
2499            sym::simd_ctlz | sym::simd_cttz => {
2500                // for the (int, i1 immediate) pair, the second arg adds `(0, true) => poison`
2501                let fn_ty = bx.type_func(&[vec_ty, bx.type_i1()], vec_ty);
2502                let dont_poison_on_zero = bx.const_int(bx.type_i1(), 0);
2503                let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
2504                Ok(bx.call(
2505                    fn_ty,
2506                    None,
2507                    None,
2508                    f,
2509                    &[args[0].immediate(), dont_poison_on_zero],
2510                    None,
2511                    None,
2512                ))
2513            }
2514            sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctpop => {
2515                // simple unary argument cases
2516                let fn_ty = bx.type_func(&[vec_ty], vec_ty);
2517                let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
2518                Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None))
2519            }
2520            _ => unreachable!(),
2521        };
2522    }
2523
2524    if name == sym::simd_arith_offset {
2525        // This also checks that the first operand is a ptr type.
2526        let pointee = in_elem.builtin_deref(true).unwrap_or_else(|| {
2527            span_bug!(span, "must be called with a vector of pointer types as first argument")
2528        });
2529        let layout = bx.layout_of(pointee);
2530        let ptrs = args[0].immediate();
2531        // The second argument must be a ptr-sized integer.
2532        // (We don't care about the signedness, this is wrapping anyway.)
2533        let (_offsets_len, offsets_elem) = arg_tys[1].simd_size_and_type(bx.tcx());
2534        if !matches!(offsets_elem.kind(), ty::Int(ty::IntTy::Isize) | ty::Uint(ty::UintTy::Usize)) {
2535            span_bug!(
2536                span,
2537                "must be called with a vector of pointer-sized integers as second argument"
2538            );
2539        }
2540        let offsets = args[1].immediate();
2541
2542        return Ok(bx.gep(bx.backend_type(layout), ptrs, &[offsets]));
2543    }
2544
2545    if name == sym::simd_saturating_add || name == sym::simd_saturating_sub {
2546        let lhs = args[0].immediate();
2547        let rhs = args[1].immediate();
2548        let is_add = name == sym::simd_saturating_add;
2549        let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _;
2550        let (signed, elem_width, elem_ty) = match *in_elem.kind() {
2551            ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)),
2552            ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)),
2553            _ => {
2554                return_error!(InvalidMonomorphization::ExpectedVectorElementType {
2555                    span,
2556                    name,
2557                    expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1,
2558                    vector_type: arg_tys[0]
2559                });
2560            }
2561        };
2562        let llvm_intrinsic = &format!(
2563            "llvm.{}{}.sat.v{}i{}",
2564            if signed { 's' } else { 'u' },
2565            if is_add { "add" } else { "sub" },
2566            in_len,
2567            elem_width
2568        );
2569        let vec_ty = bx.cx.type_vector(elem_ty, in_len as u64);
2570
2571        let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty);
2572        let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
2573        let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None, None);
2574        return Ok(v);
2575    }
2576
2577    span_bug!(span, "unknown SIMD intrinsic");
2578}