rustc_codegen_llvm/
intrinsic.rs

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