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