Skip to main content

rustc_codegen_llvm/
intrinsic.rs

1use std::cmp::Ordering;
2use std::ffi::c_uint;
3use std::{assert_matches, iter, ptr};
4
5use rustc_abi::{
6    AddressSpace, Align, BackendRepr, CVariadicStatus, Float, HasDataLayout, NumScalableVectors,
7    Primitive, Size, WrappingRange,
8};
9use rustc_codegen_ssa::RetagInfo;
10use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
11use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
12use rustc_codegen_ssa::diagnostics::{ExpectedPointerMutability, InvalidMonomorphization};
13use rustc_codegen_ssa::mir::IntrinsicResult;
14use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
15use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue};
16use rustc_codegen_ssa::traits::*;
17use rustc_hir as hir;
18use rustc_hir::def_id::LOCAL_CRATE;
19use rustc_hir::find_attr;
20use rustc_lint_defs::builtin::DEPRECATED_LLVM_INTRINSIC;
21use rustc_middle::mir::BinOp;
22use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf};
23use rustc_middle::ty::offload_meta::OffloadMetadata;
24use rustc_middle::ty::{self, GenericArgsRef, Instance, SimdAlign, Ty, TyCtxt, TypingEnv};
25use rustc_middle::{bug, span_bug};
26use rustc_session::diagnostics::feature_err;
27use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
28use rustc_structures::CrateType;
29use rustc_symbol_mangling::{
30    mangle_internal_symbol, mangle_offload_export, symbol_name_for_instance_in_crate,
31};
32use rustc_target::callconv::PassMode;
33use rustc_target::spec::Arch;
34use tracing::debug;
35
36use crate::abi::FnAbiLlvmExt;
37use crate::builder::Builder;
38use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call};
39use crate::builder::gpu_offload::{self, OffloadKernelDims, declare_omp_get_num_devices};
40use crate::context::CodegenCx;
41use crate::declare::declare_raw_fn;
42use crate::diagnostics::{
43    AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, IntrinsicWrongArch,
44    OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic,
45};
46use crate::intrinsic::ty::typetree::fnc_typetrees;
47use crate::llvm::{self, Attribute, AttributePlace, Type, Value};
48use crate::type_of::LayoutLlvmExt;
49use crate::va_arg::emit_va_arg;
50
51fn call_simple_intrinsic<'ll, 'tcx>(
52    bx: &mut Builder<'_, 'll, 'tcx>,
53    name: Symbol,
54    args: &[OperandRef<'tcx, &'ll Value>],
55) -> Option<&'ll Value> {
56    let llvm_version = crate::llvm_util::get_version();
57    // minimum/maximum were broken for f64/f128 before
58    // <https://github.com/llvm/llvm-project/commit/56385af687c3a7a1f67716fb3f819336789a8cab>.
59    // We use the fallback body there.
60    let fixed_minmax = llvm_version >= (23, 0, 0);
61
62    let (base_name, type_params): (&'static str, &[&'ll Type]) = match name {
63        sym::sqrtf16 => ("llvm.sqrt", &[bx.type_f16()]),
64        sym::sqrtf32 => ("llvm.sqrt", &[bx.type_f32()]),
65        sym::sqrtf64 => ("llvm.sqrt", &[bx.type_f64()]),
66        sym::sqrtf128 => ("llvm.sqrt", &[bx.type_f128()]),
67
68        sym::powif16 => ("llvm.powi", &[bx.type_f16(), bx.type_i32()]),
69        sym::powif32 => ("llvm.powi", &[bx.type_f32(), bx.type_i32()]),
70        sym::powif64 => ("llvm.powi", &[bx.type_f64(), bx.type_i32()]),
71        sym::powif128 => ("llvm.powi", &[bx.type_f128(), bx.type_i32()]),
72
73        sym::powf16 => ("llvm.pow", &[bx.type_f16()]),
74        sym::powf32 => ("llvm.pow", &[bx.type_f32()]),
75        sym::powf64 => ("llvm.pow", &[bx.type_f64()]),
76        sym::powf128 => ("llvm.pow", &[bx.type_f128()]),
77
78        sym::fmaf16 => ("llvm.fma", &[bx.type_f16()]),
79        sym::fmaf32 => ("llvm.fma", &[bx.type_f32()]),
80        sym::fmaf64 => ("llvm.fma", &[bx.type_f64()]),
81        sym::fmaf128 => ("llvm.fma", &[bx.type_f128()]),
82
83        sym::fmuladdf16 => ("llvm.fmuladd", &[bx.type_f16()]),
84        sym::fmuladdf32 => ("llvm.fmuladd", &[bx.type_f32()]),
85        sym::fmuladdf64 => ("llvm.fmuladd", &[bx.type_f64()]),
86        sym::fmuladdf128 => ("llvm.fmuladd", &[bx.type_f128()]),
87
88        sym::minimumf16 => ("llvm.minimum", &[bx.type_f16()]),
89        sym::minimumf32 => ("llvm.minimum", &[bx.type_f32()]),
90        sym::minimumf64 if fixed_minmax => ("llvm.minimum", &[bx.type_f64()]),
91        sym::minimumf128 if fixed_minmax => ("llvm.minimum", &[bx.type_f128()]),
92
93        sym::maximumf16 => ("llvm.maximum", &[bx.type_f16()]),
94        sym::maximumf32 => ("llvm.maximum", &[bx.type_f32()]),
95        sym::maximumf64 if fixed_minmax => ("llvm.maximum", &[bx.type_f64()]),
96        sym::maximumf128 if fixed_minmax => ("llvm.maximum", &[bx.type_f128()]),
97
98        sym::copysignf16 => ("llvm.copysign", &[bx.type_f16()]),
99        sym::copysignf32 => ("llvm.copysign", &[bx.type_f32()]),
100        sym::copysignf64 => ("llvm.copysign", &[bx.type_f64()]),
101        sym::copysignf128 => ("llvm.copysign", &[bx.type_f128()]),
102
103        sym::floorf16 => ("llvm.floor", &[bx.type_f16()]),
104        sym::floorf32 => ("llvm.floor", &[bx.type_f32()]),
105        sym::floorf64 => ("llvm.floor", &[bx.type_f64()]),
106        sym::floorf128 => ("llvm.floor", &[bx.type_f128()]),
107
108        sym::ceilf16 => ("llvm.ceil", &[bx.type_f16()]),
109        sym::ceilf32 => ("llvm.ceil", &[bx.type_f32()]),
110        sym::ceilf64 => ("llvm.ceil", &[bx.type_f64()]),
111        sym::ceilf128 => ("llvm.ceil", &[bx.type_f128()]),
112
113        sym::truncf16 => ("llvm.trunc", &[bx.type_f16()]),
114        sym::truncf32 => ("llvm.trunc", &[bx.type_f32()]),
115        sym::truncf64 => ("llvm.trunc", &[bx.type_f64()]),
116        sym::truncf128 => ("llvm.trunc", &[bx.type_f128()]),
117
118        // We could use any of `rint`, `nearbyint`, or `roundeven`
119        // for this -- they are all identical in semantics when
120        // assuming the default FP environment.
121        // `rint` is what we used for $forever.
122        sym::round_ties_even_f16 => ("llvm.rint", &[bx.type_f16()]),
123        sym::round_ties_even_f32 => ("llvm.rint", &[bx.type_f32()]),
124        sym::round_ties_even_f64 => ("llvm.rint", &[bx.type_f64()]),
125        sym::round_ties_even_f128 => ("llvm.rint", &[bx.type_f128()]),
126
127        sym::roundf16 => ("llvm.round", &[bx.type_f16()]),
128        sym::roundf32 => ("llvm.round", &[bx.type_f32()]),
129        sym::roundf64 => ("llvm.round", &[bx.type_f64()]),
130        sym::roundf128 => ("llvm.round", &[bx.type_f128()]),
131
132        _ => return None,
133    };
134    Some(bx.call_intrinsic(
135        base_name,
136        type_params,
137        &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
138    ))
139}
140
141impl<'ll, 'tcx> Builder<'_, 'll, 'tcx> {
142    fn black_box(&mut self, result: PlaceRef<'tcx, &'ll Value>, span: Span) {
143        let result_val_span = [result.val.llval];
144        // We need to "use" the argument in some way LLVM can't introspect, and on
145        // targets that support it we can typically leverage inline assembly to do
146        // this. LLVM's interpretation of inline assembly is that it's, well, a black
147        // box. This isn't the greatest implementation since it probably deoptimizes
148        // more than we want, but it's so far good enough.
149        //
150        // For zero-sized types, the location pointed to by the result may be
151        // uninitialized. Do not "use" the result in this case; instead just clobber
152        // the memory.
153        let (constraint, inputs): (&str, &[_]) = if result.layout.is_zst() {
154            ("~{memory}", &[])
155        } else {
156            ("r,~{memory}", &result_val_span)
157        };
158        crate::asm::inline_asm_call(
159            self,
160            "",
161            constraint,
162            inputs,
163            self.type_void(),
164            &[],
165            true,
166            false,
167            llvm::AsmDialect::Att,
168            &[span],
169            false,
170            None,
171            None,
172        )
173        .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`"));
174    }
175}
176
177impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
178    fn codegen_intrinsic_call(
179        &mut self,
180        instance: ty::Instance<'tcx>,
181        args: &[OperandRef<'tcx, &'ll Value>],
182        result_layout: ty::layout::TyAndLayout<'tcx>,
183        result_place: Option<PlaceValue<&'ll Value>>,
184        span: Span,
185    ) -> IntrinsicResult<'tcx, &'ll Value> {
186        let tcx = self.tcx;
187        let llvm_version = crate::llvm_util::get_version();
188
189        let name = tcx.item_name(instance.def_id());
190        let fn_args = instance.args;
191
192        let simple = call_simple_intrinsic(self, name, args);
193        let llval = match name {
194            _ if simple.is_some() => simple.unwrap(),
195            // Need at least LLVM 22 for `min/maximumnum` to not crash LLVM.
196            sym::minimum_number_nsz_f16
197            | sym::minimum_number_nsz_f32
198            | sym::minimum_number_nsz_f64
199            | sym::minimum_number_nsz_f128
200            | sym::maximum_number_nsz_f16
201            | sym::maximum_number_nsz_f32
202            | sym::maximum_number_nsz_f64
203            | sym::maximum_number_nsz_f128
204                if llvm_version >= (22, 0, 0) =>
205            {
206                let intrinsic_name = if name.as_str().starts_with("min") {
207                    "llvm.minimumnum"
208                } else {
209                    "llvm.maximumnum"
210                };
211                let call = self.call_intrinsic(
212                    intrinsic_name,
213                    &[args[0].layout.immediate_llvm_type(self.cx)],
214                    &[args[0].immediate(), args[1].immediate()],
215                );
216                // `nsz` on minimumnum/maximumnum is special: its only effect is to make
217                // signed-zero ordering non-deterministic.
218                unsafe { llvm::LLVMRustSetNoSignedZeros(call) };
219                call
220            }
221            sym::ptr_mask => {
222                let ptr = args[0].immediate();
223                self.call_intrinsic(
224                    "llvm.ptrmask",
225                    &[self.val_ty(ptr), self.type_isize()],
226                    &[ptr, args[1].immediate()],
227                )
228            }
229            sym::autodiff => {
230                return codegen_autodiff(self, instance, args, result_layout, result_place);
231            }
232            sym::offload => {
233                if tcx.sess.opts.unstable_opts.offload.is_empty() {
234                    let _ = tcx.dcx().emit_err(OffloadWithoutEnable);
235                }
236
237                if tcx.sess.lto() != rustc_session::config::Lto::Fat {
238                    let _ = tcx.dcx().emit_err(OffloadWithoutFatLTO);
239                }
240
241                codegen_offload(self, tcx, instance, args);
242                // offload *has* a return type, but somehow works without mentioning the place
243                return IntrinsicResult::WroteIntoPlace;
244            }
245            sym::offload_get_num_devices => {
246                let (fn_decl, fn_ty) = declare_omp_get_num_devices(self.cx);
247
248                let llval =
249                    self.call(fn_ty, None, None, fn_decl, ReturnSlot::Direct, &[], None, None);
250
251                return IntrinsicResult::Operand(OperandValue::Immediate(llval));
252            }
253            sym::is_val_statically_known => {
254                if let OperandValue::Immediate(imm) = args[0].val {
255                    self.call_intrinsic(
256                        "llvm.is.constant",
257                        &[args[0].layout.immediate_llvm_type(self.cx)],
258                        &[imm],
259                    )
260                } else {
261                    self.const_bool(false)
262                }
263            }
264            sym::select_unpredictable => {
265                let cond = args[0].immediate();
266                {
    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);
267                let select = |bx: &mut Self, true_val, false_val| {
268                    let result = bx.select(cond, true_val, false_val);
269                    bx.set_unpredictable(&result);
270                    result
271                };
272                match (args[1].val, args[2].val) {
273                    (OperandValue::Ref(true_val), OperandValue::Ref(false_val)) => {
274                        if !true_val.llextra.is_none() {
    ::core::panicking::panic("assertion failed: true_val.llextra.is_none()")
};assert!(true_val.llextra.is_none());
275                        if !false_val.llextra.is_none() {
    ::core::panicking::panic("assertion failed: false_val.llextra.is_none()")
};assert!(false_val.llextra.is_none());
276                        {
    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);
277                        let ptr = select(self, true_val.llval, false_val.llval);
278                        let selected =
279                            OperandValue::Ref(PlaceValue::new_sized(ptr, true_val.align));
280                        let result = PlaceRef { val: result_place.unwrap(), layout: result_layout };
281                        selected.store(self, result);
282                        return IntrinsicResult::WroteIntoPlace;
283                    }
284                    (OperandValue::Immediate(_), OperandValue::Immediate(_))
285                    | (OperandValue::Pair(_, _), OperandValue::Pair(_, _)) => {
286                        let true_val = args[1].immediate_or_packed_pair(self);
287                        let false_val = args[2].immediate_or_packed_pair(self);
288                        select(self, true_val, false_val)
289                    }
290                    (OperandValue::ZeroSized, OperandValue::ZeroSized) => {
291                        return IntrinsicResult::Operand(OperandValue::ZeroSized);
292                    }
293                    _ => ::rustc_middle::util::bug::span_bug_fmt(span,
    format_args!("Incompatible OperandValue for select_unpredictable"))span_bug!(span, "Incompatible OperandValue for select_unpredictable"),
294                }
295            }
296            sym::catch_unwind => catch_unwind_intrinsic(
297                self,
298                args[0].immediate(),
299                args[1].immediate(),
300                args[2].immediate(),
301            ),
302            sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]),
303            sym::va_arg => {
304                let target = &self.cx.tcx.sess.target;
305                let stability = target.supports_c_variadic_definitions();
306                if let CVariadicStatus::Unstable { feature } = stability
307                    && !self.tcx.features().enabled(feature)
308                {
309                    let msg =
310                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("C-variadic function definitions on this target are unstable"))
    })format!("C-variadic function definitions on this target are unstable");
311                    feature_err(&*self.sess(), feature, span, msg).emit();
312                }
313
314                let BackendRepr::Scalar(scalar) = result_layout.backend_repr else {
315                    ::rustc_middle::util::bug::bug_fmt(format_args!("the va_arg intrinsic does not support non-scalar types"))bug!("the va_arg intrinsic does not support non-scalar types")
316                };
317
318                // We reject types that would never be passed as varargs in C because
319                // they get promoted to a larger type, specifically integers smaller than
320                // c_int and float type smaller than c_double.
321                match scalar.primitive() {
322                    Primitive::Pointer(_) => {
323                        // Pointers are always OK.
324                    }
325                    Primitive::Int(..) => {
326                        let int_width = self.cx().size_of(result_layout.ty).bits();
327                        let target_c_int_width = self.cx().sess().target.options.c_int_width;
328                        if int_width < u64::from(target_c_int_width) {
329                            // Smaller integer types are automatically promototed and `va_arg`
330                            // should not be called on them.
331                            ::rustc_middle::util::bug::bug_fmt(format_args!("va_arg got i{0} but needs at least c_int (an i{1})",
        int_width, target_c_int_width));bug!(
332                                "va_arg got i{} but needs at least c_int (an i{})",
333                                int_width,
334                                target_c_int_width
335                            );
336                        }
337                    }
338                    Primitive::Float(Float::F16) => {
339                        ::rustc_middle::util::bug::bug_fmt(format_args!("the va_arg intrinsic does not support `f16`"))bug!("the va_arg intrinsic does not support `f16`")
340                    }
341                    Primitive::Float(Float::F32) => {
342                        // c_double is actually f32 on avr.
343                        if self.cx().sess().target.arch != Arch::Avr {
344                            ::rustc_middle::util::bug::bug_fmt(format_args!("the va_arg intrinsic does not support `f32` on this target"))bug!("the va_arg intrinsic does not support `f32` on this target")
345                        }
346                    }
347                    Primitive::Float(Float::F64) => {
348                        // 64-bit floats are always OK.
349                    }
350                    Primitive::Float(Float::F128) => {
351                        // FIXME(f128) figure out whether we should support this.
352                        ::rustc_middle::util::bug::bug_fmt(format_args!("the va_arg intrinsic does not support `f128`"))bug!("the va_arg intrinsic does not support `f128`")
353                    }
354                }
355
356                emit_va_arg(self, args[0], result_layout.ty)
357            }
358
359            sym::volatile_load | sym::unaligned_volatile_load => {
360                // Note that we cannot just load the `llvm_type` because we should never load non-scalars.
361                // Trying to do so blows up horribly in some cases -- for example loading a
362                // `MaybeUninint<&dyn Trait>` would load as `{ [i64x2] }` which gives assertions later
363                // (if we're lucky) from things not being pointers that ought to be.
364                let ptr = args[0].immediate();
365                let abi_align = result_layout.align.abi;
366                let ptr_align = if name == sym::volatile_load { abi_align } else { Align::ONE };
367                let need_black_box = llvm_version < (23, 0, 0);
368                if result_layout.is_zst() {
369                    return IntrinsicResult::Operand(OperandValue::ZeroSized);
370                } else if let BackendRepr::Scalar(scalar) = result_layout.backend_repr
371                    && !need_black_box
372                {
373                    let load = self.volatile_load(self.type_from_scalar(scalar), ptr, ptr_align);
374                    self.to_immediate_scalar(load, scalar)
375                } else {
376                    // One day Rust will probably want to define how we split up a volatile load
377                    // of something that's *not* just an ordinary scalar, but for now we can just
378                    // use an LLVM integer type of the correct width and let it split it however.
379                    let llty = self.type_ix(result_layout.size.bits());
380                    let temp = if let Some(result_place) = result_place {
381                        PlaceRef { val: result_place, layout: result_layout }
382                    } else {
383                        PlaceRef::alloca(self, result_layout)
384                    };
385                    let llval = self.volatile_load(llty, ptr, ptr_align);
386                    self.store(llval, temp.val.llval, abi_align);
387                    if need_black_box {
388                        // LLVM up until v22 considers volatile reads `willreturn` and hence can
389                        // move UB from further down up across this read. To prevent that, insert an
390                        // inline asm block that, as far as LLVM is concerned, might not terminate,
391                        // and hence should prevent such reordering.
392                        self.black_box(temp, span);
393                    }
394                    return if result_place.is_none() {
395                        IntrinsicResult::Operand(self.load_operand(temp).val)
396                    } else {
397                        IntrinsicResult::WroteIntoPlace
398                    };
399                }
400            }
401            sym::prefetch_read_data
402            | sym::prefetch_write_data
403            | sym::prefetch_read_instruction
404            | sym::prefetch_write_instruction => {
405                let (rw, cache_type) = match name {
406                    sym::prefetch_read_data => (0, 1),
407                    sym::prefetch_write_data => (1, 1),
408                    sym::prefetch_read_instruction => (0, 0),
409                    sym::prefetch_write_instruction => (1, 0),
410                    _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
411                };
412                let ptr = args[0].immediate();
413                let locality = fn_args.const_at(1).to_leaf().to_i32();
414                self.call_intrinsic(
415                    "llvm.prefetch.p0",
416                    &[self.val_ty(ptr)],
417                    &[
418                        ptr,
419                        self.const_i32(rw),
420                        self.const_i32(locality),
421                        self.const_i32(cache_type),
422                    ],
423                );
424                return IntrinsicResult::Operand(OperandValue::ZeroSized);
425            }
426            sym::carrying_mul_add => {
427                let (size, signed) = fn_args.type_at(0).int_size_and_signed(self.tcx);
428
429                let wide_llty = self.type_ix(size.bits() * 2);
430                let args = args.as_array().unwrap();
431                let [a, b, c, d] = args.map(|a| self.intcast(a.immediate(), wide_llty, signed));
432
433                let wide = if signed {
434                    let prod = self.unchecked_smul(a, b);
435                    let acc = self.unchecked_sadd(prod, c);
436                    self.unchecked_sadd(acc, d)
437                } else {
438                    let prod = self.unchecked_umul(a, b);
439                    let acc = self.unchecked_uadd(prod, c);
440                    self.unchecked_uadd(acc, d)
441                };
442
443                let narrow_llty = self.type_ix(size.bits());
444                let low = self.trunc(wide, narrow_llty);
445                let bits_const = self.const_uint(wide_llty, size.bits());
446                // No need for ashr when signed; LLVM changes it to lshr anyway.
447                let high = self.lshr(wide, bits_const);
448                // FIXME: could be `trunc nuw`, even for signed.
449                let high = self.trunc(high, narrow_llty);
450
451                let pair_llty = self.type_struct(&[narrow_llty, narrow_llty], false);
452                let pair = self.const_poison(pair_llty);
453                let pair = self.insert_value(pair, low, 0);
454                let pair = self.insert_value(pair, high, 1);
455                pair
456            }
457
458            // FIXME move into the branch below when LLVM 22 is the lowest version we support.
459            sym::carryless_mul if llvm_version >= (22, 0, 0) => {
460                let ty = args[0].layout.ty;
461                if !ty.is_integral() {
462                    let err = tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
463                        span,
464                        name,
465                        ty,
466                    });
467                    return IntrinsicResult::Err(err);
468                }
469                let (size, _) = ty.int_size_and_signed(self.tcx);
470                let width = size.bits();
471                let llty = self.type_ix(width);
472
473                let lhs = args[0].immediate();
474                let rhs = args[1].immediate();
475                self.call_intrinsic("llvm.clmul", &[llty], &[lhs, rhs])
476            }
477
478            sym::ctlz
479            | sym::ctlz_nonzero
480            | sym::cttz
481            | sym::cttz_nonzero
482            | sym::ctpop
483            | sym::bswap
484            | sym::bitreverse
485            | sym::integer_max
486            | sym::integer_min
487            | sym::saturating_add
488            | sym::saturating_sub
489            | sym::unchecked_funnel_shl
490            | sym::unchecked_funnel_shr => {
491                let ty = args[0].layout.ty;
492                if !ty.is_integral() {
493                    let err = tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
494                        span,
495                        name,
496                        ty,
497                    });
498                    return IntrinsicResult::Err(err);
499                }
500                let (size, signed) = ty.int_size_and_signed(self.tcx);
501                let width = size.bits();
502                let llty = self.type_ix(width);
503                match name {
504                    sym::ctlz | sym::ctlz_nonzero | sym::cttz | sym::cttz_nonzero => {
505                        let y =
506                            self.const_bool(name == sym::ctlz_nonzero || name == sym::cttz_nonzero);
507                        let llvm_name = if name == sym::ctlz || name == sym::ctlz_nonzero {
508                            "llvm.ctlz"
509                        } else {
510                            "llvm.cttz"
511                        };
512                        let ret =
513                            self.call_intrinsic(llvm_name, &[llty], &[args[0].immediate(), y]);
514                        self.intcast(ret, result_layout.llvm_type(self), false)
515                    }
516                    sym::ctpop => {
517                        let ret =
518                            self.call_intrinsic("llvm.ctpop", &[llty], &[args[0].immediate()]);
519                        self.intcast(ret, result_layout.llvm_type(self), false)
520                    }
521                    sym::bswap => {
522                        if width == 8 {
523                            args[0].immediate() // byte swap a u8/i8 is just a no-op
524                        } else {
525                            self.call_intrinsic("llvm.bswap", &[llty], &[args[0].immediate()])
526                        }
527                    }
528                    sym::bitreverse => {
529                        self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()])
530                    }
531                    sym::integer_min | sym::integer_max => {
532                        let lhs = args[0].immediate();
533                        let rhs = args[1].immediate();
534                        let llvm_name = match (name, signed) {
535                            (sym::integer_max, false) => "llvm.umax",
536                            (sym::integer_max, true) => "llvm.smax",
537                            (sym::integer_min, false) => "llvm.umin",
538                            (sym::integer_min, true) => "llvm.smin",
539                            _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
540                        };
541                        self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs])
542                    }
543                    sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => {
544                        let is_left = name == sym::unchecked_funnel_shl;
545                        let lhs = args[0].immediate();
546                        let rhs = args[1].immediate();
547                        let raw_shift = args[2].immediate();
548                        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' });
549
550                        // llvm expects shift to be the same type as the values, but rust
551                        // always uses `u32`.
552                        let raw_shift = self.intcast(raw_shift, self.val_ty(lhs), false);
553
554                        self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs, raw_shift])
555                    }
556                    sym::saturating_add | sym::saturating_sub => {
557                        let is_add = name == sym::saturating_add;
558                        let lhs = args[0].immediate();
559                        let rhs = args[1].immediate();
560                        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!(
561                            "llvm.{}{}.sat",
562                            if signed { 's' } else { 'u' },
563                            if is_add { "add" } else { "sub" },
564                        );
565                        self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs])
566                    }
567                    _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
568                }
569            }
570
571            sym::fabs
572            | sym::exp
573            | sym::exp2
574            | sym::log
575            | sym::log10
576            | sym::log2
577            | sym::sin
578            | sym::cos => {
579                let ty = args[0].layout.ty;
580                let ty::Float(f) = ty.kind() else {
581                    ::rustc_middle::util::bug::span_bug_fmt(span,
    format_args!("the `{0}` intrinsic requires a floating-point argument, got {1:?}",
        name, ty));span_bug!(
582                        span,
583                        "the `{}` intrinsic requires a floating-point argument, got {:?}",
584                        name,
585                        ty
586                    );
587                };
588                let llty = self.type_float_from_ty(*f);
589                let llvm_name = match name {
590                    sym::fabs => "llvm.fabs",
591                    sym::exp => "llvm.exp",
592                    sym::exp2 => "llvm.exp2",
593                    sym::log => "llvm.log",
594                    sym::log10 => "llvm.log10",
595                    sym::log2 => "llvm.log2",
596                    sym::sin => "llvm.sin",
597                    sym::cos => "llvm.cos",
598                    _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
599                };
600                self.call_intrinsic(
601                    llvm_name,
602                    &[llty],
603                    &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
604                )
605            }
606
607            sym::raw_eq => {
608                use BackendRepr::*;
609                let tp_ty = fn_args.type_at(0);
610                let layout = self.layout_of(tp_ty).layout;
611                let use_integer_compare = match layout.backend_repr() {
612                    Scalar(_) | ScalarPair { a: _, b: _, b_offset: _ } => true,
613                    SimdVector { .. } => false,
614                    SimdScalableVector { .. } => {
615                        let err = tcx.dcx().emit_err(InvalidMonomorphization::NonScalableType {
616                            span,
617                            name: sym::raw_eq,
618                            ty: tp_ty,
619                        });
620                        return IntrinsicResult::Err(err);
621                    }
622                    Memory { .. } => {
623                        // For rusty ABIs, small aggregates are actually passed
624                        // as `RegKind::Integer` (see `FnAbi::adjust_for_abi`),
625                        // so we re-use that same threshold here.
626                        layout.size() <= self.data_layout().pointer_size() * 2
627                    }
628                };
629
630                let a = args[0].immediate();
631                let b = args[1].immediate();
632                if layout.size().bytes() == 0 {
633                    self.const_bool(true)
634                } else if use_integer_compare {
635                    let integer_ty = self.type_ix(layout.size().bits());
636                    let a_val = self.load(integer_ty, a, layout.align().abi);
637                    let b_val = self.load(integer_ty, b, layout.align().abi);
638                    self.icmp(IntPredicate::IntEQ, a_val, b_val)
639                } else {
640                    let n = self.const_usize(layout.size().bytes());
641                    let cmp = self.call_intrinsic("memcmp", &[], &[a, b, n]);
642                    self.icmp(IntPredicate::IntEQ, cmp, self.const_int(self.type_int(), 0))
643                }
644            }
645
646            sym::compare_bytes => {
647                // Here we assume that the `memcmp` provided by the target is a NOP for size 0.
648                let cmp = self.call_intrinsic(
649                    "memcmp",
650                    &[],
651                    &[args[0].immediate(), args[1].immediate(), args[2].immediate()],
652                );
653                // Some targets have `memcmp` returning `i16`, but the intrinsic is always `i32`.
654                self.sext(cmp, self.type_ix(32))
655            }
656
657            sym::black_box => {
658                // This `unwrap` is justified by `intrinsic_call_expects_place_always` declaring
659                // this intrinsic as always needing a return place.
660                let result = PlaceRef { val: result_place.unwrap(), layout: result_layout };
661                args[0].val.store(self, result);
662                self.black_box(result, span);
663
664                // We have copied the value to `result` already.
665                return IntrinsicResult::WroteIntoPlace;
666            }
667
668            sym::gpu_launch_sized_workgroup_mem => {
669                // Generate an anonymous global per call, with these properties:
670                // 1. The global is in the address space for workgroup memory
671                // 2. It is an `external` global
672                // 3. It is correctly aligned for the pointee `T`
673                // All instances of extern addrspace(gpu_workgroup) globals are merged in the LLVM backend.
674                // The name is irrelevant.
675                // See https://docs.nvidia.com/cuda/cuda-c-programming-guide/#shared
676                let name = if llvm_version < (23, 0, 0) && tcx.sess.target.arch == Arch::Nvptx64 {
677                    // The auto-assigned name for extern shared globals in the nvptx backend does
678                    // not compile in ptxas. Workaround this issue by assigning a name.
679                    // Fixed in LLVM 23.
680                    "gpu_launch_sized_workgroup_mem"
681                } else {
682                    ""
683                };
684                let global = self.declare_global_in_addrspace(
685                    name,
686                    self.type_array(self.type_i8(), 0),
687                    AddressSpace::GPU_WORKGROUP,
688                );
689                let ty::RawPtr(inner_ty, _) = result_layout.ty.kind() else { ::core::panicking::panic("internal error: entered unreachable code")unreachable!() };
690                // The alignment of the global is used to specify the *minimum* alignment that
691                // must be obeyed by the GPU runtime.
692                // When multiple of these global variables are used by a kernel, the maximum alignment is taken.
693                // See https://github.com/llvm/llvm-project/blob/a271d07488a85ce677674bbe8101b10efff58c95/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp#L821
694                let alignment = self.align_of(*inner_ty).bytes() as u32;
695                unsafe {
696                    // FIXME Workaround the above issue by taking maximum alignment if the global existed
697                    if tcx.sess.target.arch == Arch::Nvptx64 {
698                        if alignment > llvm::LLVMGetAlignment(global) {
699                            llvm::LLVMSetAlignment(global, alignment);
700                        }
701                    } else {
702                        llvm::LLVMSetAlignment(global, alignment);
703                    }
704                }
705                self.cx().const_pointercast(global, self.type_ptr())
706            }
707
708            sym::amdgpu_dispatch_ptr => {
709                let val = self.call_intrinsic("llvm.amdgcn.dispatch.ptr", &[], &[]);
710                // Relying on `LLVMBuildPointerCast` to produce an addrspacecast
711                self.pointercast(val, self.type_ptr())
712            }
713
714            sym::sve_tuple_create2 => {
715                {
    match self.layout_of(fn_args.type_at(0)).backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(1), .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(1), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
716                    self.layout_of(fn_args.type_at(0)).backend_repr,
717                    BackendRepr::SimdScalableVector {
718                        number_of_vectors: NumScalableVectors(1),
719                        ..
720                    }
721                );
722                let tuple_ty = self.layout_of(fn_args.type_at(1));
723                {
    match tuple_ty.backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(2), .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(2), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
724                    tuple_ty.backend_repr,
725                    BackendRepr::SimdScalableVector {
726                        number_of_vectors: NumScalableVectors(2),
727                        ..
728                    }
729                );
730                let ret = self.const_poison(self.backend_type(tuple_ty));
731                let ret = self.insert_value(ret, args[0].immediate(), 0);
732                self.insert_value(ret, args[1].immediate(), 1)
733            }
734
735            sym::sve_tuple_create3 => {
736                {
    match self.layout_of(fn_args.type_at(0)).backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(1), .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(1), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
737                    self.layout_of(fn_args.type_at(0)).backend_repr,
738                    BackendRepr::SimdScalableVector {
739                        number_of_vectors: NumScalableVectors(1),
740                        ..
741                    }
742                );
743                let tuple_ty = self.layout_of(fn_args.type_at(1));
744                {
    match tuple_ty.backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(3), .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(3), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
745                    tuple_ty.backend_repr,
746                    BackendRepr::SimdScalableVector {
747                        number_of_vectors: NumScalableVectors(3),
748                        ..
749                    }
750                );
751                let ret = self.const_poison(self.backend_type(tuple_ty));
752                let ret = self.insert_value(ret, args[0].immediate(), 0);
753                let ret = self.insert_value(ret, args[1].immediate(), 1);
754                self.insert_value(ret, args[2].immediate(), 2)
755            }
756
757            sym::sve_tuple_create4 => {
758                {
    match self.layout_of(fn_args.type_at(0)).backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(1), .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(1), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
759                    self.layout_of(fn_args.type_at(0)).backend_repr,
760                    BackendRepr::SimdScalableVector {
761                        number_of_vectors: NumScalableVectors(1),
762                        ..
763                    }
764                );
765                let tuple_ty = self.layout_of(fn_args.type_at(1));
766                {
    match tuple_ty.backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(4), .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(4), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
767                    tuple_ty.backend_repr,
768                    BackendRepr::SimdScalableVector {
769                        number_of_vectors: NumScalableVectors(4),
770                        ..
771                    }
772                );
773                let ret = self.const_poison(self.backend_type(tuple_ty));
774                let ret = self.insert_value(ret, args[0].immediate(), 0);
775                let ret = self.insert_value(ret, args[1].immediate(), 1);
776                let ret = self.insert_value(ret, args[2].immediate(), 2);
777                self.insert_value(ret, args[3].immediate(), 3)
778            }
779
780            sym::sve_tuple_get => {
781                {
    match self.layout_of(fn_args.type_at(0)).backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8),
            .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
782                    self.layout_of(fn_args.type_at(0)).backend_repr,
783                    BackendRepr::SimdScalableVector {
784                        number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8),
785                        ..
786                    }
787                );
788                {
    match self.layout_of(fn_args.type_at(1)).backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(1), .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(1), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
789                    self.layout_of(fn_args.type_at(1)).backend_repr,
790                    BackendRepr::SimdScalableVector {
791                        number_of_vectors: NumScalableVectors(1),
792                        ..
793                    }
794                );
795                self.extract_value(
796                    args[0].immediate(),
797                    fn_args.const_at(2).to_leaf().to_i32() as u64,
798                )
799            }
800
801            sym::sve_tuple_set => {
802                {
    match self.layout_of(fn_args.type_at(0)).backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8),
            .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
803                    self.layout_of(fn_args.type_at(0)).backend_repr,
804                    BackendRepr::SimdScalableVector {
805                        number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8),
806                        ..
807                    }
808                );
809                {
    match self.layout_of(fn_args.type_at(1)).backend_repr {
        BackendRepr::SimdScalableVector {
            number_of_vectors: NumScalableVectors(1), .. } => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::SimdScalableVector\n{ number_of_vectors: NumScalableVectors(1), .. }",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
810                    self.layout_of(fn_args.type_at(1)).backend_repr,
811                    BackendRepr::SimdScalableVector {
812                        number_of_vectors: NumScalableVectors(1),
813                        ..
814                    }
815                );
816                self.insert_value(
817                    args[0].immediate(),
818                    args[1].immediate(),
819                    fn_args.const_at(2).to_leaf().to_i32() as u64,
820                )
821            }
822
823            _ if name.as_str().starts_with("simd_") => {
824                // Unpack non-power-of-2 #[repr(packed, simd)] arguments.
825                // This gives them the expected layout of a regular #[repr(simd)] vector.
826                let mut loaded_args = Vec::new();
827                for arg in args {
828                    loaded_args.push(
829                        // #[repr(packed, simd)] vectors are passed like arrays (as references,
830                        // with reduced alignment and no padding) rather than as immediates.
831                        // We can use a vector load to fix the layout and turn the argument
832                        // into an immediate.
833                        if arg.layout.ty.is_simd()
834                            && let OperandValue::Ref(place) = arg.val
835                        {
836                            let (size, elem_ty) = arg.layout.ty.simd_size_and_type(self.tcx());
837                            let elem_ll_ty = match elem_ty.kind() {
838                                ty::Float(f) => self.type_float_from_ty(*f),
839                                ty::Int(i) => self.type_int_from_ty(*i),
840                                ty::Uint(u) => self.type_uint_from_ty(*u),
841                                ty::RawPtr(_, _) => self.type_ptr(),
842                                _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
843                            };
844                            let loaded =
845                                self.load_from_place(self.type_vector(elem_ll_ty, size), place);
846                            OperandRef::from_immediate_or_packed_pair(self, loaded, arg.layout)
847                        } else {
848                            *arg
849                        },
850                    );
851                }
852
853                let llret_ty = if result_layout.ty.is_simd()
854                    && let BackendRepr::Memory { .. } = result_layout.backend_repr
855                {
856                    let (size, elem_ty) = result_layout.ty.simd_size_and_type(self.tcx());
857                    let elem_ll_ty = match elem_ty.kind() {
858                        ty::Float(f) => self.type_float_from_ty(*f),
859                        ty::Int(i) => self.type_int_from_ty(*i),
860                        ty::Uint(u) => self.type_uint_from_ty(*u),
861                        ty::RawPtr(_, _) => self.type_ptr(),
862                        _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
863                    };
864                    self.type_vector(elem_ll_ty, size)
865                } else {
866                    result_layout.llvm_type(self)
867                };
868
869                match generic_simd_intrinsic(
870                    self,
871                    name,
872                    fn_args,
873                    &loaded_args,
874                    result_layout.ty,
875                    llret_ty,
876                    span,
877                ) {
878                    Ok(llval) => llval,
879                    // If there was an error, just skip this invocation... we'll abort compilation
880                    // anyway, but we can keep codegen'ing to find more errors.
881                    Err(err) => return IntrinsicResult::Err(err),
882                }
883            }
884
885            sym::return_address => {
886                match self.sess().target.arch {
887                    // Expand this list as needed
888                    Arch::Wasm32 | Arch::Wasm64 => {
889                        let ty = self.type_ptr();
890                        self.const_null(ty)
891                    }
892                    _ => {
893                        let ty = self.type_ix(32);
894                        let val = self.const_int(ty, 0);
895
896                        let type_params: &[&'ll Type] =
897                            if llvm_version < (23, 0, 0) { &[] } else { &[self.type_ptr()] };
898
899                        self.call_intrinsic("llvm.returnaddress", type_params, &[val])
900                    }
901                }
902            }
903
904            _ => {
905                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_codegen_llvm/src/intrinsic.rs:905",
                        "rustc_codegen_llvm::intrinsic", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_codegen_llvm/src/intrinsic.rs"),
                        ::tracing_core::__macro_support::Option::Some(905u32),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("unknown intrinsic \'{0}\' -- falling back to default body",
                                                    name) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("unknown intrinsic '{}' -- falling back to default body", name);
906                // Call the fallback body instead of generating the intrinsic code
907                let fallback = ty::Instance::new_raw(instance.def_id(), instance.args);
908                return IntrinsicResult::Fallback(fallback);
909            }
910        };
911
912        if let BackendRepr::Memory { .. } = result_layout.backend_repr {
913            // We have an llvm immediate, but that's not what cg_ssa expects,
914            // so write it into the place (that always exists for memory)
915            if !result_layout.is_zst() {
916                self.store_to_place(llval, result_place.unwrap());
917            }
918            IntrinsicResult::WroteIntoPlace
919        } else {
920            IntrinsicResult::Operand(
921                OperandRef::from_immediate_or_packed_pair(self, llval, result_layout).val,
922            )
923        }
924    }
925
926    fn codegen_llvm_intrinsic_call(
927        &mut self,
928        instance: ty::Instance<'tcx>,
929        args: &[OperandRef<'tcx, Self::Value>],
930        _is_cleanup: bool,
931    ) -> Self::Value {
932        let tcx = self.tcx();
933
934        let fn_ty = instance.ty(tcx, self.typing_env());
935        let fn_sig = match *fn_ty.kind() {
936            ty::FnDef(def_id, args) => tcx.instantiate_bound_regions_with_erased(
937                tcx.fn_sig(def_id).instantiate(tcx, args.no_bound_vars().unwrap()).skip_norm_wip(),
938            ),
939            _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
940        };
941        if !!fn_sig.c_variadic() {
    ::core::panicking::panic("assertion failed: !fn_sig.c_variadic()")
};assert!(!fn_sig.c_variadic());
942
943        let ret_layout = self.layout_of(fn_sig.output());
944        let llreturn_ty = if ret_layout.is_zst() {
945            self.type_void()
946        } else {
947            ret_layout.immediate_llvm_type(self)
948        };
949
950        let mut llargument_tys = Vec::with_capacity(fn_sig.inputs().len());
951        for &arg in fn_sig.inputs() {
952            let arg_layout = self.layout_of(arg);
953            if arg_layout.is_zst() {
954                continue;
955            }
956            llargument_tys.push(arg_layout.immediate_llvm_type(self));
957        }
958
959        let fn_ptr = if let Some(&llfn) = self.intrinsic_instances.borrow().get(&instance) {
960            llfn
961        } else {
962            let sym = tcx.symbol_name(instance).name;
963
964            let llfn = if let Some(llfn) = self.get_declared_value(sym) {
965                llfn
966            } else {
967                intrinsic_fn(self, sym, llreturn_ty, llargument_tys, instance)
968            };
969
970            self.intrinsic_instances.borrow_mut().insert(instance, llfn);
971
972            llfn
973        };
974        let fn_ty = self.get_type_of_global(fn_ptr);
975
976        let mut llargs = ::alloc::vec::Vec::new()vec![];
977
978        for arg in args {
979            match arg.val {
980                OperandValue::ZeroSized => {}
981                OperandValue::Immediate(a) => llargs.push(a),
982                OperandValue::Pair(a, b) => {
983                    llargs.push(a);
984                    llargs.push(b);
985                }
986                OperandValue::Ref(op_place_val) => {
987                    let mut llval = op_place_val.llval;
988                    // We can't use `PlaceRef::load` here because the argument
989                    // may have a type we don't treat as immediate, but the ABI
990                    // used for this call is passing it by-value. In that case,
991                    // the load would just produce `OperandValue::Ref` instead
992                    // of the `OperandValue::Immediate` we need for the call.
993                    llval = self.load(self.backend_type(arg.layout), llval, op_place_val.align);
994                    if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr {
995                        if scalar.is_bool() {
996                            self.range_metadata(llval, WrappingRange { start: 0, end: 1 });
997                        }
998                        // We store bools as `i8` so we need to truncate to `i1`.
999                        llval = self.to_immediate_scalar(llval, scalar);
1000                    }
1001                    llargs.push(llval);
1002                }
1003            }
1004        }
1005
1006        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_codegen_llvm/src/intrinsic.rs:1006",
                        "rustc_codegen_llvm::intrinsic", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_codegen_llvm/src/intrinsic.rs"),
                        ::tracing_core::__macro_support::Option::Some(1006u32),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("call intrinsic {0:?} with args ({1:?})",
                                                    instance, llargs) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("call intrinsic {:?} with args ({:?})", instance, llargs);
1007
1008        for (dest_ty, arg) in iter::zip(self.func_params_types(fn_ty), &mut llargs) {
1009            let src_ty = self.val_ty(arg);
1010            if !can_autocast(self, src_ty, dest_ty) {
    {
        ::core::panicking::panic_fmt(format_args!("Cannot match `{0:?}` (expected) with {1:?} (found) in `{2:?}",
                dest_ty, src_ty, fn_ptr));
    }
};assert!(
1011                can_autocast(self, src_ty, dest_ty),
1012                "Cannot match `{dest_ty:?}` (expected) with {src_ty:?} (found) in `{fn_ptr:?}"
1013            );
1014
1015            *arg = autocast(self, arg, src_ty, dest_ty);
1016        }
1017
1018        let llret = unsafe {
1019            llvm::LLVMBuildCallWithOperandBundles(
1020                self.llbuilder,
1021                fn_ty,
1022                fn_ptr,
1023                llargs.as_ptr(),
1024                llargs.len() as c_uint,
1025                ptr::dangling(),
1026                0,
1027                c"".as_ptr(),
1028            )
1029        };
1030
1031        let src_ty = self.val_ty(llret);
1032        let dest_ty = llreturn_ty;
1033        if !can_autocast(self, dest_ty, src_ty) {
    {
        ::core::panicking::panic_fmt(format_args!("Cannot match `{0:?}` (expected) with `{1:?}` (found) in `{2:?}`",
                src_ty, dest_ty, fn_ptr));
    }
};assert!(
1034            can_autocast(self, dest_ty, src_ty),
1035            "Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{fn_ptr:?}`"
1036        );
1037
1038        autocast(self, llret, src_ty, dest_ty)
1039    }
1040
1041    fn abort(&mut self) {
1042        self.call_intrinsic("llvm.trap", &[], &[]);
1043    }
1044
1045    fn assume(&mut self, val: Self::Value) {
1046        if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No {
1047            self.call_intrinsic("llvm.assume", &[], &[val]);
1048        }
1049    }
1050
1051    fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value {
1052        if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No {
1053            self.call_intrinsic(
1054                "llvm.expect",
1055                &[self.type_i1()],
1056                &[cond, self.const_bool(expected)],
1057            )
1058        } else {
1059            cond
1060        }
1061    }
1062
1063    fn type_checked_load(
1064        &mut self,
1065        llvtable: &'ll Value,
1066        vtable_byte_offset: u64,
1067        typeid: &[u8],
1068    ) -> Self::Value {
1069        let typeid = self.create_metadata(typeid);
1070        let typeid = self.get_metadata_value(typeid);
1071        let vtable_byte_offset = self.const_i32(vtable_byte_offset as i32);
1072        let type_checked_load = self.call_intrinsic(
1073            "llvm.type.checked.load",
1074            &[],
1075            &[llvtable, vtable_byte_offset, typeid],
1076        );
1077        self.extract_value(type_checked_load, 0)
1078    }
1079
1080    fn va_start(&mut self, va_list: &'ll Value) {
1081        self.call_intrinsic("llvm.va_start", &[self.val_ty(va_list)], &[va_list]);
1082    }
1083
1084    fn retag_reg(&mut self, ptr: Self::Value, info: &RetagInfo<Self::Value>) -> Self::Value {
1085        codegen_retag_inner(self, "__rust_retag_reg", ptr, info)
1086    }
1087
1088    fn retag_mem(&mut self, ptr: Self::Value, info: &RetagInfo<Self::Value>) {
1089        codegen_retag_inner(self, "__rust_retag_mem", ptr, info);
1090    }
1091}
1092
1093fn llvm_arch_for(rust_arch: &Arch) -> Option<&'static str> {
1094    Some(match rust_arch {
1095        Arch::AArch64 | Arch::Arm64EC => "aarch64",
1096        Arch::AmdGpu => "amdgcn",
1097        Arch::Arm => "arm",
1098        Arch::Bpf => "bpf",
1099        Arch::Hexagon => "hexagon",
1100        Arch::LoongArch32 | Arch::LoongArch64 => "loongarch",
1101        Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => "mips",
1102        Arch::Nvptx64 => "nvvm",
1103        Arch::PowerPC | Arch::PowerPC64 => "ppc",
1104        Arch::RiscV32 | Arch::RiscV64 => "riscv",
1105        Arch::S390x => "s390",
1106        Arch::SpirV => "spv",
1107        Arch::Wasm32 | Arch::Wasm64 => "wasm",
1108        Arch::X86 | Arch::X86_64 => "x86",
1109        _ => return None, // fallback for unknown archs
1110    })
1111}
1112
1113fn can_autocast<'ll>(cx: &CodegenCx<'ll, '_>, rust_ty: &'ll Type, llvm_ty: &'ll Type) -> bool {
1114    if rust_ty == llvm_ty {
1115        return true;
1116    }
1117
1118    match cx.type_kind(llvm_ty) {
1119        // Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust
1120        // due to auto field-alignment in non-packed structs (packed structs are represented in LLVM
1121        // as, well, packed structs, so they won't match with those either)
1122        TypeKind::Struct if cx.type_kind(rust_ty) == TypeKind::Struct => {
1123            let rust_element_tys = cx.struct_element_types(rust_ty);
1124            let llvm_element_tys = cx.struct_element_types(llvm_ty);
1125
1126            if rust_element_tys.len() != llvm_element_tys.len() {
1127                return false;
1128            }
1129
1130            iter::zip(rust_element_tys, llvm_element_tys).all(
1131                |(rust_element_ty, llvm_element_ty)| {
1132                    can_autocast(cx, rust_element_ty, llvm_element_ty)
1133                },
1134            )
1135        }
1136        TypeKind::Vector => {
1137            let llvm_element_ty = cx.element_type(llvm_ty);
1138            let element_count = cx.vector_length(llvm_ty) as u64;
1139
1140            if llvm_element_ty == cx.type_bf16() {
1141                rust_ty == cx.type_vector(cx.type_i16(), element_count)
1142            } else if llvm_element_ty == cx.type_i1() {
1143                let int_width = element_count.next_power_of_two().max(8);
1144                rust_ty == cx.type_ix(int_width)
1145            } else {
1146                false
1147            }
1148        }
1149        TypeKind::BFloat => rust_ty == cx.type_i16(),
1150        TypeKind::X86_AMX if cx.type_kind(rust_ty) == TypeKind::Vector => {
1151            let element_ty = cx.element_type(rust_ty);
1152            let element_count = cx.vector_length(rust_ty) as u64;
1153
1154            let element_size_bits = match cx.type_kind(element_ty) {
1155                TypeKind::Half => 16,
1156                TypeKind::Float => 32,
1157                TypeKind::Double => 64,
1158                TypeKind::FP128 => 128,
1159                TypeKind::Integer => cx.int_width(element_ty),
1160                TypeKind::Pointer => cx.int_width(cx.isize_ty),
1161                _ => ::rustc_middle::util::bug::bug_fmt(format_args!("Vector element type `{0:?}` not one of integer, float or pointer",
        element_ty))bug!(
1162                    "Vector element type `{element_ty:?}` not one of integer, float or pointer"
1163                ),
1164            };
1165
1166            element_size_bits * element_count == 8192
1167        }
1168        _ => false,
1169    }
1170}
1171
1172fn autocast<'ll>(
1173    bx: &mut Builder<'_, 'll, '_>,
1174    val: &'ll Value,
1175    src_ty: &'ll Type,
1176    dest_ty: &'ll Type,
1177) -> &'ll Value {
1178    if src_ty == dest_ty {
1179        return val;
1180    }
1181    match (bx.type_kind(src_ty), bx.type_kind(dest_ty)) {
1182        // re-pack structs
1183        (TypeKind::Struct, TypeKind::Struct) => {
1184            let mut ret = bx.const_poison(dest_ty);
1185            for (idx, (src_element_ty, dest_element_ty)) in
1186                iter::zip(bx.struct_element_types(src_ty), bx.struct_element_types(dest_ty))
1187                    .enumerate()
1188            {
1189                let elt = bx.extract_value(val, idx as u64);
1190                let casted_elt = autocast(bx, elt, src_element_ty, dest_element_ty);
1191                ret = bx.insert_value(ret, casted_elt, idx as u64);
1192            }
1193            ret
1194        }
1195        // cast from the i1xN vector type to the primitive type
1196        (TypeKind::Vector, TypeKind::Integer) if bx.element_type(src_ty) == bx.type_i1() => {
1197            let vector_length = bx.vector_length(src_ty) as u64;
1198            let int_width = vector_length.next_power_of_two().max(8);
1199
1200            let val = if vector_length == int_width {
1201                val
1202            } else {
1203                // zero-extends vector
1204                let shuffle_indices = match vector_length {
1205                    0 => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("zero length vectors are not allowed")));
}unreachable!("zero length vectors are not allowed"),
1206                    1 => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [0, 1, 1, 1, 1, 1, 1, 1]))vec![0, 1, 1, 1, 1, 1, 1, 1],
1207                    2 => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [0, 1, 2, 2, 2, 2, 2, 2]))vec![0, 1, 2, 2, 2, 2, 2, 2],
1208                    3 => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [0, 1, 2, 3, 3, 3, 3, 3]))vec![0, 1, 2, 3, 3, 3, 3, 3],
1209                    4.. => (0..int_width as i32).collect(),
1210                };
1211                let shuffle_mask =
1212                    shuffle_indices.into_iter().map(|i| bx.const_i32(i)).collect::<Vec<_>>();
1213                bx.shuffle_vector(val, bx.const_null(src_ty), bx.const_vector(&shuffle_mask))
1214            };
1215            bx.bitcast(val, dest_ty)
1216        }
1217        // cast from the primitive type to the i1xN vector type
1218        (TypeKind::Integer, TypeKind::Vector) if bx.element_type(dest_ty) == bx.type_i1() => {
1219            let vector_length = bx.vector_length(dest_ty) as u64;
1220            let int_width = vector_length.next_power_of_two().max(8);
1221
1222            let intermediate_ty = bx.type_vector(bx.type_i1(), int_width);
1223            let intermediate = bx.bitcast(val, intermediate_ty);
1224
1225            if vector_length == int_width {
1226                intermediate
1227            } else {
1228                let shuffle_mask: Vec<_> =
1229                    (0..vector_length).map(|i| bx.const_i32(i as i32)).collect();
1230                bx.shuffle_vector(
1231                    intermediate,
1232                    bx.const_poison(intermediate_ty),
1233                    bx.const_vector(&shuffle_mask),
1234                )
1235            }
1236        }
1237        (TypeKind::Vector, TypeKind::X86_AMX) => {
1238            bx.call_intrinsic("llvm.x86.cast.vector.to.tile", &[src_ty], &[val])
1239        }
1240        (TypeKind::X86_AMX, TypeKind::Vector) => {
1241            bx.call_intrinsic("llvm.x86.cast.tile.to.vector", &[dest_ty], &[val])
1242        }
1243        _ => bx.bitcast(val, dest_ty), // for `bf16(xN)` <-> `u16(xN)`
1244    }
1245}
1246
1247fn intrinsic_fn<'ll, 'tcx>(
1248    bx: &Builder<'_, 'll, 'tcx>,
1249    name: &str,
1250    rust_return_ty: &'ll Type,
1251    rust_argument_tys: Vec<&'ll Type>,
1252    instance: ty::Instance<'tcx>,
1253) -> &'ll Value {
1254    let tcx = bx.tcx;
1255
1256    let rust_fn_ty = bx.type_func(&rust_argument_tys, rust_return_ty);
1257
1258    let intrinsic = llvm::Intrinsic::lookup(name.as_bytes());
1259
1260    if let Some(intrinsic) = intrinsic
1261        && intrinsic.is_target_specific()
1262    {
1263        let (llvm_arch, _) = name[5..].split_once('.').unwrap();
1264        let rust_arch = &tcx.sess.target.arch;
1265
1266        if let Some(correct_llvm_arch) = llvm_arch_for(rust_arch)
1267            && llvm_arch != correct_llvm_arch
1268        {
1269            tcx.dcx().emit_fatal(IntrinsicWrongArch {
1270                name,
1271                target_arch: rust_arch.desc(),
1272                span: tcx.def_span(instance.def_id()),
1273            });
1274        }
1275    }
1276
1277    if let Some(intrinsic) = intrinsic
1278        && !intrinsic.is_overloaded()
1279    {
1280        // FIXME: also do this for overloaded intrinsics
1281        let llfn = intrinsic.get_declaration(bx.llmod, &[]);
1282        let llvm_fn_ty = bx.get_type_of_global(llfn);
1283
1284        let llvm_return_ty = bx.get_return_type(llvm_fn_ty);
1285        let llvm_argument_tys = bx.func_params_types(llvm_fn_ty);
1286        let llvm_is_variadic = bx.func_is_variadic(llvm_fn_ty);
1287
1288        let is_correct_signature = !llvm_is_variadic
1289            && rust_argument_tys.len() == llvm_argument_tys.len()
1290            && iter::once((rust_return_ty, llvm_return_ty))
1291                .chain(iter::zip(rust_argument_tys, llvm_argument_tys))
1292                .all(|(rust_ty, llvm_ty)| can_autocast(bx, rust_ty, llvm_ty));
1293
1294        if !is_correct_signature {
1295            tcx.dcx().emit_fatal(IntrinsicSignatureMismatch {
1296                name,
1297                llvm_fn_ty: &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:?}", llvm_fn_ty))
    })format!("{llvm_fn_ty:?}"),
1298                rust_fn_ty: &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:?}", rust_fn_ty))
    })format!("{rust_fn_ty:?}"),
1299                span: tcx.def_span(instance.def_id()),
1300            });
1301        }
1302
1303        return llfn;
1304    }
1305
1306    // Function addresses in Rust are never significant, allowing functions to be merged.
1307    let llfn = declare_raw_fn(
1308        bx,
1309        name,
1310        llvm::CCallConv,
1311        llvm::UnnamedAddr::Global,
1312        llvm::Visibility::Default,
1313        rust_fn_ty,
1314    );
1315
1316    if intrinsic.is_none() {
1317        let mut new_llfn = None;
1318        let can_upgrade = unsafe { llvm::LLVMRustUpgradeIntrinsicFunction(llfn, &mut new_llfn) };
1319
1320        if !can_upgrade {
1321            // This is either plain wrong, or this can be caused by incompatible LLVM versions
1322            tcx.dcx().emit_fatal(UnknownIntrinsic { name, span: tcx.def_span(instance.def_id()) });
1323        } else if let Some(def_id) = instance.def_id().as_local() {
1324            // we can emit diagnostics only for local crates
1325            let hir_id = tcx.local_def_id_to_hir_id(def_id);
1326
1327            // not all intrinsics are upgraded to some other intrinsics, most are upgraded to instruction sequences
1328            let msg = if let Some(new_llfn) = new_llfn {
1329                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("using deprecated intrinsic `{1}`, `{0}` can be used instead",
                str::from_utf8(&llvm::get_value_name(new_llfn)).unwrap(),
                name))
    })format!(
1330                    "using deprecated intrinsic `{name}`, `{}` can be used instead",
1331                    str::from_utf8(&llvm::get_value_name(new_llfn)).unwrap()
1332                )
1333            } else {
1334                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("using deprecated intrinsic `{0}`",
                name))
    })format!("using deprecated intrinsic `{name}`")
1335            };
1336
1337            tcx.emit_node_lint(
1338                DEPRECATED_LLVM_INTRINSIC,
1339                hir_id,
1340                rustc_errors::DiagDecorator(|d| {
1341                    d.primary_message(msg).span(tcx.hir_span(hir_id));
1342                }),
1343            );
1344        }
1345    }
1346
1347    llfn
1348}
1349
1350fn catch_unwind_intrinsic<'ll, 'tcx>(
1351    bx: &mut Builder<'_, 'll, 'tcx>,
1352    try_func: &'ll Value,
1353    data: &'ll Value,
1354    catch_func: &'ll Value,
1355) -> &'ll Value {
1356    if !bx.sess().panic_strategy().unwinds() {
1357        let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
1358        bx.call(try_func_ty, None, None, try_func, ReturnSlot::Direct, &[data], None, None);
1359        // Return 0 unconditionally from the intrinsic call;
1360        // we can never unwind.
1361        bx.const_bool(false)
1362    } else if wants_msvc_seh(bx.sess()) {
1363        codegen_msvc_try(bx, try_func, data, catch_func)
1364    } else if wants_wasm_eh(bx.sess()) {
1365        codegen_wasm_try(bx, try_func, data, catch_func)
1366    } else {
1367        codegen_gnu_try(bx, try_func, data, catch_func)
1368    }
1369}
1370
1371// MSVC's definition of the `rust_try` function.
1372//
1373// This implementation uses the new exception handling instructions in LLVM
1374// which have support in LLVM for SEH on MSVC targets. Although these
1375// instructions are meant to work for all targets, as of the time of this
1376// writing, however, LLVM does not recommend the usage of these new instructions
1377// as the old ones are still more optimized.
1378fn codegen_msvc_try<'ll, 'tcx>(
1379    bx: &mut Builder<'_, 'll, 'tcx>,
1380    try_func: &'ll Value,
1381    data: &'ll Value,
1382    catch_func: &'ll Value,
1383) -> &'ll Value {
1384    let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
1385        bx.set_personality_fn(bx.eh_personality());
1386
1387        let normal = bx.append_sibling_block("normal");
1388        let catchswitch = bx.append_sibling_block("catchswitch");
1389        let catchpad_rust = bx.append_sibling_block("catchpad_rust");
1390        let catchpad_foreign = bx.append_sibling_block("catchpad_foreign");
1391        let caught = bx.append_sibling_block("caught");
1392
1393        let try_func = llvm::get_param(bx.llfn(), 0);
1394        let data = llvm::get_param(bx.llfn(), 1);
1395        let catch_func = llvm::get_param(bx.llfn(), 2);
1396
1397        // We're generating an IR snippet that looks like:
1398        //
1399        //   declare bool @rust_try(%try_func, %data, %catch_func) {
1400        //      %slot = alloca i8*
1401        //      invoke %try_func(%data) to label %normal unwind label %catchswitch
1402        //
1403        //   normal:
1404        //      ret i1 false
1405        //
1406        //   catchswitch:
1407        //      %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller
1408        //
1409        //   catchpad_rust:
1410        //      %tok = catchpad within %cs [%type_descriptor, 8, %slot]
1411        //      %ptr = load %slot
1412        //      call %catch_func(%data, %ptr)
1413        //      catchret from %tok to label %caught
1414        //
1415        //   catchpad_foreign:
1416        //      %tok = catchpad within %cs [null, 64, null]
1417        //      call %catch_func(%data, null)
1418        //      catchret from %tok to label %caught
1419        //
1420        //   caught:
1421        //      ret i1 true
1422        //   }
1423        //
1424        // This structure follows the basic usage of throw/try/catch in LLVM.
1425        // For example, compile this C++ snippet to see what LLVM generates:
1426        //
1427        //      struct rust_panic {
1428        //          rust_panic(const rust_panic&);
1429        //          ~rust_panic();
1430        //
1431        //          void* x[2];
1432        //      };
1433        //
1434        //      int __rust_try(
1435        //          void (*try_func)(void*),
1436        //          void *data,
1437        //          void (*catch_func)(void*, void*) noexcept
1438        //      ) {
1439        //          try {
1440        //              try_func(data);
1441        //              return 0;
1442        //          } catch(rust_panic& a) {
1443        //              catch_func(data, &a);
1444        //              return 1;
1445        //          } catch(...) {
1446        //              catch_func(data, NULL);
1447        //              return 1;
1448        //          }
1449        //      }
1450        //
1451        // More information can be found in libstd's seh.rs implementation.
1452        let ptr_size = bx.tcx().data_layout.pointer_size();
1453        let ptr_align = bx.tcx().data_layout.pointer_align().abi;
1454        let slot = bx.alloca(ptr_size, ptr_align);
1455        let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
1456        bx.invoke(
1457            try_func_ty,
1458            None,
1459            None,
1460            try_func,
1461            ReturnSlot::Direct,
1462            &[data],
1463            normal,
1464            catchswitch,
1465            None,
1466            None,
1467        );
1468
1469        bx.switch_to_block(normal);
1470        bx.ret(bx.const_bool(false));
1471
1472        bx.switch_to_block(catchswitch);
1473        let cs = bx.catch_switch(None, None, &[catchpad_rust, catchpad_foreign]);
1474
1475        // We can't use the TypeDescriptor defined in libpanic_unwind because it
1476        // might be in another DLL and the SEH encoding only supports specifying
1477        // a TypeDescriptor from the current module.
1478        //
1479        // However this isn't an issue since the MSVC runtime uses string
1480        // comparison on the type name to match TypeDescriptors rather than
1481        // pointer equality.
1482        //
1483        // So instead we generate a new TypeDescriptor in each module that uses
1484        // `try` and let the linker merge duplicate definitions in the same
1485        // module.
1486        //
1487        // When modifying, make sure that the type_name string exactly matches
1488        // the one used in library/panic_unwind/src/seh.rs.
1489        let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_ptr());
1490        let type_name = bx.const_bytes(b"rust_panic\0");
1491        let type_info =
1492            bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_ptr()), type_name], false);
1493        let tydesc = bx.declare_global(
1494            &mangle_internal_symbol(bx.tcx, "__rust_panic_type_info"),
1495            bx.val_ty(type_info),
1496        );
1497
1498        llvm::set_linkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
1499        if bx.cx.tcx.sess.target.supports_comdat() {
1500            llvm::SetUniqueComdat(bx.llmod, tydesc);
1501        }
1502        llvm::set_initializer(tydesc, type_info);
1503
1504        // The flag value of 8 indicates that we are catching the exception by
1505        // reference instead of by value. We can't use catch by value because
1506        // that requires copying the exception object, which we don't support
1507        // since our exception object effectively contains a Box.
1508        //
1509        // Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
1510        bx.switch_to_block(catchpad_rust);
1511        let flags = bx.const_i32(8);
1512        let funclet = bx.catch_pad(cs, &[tydesc, flags, slot]);
1513        let ptr = bx.load(bx.type_ptr(), slot, ptr_align);
1514        let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
1515        bx.call(
1516            catch_ty,
1517            None,
1518            None,
1519            catch_func,
1520            ReturnSlot::Direct,
1521            &[data, ptr],
1522            Some(&funclet),
1523            None,
1524        );
1525        bx.catch_ret(&funclet, caught);
1526
1527        // The flag value of 64 indicates a "catch-all".
1528        bx.switch_to_block(catchpad_foreign);
1529        let flags = bx.const_i32(64);
1530        let null = bx.const_null(bx.type_ptr());
1531        let funclet = bx.catch_pad(cs, &[null, flags, null]);
1532        bx.call(
1533            catch_ty,
1534            None,
1535            None,
1536            catch_func,
1537            ReturnSlot::Direct,
1538            &[data, null],
1539            Some(&funclet),
1540            None,
1541        );
1542        bx.catch_ret(&funclet, caught);
1543
1544        bx.switch_to_block(caught);
1545        bx.ret(bx.const_bool(true));
1546    });
1547
1548    // Note that no invoke is used here because by definition this function
1549    // can't panic (that's what it's catching).
1550    let ret = bx.call(
1551        llty,
1552        None,
1553        None,
1554        llfn,
1555        ReturnSlot::Direct,
1556        &[try_func, data, catch_func],
1557        None,
1558        None,
1559    );
1560    ret
1561}
1562
1563// WASM's definition of the `rust_try` function.
1564fn codegen_wasm_try<'ll, 'tcx>(
1565    bx: &mut Builder<'_, 'll, 'tcx>,
1566    try_func: &'ll Value,
1567    data: &'ll Value,
1568    catch_func: &'ll Value,
1569) -> &'ll Value {
1570    let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
1571        bx.set_personality_fn(bx.eh_personality());
1572
1573        let normal = bx.append_sibling_block("normal");
1574        let catchswitch = bx.append_sibling_block("catchswitch");
1575        let catchpad = bx.append_sibling_block("catchpad");
1576        let caught = bx.append_sibling_block("caught");
1577
1578        let try_func = llvm::get_param(bx.llfn(), 0);
1579        let data = llvm::get_param(bx.llfn(), 1);
1580        let catch_func = llvm::get_param(bx.llfn(), 2);
1581
1582        // We're generating an IR snippet that looks like:
1583        //
1584        //   declare i1 @rust_try(%try_func, %data, %catch_func) {
1585        //      %slot = alloca i8*
1586        //      invoke %try_func(%data) to label %normal unwind label %catchswitch
1587        //
1588        //   normal:
1589        //      ret i1 false
1590        //
1591        //   catchswitch:
1592        //      %cs = catchswitch within none [%catchpad] unwind to caller
1593        //
1594        //   catchpad:
1595        //      %tok = catchpad within %cs [null]
1596        //      %ptr = call @llvm.wasm.get.exception(token %tok)
1597        //      %sel = call @llvm.wasm.get.ehselector(token %tok)
1598        //      call %catch_func(%data, %ptr)
1599        //      catchret from %tok to label %caught
1600        //
1601        //   caught:
1602        //      ret i1 true
1603        //   }
1604        //
1605        let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
1606        bx.invoke(
1607            try_func_ty,
1608            None,
1609            None,
1610            try_func,
1611            ReturnSlot::Direct,
1612            &[data],
1613            normal,
1614            catchswitch,
1615            None,
1616            None,
1617        );
1618
1619        bx.switch_to_block(normal);
1620        bx.ret(bx.const_bool(false));
1621
1622        bx.switch_to_block(catchswitch);
1623        let cs = bx.catch_switch(None, None, &[catchpad]);
1624
1625        bx.switch_to_block(catchpad);
1626        let null = bx.const_null(bx.type_ptr());
1627        let funclet = bx.catch_pad(cs, &[null]);
1628
1629        let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[], &[funclet.cleanuppad()]);
1630        let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[], &[funclet.cleanuppad()]);
1631
1632        let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
1633        bx.call(
1634            catch_ty,
1635            None,
1636            None,
1637            catch_func,
1638            ReturnSlot::Direct,
1639            &[data, ptr],
1640            Some(&funclet),
1641            None,
1642        );
1643        bx.catch_ret(&funclet, caught);
1644
1645        bx.switch_to_block(caught);
1646        bx.ret(bx.const_bool(true));
1647    });
1648
1649    // Note that no invoke is used here because by definition this function
1650    // can't panic (that's what it's catching).
1651    let ret = bx.call(
1652        llty,
1653        None,
1654        None,
1655        llfn,
1656        ReturnSlot::Direct,
1657        &[try_func, data, catch_func],
1658        None,
1659        None,
1660    );
1661    ret
1662}
1663
1664// Definition of the standard `try` function for Rust using the GNU-like model
1665// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke`
1666// instructions).
1667//
1668// This codegen is a little surprising because we always call a shim
1669// function instead of inlining the call to `invoke` manually here. This is done
1670// because in LLVM we're only allowed to have one personality per function
1671// definition. The call to the `try` intrinsic is being inlined into the
1672// function calling it, and that function may already have other personality
1673// functions in play. By calling a shim we're guaranteed that our shim will have
1674// the right personality function.
1675fn codegen_gnu_try<'ll, 'tcx>(
1676    bx: &mut Builder<'_, 'll, 'tcx>,
1677    try_func: &'ll Value,
1678    data: &'ll Value,
1679    catch_func: &'ll Value,
1680) -> &'ll Value {
1681    let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
1682        // Codegens the shims described above:
1683        //
1684        //   bx:
1685        //      invoke %try_func(%data) normal %normal unwind %catch
1686        //
1687        //   normal:
1688        //      ret 0
1689        //
1690        //   catch:
1691        //      (%ptr, _) = landingpad
1692        //      call %catch_func(%data, %ptr)
1693        //      ret 1
1694        let then = bx.append_sibling_block("then");
1695        let catch = bx.append_sibling_block("catch");
1696
1697        let try_func = llvm::get_param(bx.llfn(), 0);
1698        let data = llvm::get_param(bx.llfn(), 1);
1699        let catch_func = llvm::get_param(bx.llfn(), 2);
1700        let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
1701        bx.invoke(
1702            try_func_ty,
1703            None,
1704            None,
1705            try_func,
1706            ReturnSlot::Direct,
1707            &[data],
1708            then,
1709            catch,
1710            None,
1711            None,
1712        );
1713
1714        bx.switch_to_block(then);
1715        bx.ret(bx.const_bool(false));
1716
1717        // Type indicator for the exception being thrown.
1718        //
1719        // The first value in this tuple is a pointer to the exception object
1720        // being thrown. The second value is a "selector" indicating which of
1721        // the landing pad clauses the exception's type had been matched to.
1722        // rust_try ignores the selector.
1723        bx.switch_to_block(catch);
1724        let lpad_ty = bx.type_struct(&[bx.type_ptr(), bx.type_i32()], false);
1725        let vals = bx.landing_pad(lpad_ty, bx.eh_personality(), 1);
1726        let tydesc = bx.const_null(bx.type_ptr());
1727        bx.add_clause(vals, tydesc);
1728        let ptr = bx.extract_value(vals, 0);
1729        let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
1730        bx.call(catch_ty, None, None, catch_func, ReturnSlot::Direct, &[data, ptr], None, None);
1731        bx.ret(bx.const_bool(true));
1732    });
1733
1734    // Note that no invoke is used here because by definition this function
1735    // can't panic (that's what it's catching).
1736    let ret = bx.call(
1737        llty,
1738        None,
1739        None,
1740        llfn,
1741        ReturnSlot::Direct,
1742        &[try_func, data, catch_func],
1743        None,
1744        None,
1745    );
1746    ret
1747}
1748
1749// Helper function to give a Block to a closure to codegen a shim function.
1750// This is currently primarily used for the `try` intrinsic functions above.
1751fn gen_fn<'a, 'll, 'tcx>(
1752    cx: &'a CodegenCx<'ll, 'tcx>,
1753    name: &str,
1754    rust_fn_sig: ty::PolyFnSig<'tcx>,
1755    codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
1756) -> (&'ll Type, &'ll Value) {
1757    let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
1758    let llty = fn_abi.llvm_type(cx);
1759    let llfn = cx.declare_fn(name, fn_abi, None);
1760    cx.set_frame_pointer_type(llfn);
1761    cx.apply_target_cpu_attr(llfn);
1762    // FIXME(eddyb) find a nicer way to do this.
1763    llvm::set_linkage(llfn, llvm::Linkage::InternalLinkage);
1764    let llbb = Builder::append_block(cx, llfn, "entry-block");
1765    let bx = Builder::build(cx, llbb);
1766    codegen(bx);
1767    (llty, llfn)
1768}
1769
1770// Helper function used to get a handle to the `__rust_try` function used to
1771// catch exceptions.
1772//
1773// This function is only generated once and is then cached.
1774fn get_rust_try_fn<'a, 'll, 'tcx>(
1775    cx: &'a CodegenCx<'ll, 'tcx>,
1776    codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
1777) -> (&'ll Type, &'ll Value) {
1778    if let Some(llfn) = cx.rust_try_fn.get() {
1779        return llfn;
1780    }
1781
1782    // Define the type up front for the signature of the rust_try function.
1783    let tcx = cx.tcx;
1784    let i8p = Ty::new_mut_ptr(tcx, tcx.types.i8);
1785    // `unsafe fn(*mut Data) -> ()`
1786    let try_fn_ty = Ty::new_fn_ptr(
1787        tcx,
1788        ty::Binder::dummy(tcx.mk_fn_sig_rust_abi([i8p], tcx.types.unit, hir::Safety::Unsafe)),
1789    );
1790    // `unsafe fn(*mut Data, *mut i8) -> ()`
1791    let catch_fn_ty = Ty::new_fn_ptr(
1792        tcx,
1793        ty::Binder::dummy(tcx.mk_fn_sig_rust_abi([i8p, i8p], tcx.types.unit, hir::Safety::Unsafe)),
1794    );
1795    // `unsafe fn(unsafe fn(*mut Data) -> (), *mut Data, unsafe fn(*mut Data, *mut i8) -> ()) -> bool`
1796    let rust_fn_sig = ty::Binder::dummy(cx.tcx.mk_fn_sig_rust_abi(
1797        [try_fn_ty, i8p, catch_fn_ty],
1798        tcx.types.bool,
1799        hir::Safety::Unsafe,
1800    ));
1801    let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen);
1802
1803    if cx.sess().pointer_authentication() {
1804        let cfg = cx.sess().pointer_auth_config.as_ref().unwrap();
1805        let attrs: Vec<&Attribute> =
1806            cfg.fn_attrs().into_iter().map(|name| llvm::CreateAttrString(cx.llcx, name)).collect();
1807
1808        let (_ty, rust_try_fn) = rust_try;
1809        crate::attributes::apply_to_llfn(rust_try_fn, AttributePlace::Function, &attrs);
1810    }
1811
1812    cx.rust_try_fn.set(Some(rust_try));
1813    rust_try
1814}
1815
1816fn codegen_retag_inner<'ll, 'tcx>(
1817    bx: &mut Builder<'_, 'll, 'tcx>,
1818    name: &'static str,
1819    ptr: &'ll Value,
1820    info: &RetagInfo<&'ll Value>,
1821) -> &'ll Value {
1822    let size = bx.const_usize(info.size.bytes());
1823    let perms = bx.const_u8(info.flags.bits());
1824
1825    bx.call_intrinsic(
1826        name,
1827        // Retag intrinsics have special handling within `CodegenCx::declare_intrinsic`
1828        // to ensure that each form has the correct return type.
1829        &[bx.type_ptr(), bx.val_ty(size), bx.type_i8(), bx.type_ptr(), bx.type_ptr()],
1830        &[ptr, size, perms, info.im_layout, info.pin_layout],
1831    )
1832}
1833
1834fn codegen_autodiff<'ll, 'tcx>(
1835    bx: &mut Builder<'_, 'll, 'tcx>,
1836    instance: ty::Instance<'tcx>,
1837    args: &[OperandRef<'tcx, &'ll Value>],
1838    result_layout: ty::layout::TyAndLayout<'tcx>,
1839    result_place: Option<PlaceValue<&'ll Value>>,
1840) -> IntrinsicResult<'tcx, &'ll Value> {
1841    let tcx = bx.tcx;
1842    if !tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) {
1843        let _ = tcx.dcx().emit_err(AutoDiffWithoutEnable);
1844    }
1845
1846    let ct = tcx.crate_types();
1847    let lto = tcx.sess.lto();
1848    if ct.len() == 1 && ct.contains(&CrateType::Executable) {
1849        if lto != rustc_session::config::Lto::Fat {
1850            let _ = tcx.dcx().emit_err(AutoDiffWithoutLto);
1851        }
1852    } else {
1853        if lto != rustc_session::config::Lto::Fat && !tcx.sess.opts.cg.linker_plugin_lto.enabled() {
1854            let _ = tcx.dcx().emit_err(AutoDiffWithoutLto);
1855        }
1856    }
1857
1858    let fn_args = instance.args;
1859    let callee_ty = instance.ty(tcx, bx.typing_env());
1860
1861    let sig = callee_ty.fn_sig(tcx).skip_binder();
1862
1863    let ret_ty = sig.output();
1864    let llret_ty = bx.layout_of(ret_ty).llvm_type(bx);
1865
1866    let source_fn_ptr_ty = fn_args.into_type_list(tcx)[0];
1867    let fn_to_diff = args[0].immediate();
1868
1869    let (diff_id, diff_args) = match fn_args.into_type_list(tcx)[1].kind() {
1870        ty::FnDef(def_id, diff_args) => (def_id, diff_args.no_bound_vars().unwrap()),
1871        _ => ::rustc_middle::util::bug::bug_fmt(format_args!("invalid args"))bug!("invalid args"),
1872    };
1873
1874    let fn_diff = match Instance::try_resolve(tcx, bx.cx.typing_env(), *diff_id, diff_args) {
1875        Ok(Some(instance)) => instance,
1876        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!(
1877            "could not resolve ({:?}, {:?}) to a specific autodiff instance",
1878            diff_id,
1879            diff_args
1880        ),
1881        Err(err) => {
1882            // An error has already been emitted
1883            return IntrinsicResult::Err(err);
1884        }
1885    };
1886
1887    let val_arr = get_args_from_tuple(bx, args[2], fn_diff);
1888    let diff_symbol = symbol_name_for_instance_in_crate(tcx, fn_diff.clone(), LOCAL_CRATE);
1889
1890    let Some(Some(mut diff_attrs)) =
1891        {
    {
        'done:
            {
            for i in
                ::rustc_attr_ir::HasAttrs::get_attrs(fn_diff.def_id(), &tcx) {
                #[allow(unused_imports)]
                use ::rustc_attr_ir::AttributeKind::*;
                let i: &::rustc_attr_ir::Attribute = i;
                match i {
                    ::rustc_attr_ir::Attribute::Parsed(RustcAutodiff(attr)) => {
                        break 'done Some(attr.clone());
                    }
                    ::rustc_attr_ir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(tcx, fn_diff.def_id(), RustcAutodiff(attr) => attr.clone())
1892    else {
1893        ::rustc_middle::util::bug::bug_fmt(format_args!("could not find autodiff attrs"))bug!("could not find autodiff attrs")
1894    };
1895
1896    adjust_activity_to_abi(
1897        tcx,
1898        source_fn_ptr_ty,
1899        TypingEnv::fully_monomorphized(),
1900        &mut diff_attrs.input_activity,
1901    );
1902
1903    let fnc_tree = fnc_typetrees(tcx, source_fn_ptr_ty);
1904
1905    // Build body
1906    generate_enzyme_call(
1907        bx,
1908        fn_to_diff,
1909        &diff_symbol,
1910        llret_ty,
1911        &val_arr,
1912        &diff_attrs,
1913        result_layout,
1914        result_place,
1915        fnc_tree,
1916    )
1917}
1918
1919// Generates the LLVM code to offload a Rust function to a target device (e.g., GPU).
1920// For each kernel call, it generates the necessary globals (including metadata such as
1921// size and pass mode), manages memory mapping to and from the device, handles all
1922// data transfers, and launches the kernel on the target device.
1923fn codegen_offload<'ll, 'tcx>(
1924    bx: &mut Builder<'_, 'll, 'tcx>,
1925    tcx: TyCtxt<'tcx>,
1926    instance: ty::Instance<'tcx>,
1927    args: &[OperandRef<'tcx, &'ll Value>],
1928) {
1929    let cx = bx.cx;
1930    let fn_args = instance.args;
1931
1932    let (target_id, target_args) = match fn_args.into_type_list(tcx)[0].kind() {
1933        ty::FnDef(def_id, params) => (def_id, params.no_bound_vars().unwrap()),
1934        _ => ::rustc_middle::util::bug::bug_fmt(format_args!("invalid offload intrinsic arg"))bug!("invalid offload intrinsic arg"),
1935    };
1936
1937    let fn_target = match Instance::try_resolve(tcx, cx.typing_env(), *target_id, target_args) {
1938        Ok(Some(instance)) => instance,
1939        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!(
1940            "could not resolve ({:?}, {:?}) to a specific offload instance",
1941            target_id,
1942            target_args
1943        ),
1944        Err(_) => {
1945            // An error has already been emitted
1946            return;
1947        }
1948    };
1949
1950    let offload_dims = OffloadKernelDims::from_operands(bx, &args[1], &args[2]);
1951    let dyn_cache = match args[3].val {
1952        OperandValue::Immediate(val) => val,
1953        _ => { ::core::panicking::panic_fmt(format_args!("unparsable")); }panic!("unparsable"),
1954    };
1955    let device_id = match args[4].val {
1956        OperandValue::Immediate(val) => val,
1957        _ => { ::core::panicking::panic_fmt(format_args!("unparsable")); }panic!("unparsable"),
1958    };
1959    let args = get_args_from_tuple(bx, args[5], fn_target);
1960    let target_symbol = mangle_offload_export(tcx, fn_target);
1961
1962    let sig = tcx.fn_sig(fn_target.def_id()).instantiate(tcx, fn_target.args).skip_norm_wip();
1963    let sig = tcx.instantiate_bound_regions_with_erased(sig);
1964    let inputs = sig.inputs();
1965
1966    let fn_abi = cx.fn_abi_of_instance(fn_target, ty::List::empty());
1967
1968    let mut metadata = Vec::new();
1969    let mut types = Vec::new();
1970
1971    for (i, arg_abi) in fn_abi.args.iter().enumerate() {
1972        let ty = inputs[i];
1973        let decomposed = OffloadMetadata::handle_abi(cx, tcx, ty, arg_abi);
1974
1975        for (meta, entry_ty) in decomposed {
1976            metadata.push(meta);
1977            types.push(bx.cx.layout_of(entry_ty).llvm_type(bx.cx));
1978        }
1979    }
1980
1981    let offload_globals_ref = cx.offload_globals.borrow();
1982    let offload_globals = match offload_globals_ref.as_ref() {
1983        Some(globals) => globals,
1984        None => {
1985            // Offload is not initialized, cannot continue
1986            return;
1987        }
1988    };
1989    let offload_data =
1990        gpu_offload::gen_define_handling(&cx, &metadata, target_symbol, offload_globals);
1991    gpu_offload::gen_call_handling(
1992        bx,
1993        &offload_data,
1994        &args,
1995        &types,
1996        &metadata,
1997        offload_globals,
1998        &offload_dims,
1999        &dyn_cache,
2000        &device_id,
2001    );
2002}
2003
2004fn get_args_from_tuple<'ll, 'tcx>(
2005    bx: &mut Builder<'_, 'll, 'tcx>,
2006    tuple_op: OperandRef<'tcx, &'ll Value>,
2007    fn_instance: Instance<'tcx>,
2008) -> Vec<&'ll Value> {
2009    let cx = bx.cx;
2010    let fn_abi = cx.fn_abi_of_instance(fn_instance, ty::List::empty());
2011
2012    match tuple_op.val {
2013        OperandValue::Immediate(val) => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [val]))vec![val],
2014        OperandValue::Pair(v1, v2) => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [v1, v2]))vec![v1, v2],
2015        OperandValue::Ref(ptr) => {
2016            let tuple_place = PlaceRef { val: ptr, layout: tuple_op.layout };
2017
2018            let mut result = Vec::with_capacity(fn_abi.args.len());
2019            let mut tuple_index = 0;
2020
2021            for arg in &fn_abi.args {
2022                match arg.mode {
2023                    PassMode::Ignore => {}
2024                    PassMode::Direct(_) | PassMode::Cast { .. } => {
2025                        let field = tuple_place.project_field(bx, tuple_index);
2026                        let llvm_ty = field.layout.llvm_type(bx.cx);
2027                        let val = bx.load(llvm_ty, field.val.llval, field.val.align);
2028                        result.push(val);
2029                        tuple_index += 1;
2030                    }
2031                    PassMode::Pair(_, _) => {
2032                        let field = tuple_place.project_field(bx, tuple_index);
2033                        let llvm_ty = field.layout.llvm_type(bx.cx);
2034                        let pair_val = bx.load(llvm_ty, field.val.llval, field.val.align);
2035                        result.push(bx.extract_value(pair_val, 0));
2036                        result.push(bx.extract_value(pair_val, 1));
2037                        tuple_index += 1;
2038                    }
2039                    PassMode::Indirect { .. } => {
2040                        let field = tuple_place.project_field(bx, tuple_index);
2041                        result.push(field.val.llval);
2042                        tuple_index += 1;
2043                    }
2044                }
2045            }
2046
2047            result
2048        }
2049
2050        OperandValue::ZeroSized => ::alloc::vec::Vec::new()vec![],
2051    }
2052}
2053
2054fn generic_simd_intrinsic<'ll, 'tcx>(
2055    bx: &mut Builder<'_, 'll, 'tcx>,
2056    name: Symbol,
2057    fn_args: GenericArgsRef<'tcx>,
2058    args: &[OperandRef<'tcx, &'ll Value>],
2059    ret_ty: Ty<'tcx>,
2060    llret_ty: &'ll Type,
2061    span: Span,
2062) -> Result<&'ll Value, ErrorGuaranteed> {
2063    macro_rules! return_error {
2064        ($diag: expr) => {{
2065            let err = bx.sess().dcx().emit_err($diag);
2066            return Err(err);
2067        }};
2068    }
2069
2070    macro_rules! require {
2071        ($cond: expr, $diag: expr) => {
2072            if !$cond {
2073                return_error!($diag);
2074            }
2075        };
2076    }
2077
2078    macro_rules! require_simd {
2079        ($ty: expr, $variant:ident) => {{
2080            require!($ty.is_simd(), InvalidMonomorphization::$variant { span, name, ty: $ty });
2081            $ty.simd_size_and_type(bx.tcx())
2082        }};
2083    }
2084
2085    macro_rules! require_simd_or_scalable {
2086        ($ty: expr, $variant:ident) => {{
2087            require!(
2088                $ty.is_simd() || $ty.is_scalable_vector(),
2089                InvalidMonomorphization::$variant { span, name, ty: $ty }
2090            );
2091            if $ty.is_simd() {
2092                let (len, ty) = $ty.simd_size_and_type(bx.tcx());
2093                (len, ty, None)
2094            } else {
2095                let (count, ty, num_vecs) =
2096                    $ty.scalable_vector_parts(bx.tcx()).expect("`is_scalable_vector` was wrong");
2097                (count as u64, ty, Some(num_vecs))
2098            }
2099        }};
2100    }
2101
2102    /// Returns the bitwidth of the `$ty` argument if it is an `Int` or `Uint` type.
2103    macro_rules! require_int_or_uint_ty {
2104        ($ty: expr, $diag: expr) => {
2105            match $ty {
2106                ty::Int(i) => {
2107                    i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size().bits())
2108                }
2109                ty::Uint(i) => {
2110                    i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size().bits())
2111                }
2112                _ => {
2113                    return_error!($diag);
2114                }
2115            }
2116        };
2117    }
2118
2119    let llvm_version = crate::llvm_util::get_version();
2120
2121    /// Converts a vector mask, where each element has a bit width equal to the data elements it is used with,
2122    /// down to an i1 based mask that can be used by llvm intrinsics.
2123    ///
2124    /// The rust simd semantics are that each element should either consist of all ones or all zeroes,
2125    /// but this information is not available to llvm. Truncating the vector effectively uses the lowest bit,
2126    /// but codegen for several targets is better if we consider the highest bit by shifting.
2127    ///
2128    /// For x86 SSE/AVX targets this is beneficial since most instructions with mask parameters only consider the highest bit.
2129    /// So even though on llvm level we have an additional shift, in the final assembly there is no shift or truncate and
2130    /// instead the mask can be used as is.
2131    ///
2132    /// For aarch64 and other targets there is a benefit because a mask from the sign bit can be more
2133    /// efficiently converted to an all ones / all zeroes mask by comparing whether each element is negative.
2134    fn vector_mask_to_bitmask<'a, 'll, 'tcx>(
2135        bx: &mut Builder<'a, 'll, 'tcx>,
2136        i_xn: &'ll Value,
2137        in_elem_bitwidth: u64,
2138        in_len: u64,
2139    ) -> &'ll Value {
2140        // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position.
2141        let shift_idx = bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _);
2142        let shift_indices = ::alloc::vec::from_elem(shift_idx, in_len as _)vec![shift_idx; in_len as _];
2143        let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice()));
2144        // Truncate vector to an <i1 x N>
2145        bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len))
2146    }
2147
2148    // Sanity-check: all vector arguments must be immediates.
2149    if truecfg!(debug_assertions) {
2150        for arg in args {
2151            if arg.layout.ty.is_simd() {
2152                {
    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(_));
2153            }
2154        }
2155    }
2156
2157    if name == sym::simd_select_bitmask {
2158        let (len, _) = {
    if !args[1].layout.ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdArgument {
                        span,
                        name,
                        ty: args[1].layout.ty,
                    });
            return Err(err);
        };
    };
    args[1].layout.ty.simd_size_and_type(bx.tcx())
}require_simd!(args[1].layout.ty, SimdArgument);
2159
2160        let expected_int_bits = len.max(8).next_power_of_two();
2161        let expected_bytes = len.div_ceil(8);
2162
2163        let mask_ty = args[0].layout.ty;
2164        let mask = match mask_ty.kind() {
2165            ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
2166            ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
2167            ty::Array(elem, len)
2168                if #[allow(non_exhaustive_omitted_patterns)] match elem.kind() {
    ty::Uint(ty::UintTy::U8) => true,
    _ => false,
}matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
2169                    && len
2170                        .try_to_target_usize(bx.tcx)
2171                        .expect("expected monomorphic const in codegen")
2172                        == expected_bytes =>
2173            {
2174                let place = PlaceRef::alloca(bx, args[0].layout);
2175                args[0].val.store(bx, place);
2176                let int_ty = bx.type_ix(expected_bytes * 8);
2177                bx.load(int_ty, place.val.llval, Align::ONE)
2178            }
2179            _ => {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::InvalidBitmask {
                span,
                name,
                mask_ty,
                expected_int_bits,
                expected_bytes,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::InvalidBitmask {
2180                span,
2181                name,
2182                mask_ty,
2183                expected_int_bits,
2184                expected_bytes
2185            }),
2186        };
2187
2188        let i1 = bx.type_i1();
2189        let im = bx.type_ix(len);
2190        let i1xn = bx.type_vector(i1, len);
2191        let m_im = bx.trunc(mask, im);
2192        let m_i1s = bx.bitcast(m_im, i1xn);
2193        return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
2194    }
2195
2196    if name == sym::simd_splat {
2197        let (out_len, out_ty) = {
    if !ret_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    ret_ty.simd_size_and_type(bx.tcx())
}require_simd!(ret_ty, SimdReturn);
2198
2199        if !(args[0].layout.ty == out_ty) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ExpectedVectorElementType {
                    span,
                    name,
                    expected_element: out_ty,
                    vector_type: ret_ty,
                });
        return Err(err);
    };
};require!(
2200            args[0].layout.ty == out_ty,
2201            InvalidMonomorphization::ExpectedVectorElementType {
2202                span,
2203                name,
2204                expected_element: out_ty,
2205                vector_type: ret_ty,
2206            }
2207        );
2208
2209        // `insertelement <N x elem> poison, elem %x, i32 0`
2210        let poison_vec = bx.const_poison(llret_ty);
2211        let idx0 = bx.const_i32(0);
2212        let v0 = bx.insert_element(poison_vec, args[0].immediate(), idx0);
2213
2214        // `shufflevector <N x elem> v0, <N x elem> poison, <N x i32> zeroinitializer`
2215        // The masks is all zeros, so this splats lane 0 (which has our element in it).
2216        let mask_ty = bx.type_vector(bx.type_i32(), out_len);
2217        let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(mask_ty));
2218
2219        return Ok(splat);
2220    }
2221
2222    let supports_scalable = match name {
2223        sym::simd_cast | sym::simd_select => true,
2224        _ => false,
2225    };
2226
2227    // Every intrinsic below takes a SIMD vector as its first argument. Some intrinsics also accept
2228    // scalable vectors. `require_simd_or_scalable` is used regardless as it'll do the right thing
2229    // for non-scalable vectors, and an additional check to prohibit scalable vectors for those
2230    // intrinsics that do not support them is added.
2231    if !supports_scalable {
2232        let _ = {
    if !args[0].layout.ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdInput {
                        span,
                        name,
                        ty: args[0].layout.ty,
                    });
            return Err(err);
        };
    };
    args[0].layout.ty.simd_size_and_type(bx.tcx())
}require_simd!(args[0].layout.ty, SimdInput);
2233    }
2234    let (in_len, in_elem, in_num_vecs) = {
    if !(args[0].layout.ty.is_simd() ||
                args[0].layout.ty.is_scalable_vector()) {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdInput {
                        span,
                        name,
                        ty: args[0].layout.ty,
                    });
            return Err(err);
        };
    };
    if args[0].layout.ty.is_simd() {
        let (len, ty) = args[0].layout.ty.simd_size_and_type(bx.tcx());
        (len, ty, None)
    } else {
        let (count, ty, num_vecs) =
            args[0].layout.ty.scalable_vector_parts(bx.tcx()).expect("`is_scalable_vector` was wrong");
        (count as u64, ty, Some(num_vecs))
    }
}require_simd_or_scalable!(args[0].layout.ty, SimdInput);
2235    let in_ty = args[0].layout.ty;
2236
2237    let comparison = match name {
2238        sym::simd_eq => Some(BinOp::Eq),
2239        sym::simd_ne => Some(BinOp::Ne),
2240        sym::simd_lt => Some(BinOp::Lt),
2241        sym::simd_le => Some(BinOp::Le),
2242        sym::simd_gt => Some(BinOp::Gt),
2243        sym::simd_ge => Some(BinOp::Ge),
2244        _ => None,
2245    };
2246
2247    if let Some(cmp_op) = comparison {
2248        let (out_len, out_ty) = {
    if !ret_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    ret_ty.simd_size_and_type(bx.tcx())
}require_simd!(ret_ty, SimdReturn);
2249
2250        if !(in_len == out_len) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnLengthInputType {
                    span,
                    name,
                    in_len,
                    in_ty,
                    ret_ty,
                    out_len,
                });
        return Err(err);
    };
};require!(
2251            in_len == out_len,
2252            InvalidMonomorphization::ReturnLengthInputType {
2253                span,
2254                name,
2255                in_len,
2256                in_ty,
2257                ret_ty,
2258                out_len
2259            }
2260        );
2261        if !(bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnIntegerType {
                    span,
                    name,
                    ret_ty,
                    out_ty,
                });
        return Err(err);
    };
};require!(
2262            bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
2263            InvalidMonomorphization::ReturnIntegerType { span, name, ret_ty, out_ty }
2264        );
2265
2266        return Ok(compare_simd_types(
2267            bx,
2268            args[0].immediate(),
2269            args[1].immediate(),
2270            in_elem,
2271            llret_ty,
2272            cmp_op,
2273        ));
2274    }
2275
2276    if name == sym::simd_shuffle_const_generic {
2277        let idx = fn_args[2].expect_const().to_branch();
2278        let n = idx.len() as u64;
2279
2280        let (out_len, out_ty) = {
    if !ret_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    ret_ty.simd_size_and_type(bx.tcx())
}require_simd!(ret_ty, SimdReturn);
2281        if !(out_len == n) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnLength {
                    span,
                    name,
                    in_len: n,
                    ret_ty,
                    out_len,
                });
        return Err(err);
    };
};require!(
2282            out_len == n,
2283            InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len }
2284        );
2285        if !(in_elem == out_ty) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnElement {
                    span,
                    name,
                    in_elem,
                    in_ty,
                    ret_ty,
                    out_ty,
                });
        return Err(err);
    };
};require!(
2286            in_elem == out_ty,
2287            InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
2288        );
2289
2290        let total_len = in_len * 2;
2291
2292        let indices: Option<Vec<_>> = idx
2293            .iter()
2294            .enumerate()
2295            .map(|(arg_idx, val)| {
2296                let idx = val.to_leaf().to_i32();
2297                if idx >= i32::try_from(total_len).unwrap() {
2298                    bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
2299                        span,
2300                        name,
2301                        arg_idx: arg_idx as u64,
2302                        total_len: total_len.into(),
2303                    });
2304                    None
2305                } else {
2306                    Some(bx.const_i32(idx))
2307                }
2308            })
2309            .collect();
2310        let Some(indices) = indices else {
2311            return Ok(bx.const_null(llret_ty));
2312        };
2313
2314        return Ok(bx.shuffle_vector(
2315            args[0].immediate(),
2316            args[1].immediate(),
2317            bx.const_vector(&indices),
2318        ));
2319    }
2320
2321    if name == sym::simd_shuffle {
2322        // Make sure this is actually a SIMD vector.
2323        let idx_ty = args[2].layout.ty;
2324        let n: u64 = if idx_ty.is_simd()
2325            && #[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))
2326        {
2327            idx_ty.simd_size_and_type(bx.cx.tcx).0
2328        } else {
2329            {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::SimdShuffle {
                span,
                name,
                ty: idx_ty,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::SimdShuffle { span, name, ty: idx_ty })
2330        };
2331
2332        let (out_len, out_ty) = {
    if !ret_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    ret_ty.simd_size_and_type(bx.tcx())
}require_simd!(ret_ty, SimdReturn);
2333        if !(out_len == n) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnLength {
                    span,
                    name,
                    in_len: n,
                    ret_ty,
                    out_len,
                });
        return Err(err);
    };
};require!(
2334            out_len == n,
2335            InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len }
2336        );
2337        if !(in_elem == out_ty) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnElement {
                    span,
                    name,
                    in_elem,
                    in_ty,
                    ret_ty,
                    out_ty,
                });
        return Err(err);
    };
};require!(
2338            in_elem == out_ty,
2339            InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
2340        );
2341
2342        let total_len = u128::from(in_len) * 2;
2343
2344        // Check that the indices are in-bounds.
2345        let indices = args[2].immediate();
2346        for i in 0..n {
2347            let val = bx.const_get_elt(indices, i as u64);
2348            let idx = bx
2349                .const_to_opt_u128(val, true)
2350                .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"));
2351            if idx >= total_len {
2352                {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
                span,
                name,
                arg_idx: i,
                total_len,
            });
    return Err(err);
};return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
2353                    span,
2354                    name,
2355                    arg_idx: i,
2356                    total_len,
2357                });
2358            }
2359        }
2360
2361        return Ok(bx.shuffle_vector(args[0].immediate(), args[1].immediate(), indices));
2362    }
2363
2364    if name == sym::simd_insert || name == sym::simd_insert_dyn {
2365        if !(in_elem == args[2].layout.ty) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::InsertedType {
                    span,
                    name,
                    in_elem,
                    in_ty,
                    out_ty: args[2].layout.ty,
                });
        return Err(err);
    };
};require!(
2366            in_elem == args[2].layout.ty,
2367            InvalidMonomorphization::InsertedType {
2368                span,
2369                name,
2370                in_elem,
2371                in_ty,
2372                out_ty: args[2].layout.ty
2373            }
2374        );
2375
2376        let index_imm = if name == sym::simd_insert {
2377            let idx = bx
2378                .const_to_opt_u128(args[1].immediate(), false)
2379                .expect("typeck should have ensure that this is a const");
2380            if idx >= in_len.into() {
2381                {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
                span,
                name,
                arg_idx: 1,
                total_len: in_len.into(),
            });
    return Err(err);
};return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
2382                    span,
2383                    name,
2384                    arg_idx: 1,
2385                    total_len: in_len.into(),
2386                });
2387            }
2388            bx.const_i32(idx as i32)
2389        } else {
2390            args[1].immediate()
2391        };
2392
2393        return Ok(bx.insert_element(args[0].immediate(), args[2].immediate(), index_imm));
2394    }
2395    if name == sym::simd_extract || name == sym::simd_extract_dyn {
2396        if !(ret_ty == in_elem) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                    span,
                    name,
                    in_elem,
                    in_ty,
                    ret_ty,
                });
        return Err(err);
    };
};require!(
2397            ret_ty == in_elem,
2398            InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
2399        );
2400        let index_imm = if name == sym::simd_extract {
2401            let idx = bx
2402                .const_to_opt_u128(args[1].immediate(), false)
2403                .expect("typeck should have ensure that this is a const");
2404            if idx >= in_len.into() {
2405                {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
                span,
                name,
                arg_idx: 1,
                total_len: in_len.into(),
            });
    return Err(err);
};return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
2406                    span,
2407                    name,
2408                    arg_idx: 1,
2409                    total_len: in_len.into(),
2410                });
2411            }
2412            bx.const_i32(idx as i32)
2413        } else {
2414            args[1].immediate()
2415        };
2416
2417        return Ok(bx.extract_element(args[0].immediate(), index_imm));
2418    }
2419
2420    if name == sym::simd_select {
2421        let m_elem_ty = in_elem;
2422        let m_len = in_len;
2423        let (v_len, _, _) = {
    if !(args[1].layout.ty.is_simd() ||
                args[1].layout.ty.is_scalable_vector()) {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdArgument {
                        span,
                        name,
                        ty: args[1].layout.ty,
                    });
            return Err(err);
        };
    };
    if args[1].layout.ty.is_simd() {
        let (len, ty) = args[1].layout.ty.simd_size_and_type(bx.tcx());
        (len, ty, None)
    } else {
        let (count, ty, num_vecs) =
            args[1].layout.ty.scalable_vector_parts(bx.tcx()).expect("`is_scalable_vector` was wrong");
        (count as u64, ty, Some(num_vecs))
    }
}require_simd_or_scalable!(args[1].layout.ty, SimdArgument);
2424        if !(m_len == v_len) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::MismatchedLengths {
                    span,
                    name,
                    m_len,
                    v_len,
                });
        return Err(err);
    };
};require!(
2425            m_len == v_len,
2426            InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
2427        );
2428
2429        let m_i1s = if args[1].layout.ty.is_scalable_vector() {
2430            match m_elem_ty.kind() {
2431                ty::Bool => {}
2432                _ => {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::MaskWrongElementType {
                span,
                name,
                ty: m_elem_ty,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::MaskWrongElementType {
2433                    span,
2434                    name,
2435                    ty: m_elem_ty
2436                }),
2437            };
2438            let i1 = bx.type_i1();
2439            let i1xn = bx.type_scalable_vector(i1, m_len as u64);
2440            bx.trunc(args[0].immediate(), i1xn)
2441        } else {
2442            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())
    }
    _ => {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::MaskWrongElementType {
                        span,
                        name,
                        ty: m_elem_ty,
                    });
            return Err(err);
        };
    }
}require_int_or_uint_ty!(
2443                m_elem_ty.kind(),
2444                InvalidMonomorphization::MaskWrongElementType { span, name, ty: m_elem_ty }
2445            );
2446            vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, m_len)
2447        };
2448
2449        return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
2450    }
2451
2452    if name == sym::simd_bitmask {
2453        // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a vector mask and
2454        // returns one bit for each lane (which must all be `0` or `!0`) in the form of either:
2455        // * an unsigned integer
2456        // * an array of `u8`
2457        // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits.
2458        //
2459        // The bit order of the result depends on the byte endianness, LSB-first for little
2460        // endian and MSB-first for big endian.
2461        let expected_int_bits = in_len.max(8).next_power_of_two();
2462        let expected_bytes = in_len.div_ceil(8);
2463
2464        // Integer vector <i{in_bitwidth} x in_len>:
2465        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())
    }
    _ => {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::MaskWrongElementType {
                        span,
                        name,
                        ty: in_elem,
                    });
            return Err(err);
        };
    }
}require_int_or_uint_ty!(
2466            in_elem.kind(),
2467            InvalidMonomorphization::MaskWrongElementType { span, name, ty: in_elem }
2468        );
2469
2470        let i1xn = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, in_len);
2471        // Bitcast <i1 x N> to iN:
2472        let i_ = bx.bitcast(i1xn, bx.type_ix(in_len));
2473
2474        match ret_ty.kind() {
2475            ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {
2476                // Zero-extend iN to the bitmask type:
2477                return Ok(bx.zext(i_, bx.type_ix(expected_int_bits)));
2478            }
2479            ty::Array(elem, len)
2480                if #[allow(non_exhaustive_omitted_patterns)] match elem.kind() {
    ty::Uint(ty::UintTy::U8) => true,
    _ => false,
}matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
2481                    && len
2482                        .try_to_target_usize(bx.tcx)
2483                        .expect("expected monomorphic const in codegen")
2484                        == expected_bytes =>
2485            {
2486                // Zero-extend iN to the array length:
2487                let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8));
2488
2489                // Convert the integer to a byte array
2490                let ptr = bx.alloca(Size::from_bytes(expected_bytes), Align::ONE);
2491                bx.store(ze, ptr, Align::ONE);
2492                let array_ty = bx.type_array(bx.type_i8(), expected_bytes);
2493                return Ok(bx.load(array_ty, ptr, Align::ONE));
2494            }
2495            _ => {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::CannotReturn {
                span,
                name,
                ret_ty,
                expected_int_bits,
                expected_bytes,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::CannotReturn {
2496                span,
2497                name,
2498                ret_ty,
2499                expected_int_bits,
2500                expected_bytes
2501            }),
2502        }
2503    }
2504
2505    fn simd_simple_float_intrinsic<'ll, 'tcx>(
2506        name: Symbol,
2507        in_elem: Ty<'_>,
2508        in_ty: Ty<'_>,
2509        in_len: u64,
2510        bx: &mut Builder<'_, 'll, 'tcx>,
2511        span: Span,
2512        args: &[OperandRef<'tcx, &'ll Value>],
2513    ) -> Result<&'ll Value, ErrorGuaranteed> {
2514        macro_rules! return_error {
2515            ($diag: expr) => {{
2516                let err = bx.sess().dcx().emit_err($diag);
2517                return Err(err);
2518            }};
2519        }
2520
2521        let ty::Float(f) = in_elem.kind() else {
2522            {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
                span,
                name,
                ty: in_ty,
            });
    return Err(err);
};return_error!(InvalidMonomorphization::BasicFloatType { span, name, ty: in_ty });
2523        };
2524        let elem_ty = bx.cx.type_float_from_ty(*f);
2525
2526        let vec_ty = bx.type_vector(elem_ty, in_len);
2527
2528        let intr_name = match name {
2529            sym::simd_ceil => "llvm.ceil",
2530            sym::simd_fabs => "llvm.fabs",
2531            sym::simd_fcos => "llvm.cos",
2532            sym::simd_fexp2 => "llvm.exp2",
2533            sym::simd_fexp => "llvm.exp",
2534            sym::simd_flog10 => "llvm.log10",
2535            sym::simd_flog2 => "llvm.log2",
2536            sym::simd_flog => "llvm.log",
2537            sym::simd_floor => "llvm.floor",
2538            sym::simd_fma => "llvm.fma",
2539            sym::simd_relaxed_fma => "llvm.fmuladd",
2540            sym::simd_fsin => "llvm.sin",
2541            sym::simd_fsqrt => "llvm.sqrt",
2542            sym::simd_round => "llvm.round",
2543            sym::simd_round_ties_even => "llvm.rint",
2544            sym::simd_trunc => "llvm.trunc",
2545            _ => {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::UnrecognizedIntrinsic {
                span,
                name,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }),
2546        };
2547        Ok(bx.call_intrinsic(
2548            intr_name,
2549            &[vec_ty],
2550            &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
2551        ))
2552    }
2553
2554    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!(
2555        name,
2556        sym::simd_ceil
2557            | sym::simd_fabs
2558            | sym::simd_fcos
2559            | sym::simd_fexp2
2560            | sym::simd_fexp
2561            | sym::simd_flog10
2562            | sym::simd_flog2
2563            | sym::simd_flog
2564            | sym::simd_floor
2565            | sym::simd_fma
2566            | sym::simd_fsin
2567            | sym::simd_fsqrt
2568            | sym::simd_relaxed_fma
2569            | sym::simd_round
2570            | sym::simd_round_ties_even
2571            | sym::simd_trunc
2572    ) {
2573        return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args);
2574    }
2575
2576    fn llvm_vector_ty<'ll>(cx: &CodegenCx<'ll, '_>, elem_ty: Ty<'_>, vec_len: u64) -> &'ll Type {
2577        let elem_ty = match *elem_ty.kind() {
2578            ty::Int(v) => cx.type_int_from_ty(v),
2579            ty::Uint(v) => cx.type_uint_from_ty(v),
2580            ty::Float(v) => cx.type_float_from_ty(v),
2581            ty::RawPtr(_, _) => cx.type_ptr(),
2582            _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
2583        };
2584        cx.type_vector(elem_ty, vec_len)
2585    }
2586
2587    if name == sym::simd_gather {
2588        // simd_gather(values: <N x T>, pointers: <N x *_ T>,
2589        //             mask: <N x i{M}>) -> <N x T>
2590        // * N: number of elements in the input vectors
2591        // * T: type of the element to load
2592        // * M: any integer width is supported, will be truncated to i1
2593
2594        // All types must be simd vector types
2595
2596        // The second argument must be a simd vector with an element type that's a pointer
2597        // to the element type of the first argument
2598        let (_, element_ty0) = {
    if !in_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdFirst {
                        span,
                        name,
                        ty: in_ty,
                    });
            return Err(err);
        };
    };
    in_ty.simd_size_and_type(bx.tcx())
}require_simd!(in_ty, SimdFirst);
2599        let (out_len, element_ty1) = {
    if !args[1].layout.ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdSecond {
                        span,
                        name,
                        ty: args[1].layout.ty,
                    });
            return Err(err);
        };
    };
    args[1].layout.ty.simd_size_and_type(bx.tcx())
}require_simd!(args[1].layout.ty, SimdSecond);
2600        // The element type of the third argument must be a signed integer type of any width:
2601        let (out_len2, element_ty2) = {
    if !args[2].layout.ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdThird {
                        span,
                        name,
                        ty: args[2].layout.ty,
                    });
            return Err(err);
        };
    };
    args[2].layout.ty.simd_size_and_type(bx.tcx())
}require_simd!(args[2].layout.ty, SimdThird);
2602        {
    if !ret_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    ret_ty.simd_size_and_type(bx.tcx())
};require_simd!(ret_ty, SimdReturn);
2603
2604        // Of the same length:
2605        if !(in_len == out_len) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::SecondArgumentLength {
                    span,
                    name,
                    in_len,
                    in_ty,
                    arg_ty: args[1].layout.ty,
                    out_len,
                });
        return Err(err);
    };
};require!(
2606            in_len == out_len,
2607            InvalidMonomorphization::SecondArgumentLength {
2608                span,
2609                name,
2610                in_len,
2611                in_ty,
2612                arg_ty: args[1].layout.ty,
2613                out_len
2614            }
2615        );
2616        if !(in_len == out_len2) {
    {
        let err =
            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(err);
    };
};require!(
2617            in_len == out_len2,
2618            InvalidMonomorphization::ThirdArgumentLength {
2619                span,
2620                name,
2621                in_len,
2622                in_ty,
2623                arg_ty: args[2].layout.ty,
2624                out_len: out_len2
2625            }
2626        );
2627
2628        // The return type must match the first argument type
2629        if !(ret_ty == in_ty) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ExpectedReturnType {
                    span,
                    name,
                    in_ty,
                    ret_ty,
                });
        return Err(err);
    };
};require!(
2630            ret_ty == in_ty,
2631            InvalidMonomorphization::ExpectedReturnType { span, name, in_ty, ret_ty }
2632        );
2633
2634        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,
        } {
    {
        let err =
            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(err);
    };
};require!(
2635            matches!(
2636                *element_ty1.kind(),
2637                ty::RawPtr(p_ty, _) if p_ty == in_elem && p_ty.kind() == element_ty0.kind()
2638            ),
2639            InvalidMonomorphization::ExpectedElementType {
2640                span,
2641                name,
2642                expected_element: element_ty1,
2643                second_arg: args[1].layout.ty,
2644                in_elem,
2645                in_ty,
2646                mutability: ExpectedPointerMutability::Not,
2647            }
2648        );
2649
2650        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())
    }
    _ => {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::MaskWrongElementType {
                        span,
                        name,
                        ty: element_ty2,
                    });
            return Err(err);
        };
    }
}require_int_or_uint_ty!(
2651            element_ty2.kind(),
2652            InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 }
2653        );
2654
2655        // Alignment of T, must be a constant integer value:
2656        let alignment = bx.align_of(in_elem).bytes();
2657
2658        // Truncate the mask vector to a vector of i1s:
2659        let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
2660
2661        // Type of the vector of pointers:
2662        let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len);
2663
2664        // Type of the vector of elements:
2665        let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
2666
2667        let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
2668            let alignment = bx.const_i32(alignment as i32);
2669            &[args[1].immediate(), alignment, mask, args[0].immediate()]
2670        } else {
2671            &[args[1].immediate(), mask, args[0].immediate()]
2672        };
2673
2674        let call =
2675            bx.call_intrinsic("llvm.masked.gather", &[llvm_elem_vec_ty, llvm_pointer_vec_ty], args);
2676        if llvm_version >= (22, 0, 0) {
2677            crate::attributes::apply_to_callsite(
2678                call,
2679                crate::llvm::AttributePlace::Argument(0),
2680                &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
2681            )
2682        }
2683        return Ok(call);
2684    }
2685
2686    fn llvm_alignment<'ll, 'tcx>(
2687        bx: &mut Builder<'_, 'll, 'tcx>,
2688        alignment: SimdAlign,
2689        vector_ty: Ty<'tcx>,
2690        element_ty: Ty<'tcx>,
2691    ) -> u64 {
2692        match alignment {
2693            SimdAlign::Unaligned => 1,
2694            SimdAlign::Element => bx.align_of(element_ty).bytes(),
2695            SimdAlign::Vector => bx.align_of(vector_ty).bytes(),
2696        }
2697    }
2698
2699    if name == sym::simd_masked_load {
2700        // simd_masked_load<_, _, _, const ALIGN: SimdAlign>(mask: <N x i{M}>, pointer: *_ T, values: <N x T>) -> <N x T>
2701        // * N: number of elements in the input vectors
2702        // * T: type of the element to load
2703        // * M: any integer width is supported, will be truncated to i1
2704        // Loads contiguous elements from memory behind `pointer`, but only for
2705        // those lanes whose `mask` bit is enabled.
2706        // The memory addresses corresponding to the “off” lanes are not accessed.
2707
2708        let alignment = fn_args[3].expect_const().to_branch()[0].to_leaf().to_simd_alignment();
2709
2710        // The element type of the "mask" argument must be a signed integer type of any width
2711        let mask_ty = in_ty;
2712        let (mask_len, mask_elem) = (in_len, in_elem);
2713
2714        // The second argument must be a pointer matching the element type
2715        let pointer_ty = args[1].layout.ty;
2716
2717        // The last argument is a passthrough vector providing values for disabled lanes
2718        let values_ty = args[2].layout.ty;
2719        let (values_len, values_elem) = {
    if !values_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdThird {
                        span,
                        name,
                        ty: values_ty,
                    });
            return Err(err);
        };
    };
    values_ty.simd_size_and_type(bx.tcx())
}require_simd!(values_ty, SimdThird);
2720
2721        {
    if !ret_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    ret_ty.simd_size_and_type(bx.tcx())
};require_simd!(ret_ty, SimdReturn);
2722
2723        // Of the same length:
2724        if !(values_len == mask_len) {
    {
        let err =
            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(err);
    };
};require!(
2725            values_len == mask_len,
2726            InvalidMonomorphization::ThirdArgumentLength {
2727                span,
2728                name,
2729                in_len: mask_len,
2730                in_ty: mask_ty,
2731                arg_ty: values_ty,
2732                out_len: values_len
2733            }
2734        );
2735
2736        // The return type must match the last argument type
2737        if !(ret_ty == values_ty) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ExpectedReturnType {
                    span,
                    name,
                    in_ty: values_ty,
                    ret_ty,
                });
        return Err(err);
    };
};require!(
2738            ret_ty == values_ty,
2739            InvalidMonomorphization::ExpectedReturnType { span, name, in_ty: values_ty, ret_ty }
2740        );
2741
2742        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,
        } {
    {
        let err =
            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(err);
    };
};require!(
2743            matches!(
2744                *pointer_ty.kind(),
2745                ty::RawPtr(p_ty, _) if p_ty == values_elem && p_ty.kind() == values_elem.kind()
2746            ),
2747            InvalidMonomorphization::ExpectedElementType {
2748                span,
2749                name,
2750                expected_element: values_elem,
2751                second_arg: pointer_ty,
2752                in_elem: values_elem,
2753                in_ty: values_ty,
2754                mutability: ExpectedPointerMutability::Not,
2755            }
2756        );
2757
2758        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())
    }
    _ => {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::MaskWrongElementType {
                        span,
                        name,
                        ty: mask_elem,
                    });
            return Err(err);
        };
    }
}require_int_or_uint_ty!(
2759            mask_elem.kind(),
2760            InvalidMonomorphization::MaskWrongElementType { span, name, ty: mask_elem }
2761        );
2762
2763        let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
2764
2765        // Alignment of T, must be a constant integer value:
2766        let alignment = llvm_alignment(bx, alignment, values_ty, values_elem);
2767
2768        let llvm_pointer = bx.type_ptr();
2769
2770        // Type of the vector of elements:
2771        let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
2772
2773        let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
2774            let alignment = bx.const_i32(alignment as i32);
2775
2776            &[args[1].immediate(), alignment, mask, args[2].immediate()]
2777        } else {
2778            &[args[1].immediate(), mask, args[2].immediate()]
2779        };
2780
2781        let call = bx.call_intrinsic("llvm.masked.load", &[llvm_elem_vec_ty, llvm_pointer], args);
2782        if llvm_version >= (22, 0, 0) {
2783            crate::attributes::apply_to_callsite(
2784                call,
2785                crate::llvm::AttributePlace::Argument(0),
2786                &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
2787            )
2788        }
2789        return Ok(call);
2790    }
2791
2792    if name == sym::simd_masked_store {
2793        // simd_masked_store<_, _, _, const ALIGN: SimdAlign>(mask: <N x i{M}>, pointer: *mut T, values: <N x T>) -> ()
2794        // * N: number of elements in the input vectors
2795        // * T: type of the element to load
2796        // * M: any integer width is supported, will be truncated to i1
2797        // Stores contiguous elements to memory behind `pointer`, but only for
2798        // those lanes whose `mask` bit is enabled.
2799        // The memory addresses corresponding to the “off” lanes are not accessed.
2800
2801        let alignment = fn_args[3].expect_const().to_branch()[0].to_leaf().to_simd_alignment();
2802
2803        // The element type of the "mask" argument must be a signed integer type of any width
2804        let mask_ty = in_ty;
2805        let (mask_len, mask_elem) = (in_len, in_elem);
2806
2807        // The second argument must be a pointer matching the element type
2808        let pointer_ty = args[1].layout.ty;
2809
2810        // The last argument specifies the values to store to memory
2811        let values_ty = args[2].layout.ty;
2812        let (values_len, values_elem) = {
    if !values_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdThird {
                        span,
                        name,
                        ty: values_ty,
                    });
            return Err(err);
        };
    };
    values_ty.simd_size_and_type(bx.tcx())
}require_simd!(values_ty, SimdThird);
2813
2814        // Of the same length:
2815        if !(values_len == mask_len) {
    {
        let err =
            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(err);
    };
};require!(
2816            values_len == mask_len,
2817            InvalidMonomorphization::ThirdArgumentLength {
2818                span,
2819                name,
2820                in_len: mask_len,
2821                in_ty: mask_ty,
2822                arg_ty: values_ty,
2823                out_len: values_len
2824            }
2825        );
2826
2827        // The second argument must be a mutable pointer type matching the element type
2828        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,
        } {
    {
        let err =
            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(err);
    };
};require!(
2829            matches!(
2830                *pointer_ty.kind(),
2831                ty::RawPtr(p_ty, p_mutbl)
2832                    if p_ty == values_elem && p_ty.kind() == values_elem.kind() && p_mutbl.is_mut()
2833            ),
2834            InvalidMonomorphization::ExpectedElementType {
2835                span,
2836                name,
2837                expected_element: values_elem,
2838                second_arg: pointer_ty,
2839                in_elem: values_elem,
2840                in_ty: values_ty,
2841                mutability: ExpectedPointerMutability::Mut,
2842            }
2843        );
2844
2845        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())
    }
    _ => {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::MaskWrongElementType {
                        span,
                        name,
                        ty: mask_elem,
                    });
            return Err(err);
        };
    }
}require_int_or_uint_ty!(
2846            mask_elem.kind(),
2847            InvalidMonomorphization::MaskWrongElementType { span, name, ty: mask_elem }
2848        );
2849
2850        let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
2851
2852        // Alignment of T, must be a constant integer value:
2853        let alignment = llvm_alignment(bx, alignment, values_ty, values_elem);
2854
2855        let llvm_pointer = bx.type_ptr();
2856
2857        // Type of the vector of elements:
2858        let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
2859
2860        let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
2861            let alignment = bx.const_i32(alignment as i32);
2862            &[args[2].immediate(), args[1].immediate(), alignment, mask]
2863        } else {
2864            &[args[2].immediate(), args[1].immediate(), mask]
2865        };
2866
2867        let call = bx.call_intrinsic("llvm.masked.store", &[llvm_elem_vec_ty, llvm_pointer], args);
2868        if llvm_version >= (22, 0, 0) {
2869            crate::attributes::apply_to_callsite(
2870                call,
2871                crate::llvm::AttributePlace::Argument(1),
2872                &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
2873            )
2874        }
2875        return Ok(call);
2876    }
2877
2878    if name == sym::simd_scatter {
2879        // simd_scatter(values: <N x T>, pointers: <N x *mut T>,
2880        //             mask: <N x i{M}>) -> ()
2881        // * N: number of elements in the input vectors
2882        // * T: type of the element to load
2883        // * M: any integer width is supported, will be truncated to i1
2884
2885        // All types must be simd vector types
2886        // The second argument must be a simd vector with an element type that's a pointer
2887        // to the element type of the first argument
2888        let (_, element_ty0) = {
    if !in_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdFirst {
                        span,
                        name,
                        ty: in_ty,
                    });
            return Err(err);
        };
    };
    in_ty.simd_size_and_type(bx.tcx())
}require_simd!(in_ty, SimdFirst);
2889        let (element_len1, element_ty1) = {
    if !args[1].layout.ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdSecond {
                        span,
                        name,
                        ty: args[1].layout.ty,
                    });
            return Err(err);
        };
    };
    args[1].layout.ty.simd_size_and_type(bx.tcx())
}require_simd!(args[1].layout.ty, SimdSecond);
2890        let (element_len2, element_ty2) = {
    if !args[2].layout.ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdThird {
                        span,
                        name,
                        ty: args[2].layout.ty,
                    });
            return Err(err);
        };
    };
    args[2].layout.ty.simd_size_and_type(bx.tcx())
}require_simd!(args[2].layout.ty, SimdThird);
2891
2892        // Of the same length:
2893        if !(in_len == element_len1) {
    {
        let err =
            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(err);
    };
};require!(
2894            in_len == element_len1,
2895            InvalidMonomorphization::SecondArgumentLength {
2896                span,
2897                name,
2898                in_len,
2899                in_ty,
2900                arg_ty: args[1].layout.ty,
2901                out_len: element_len1
2902            }
2903        );
2904        if !(in_len == element_len2) {
    {
        let err =
            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(err);
    };
};require!(
2905            in_len == element_len2,
2906            InvalidMonomorphization::ThirdArgumentLength {
2907                span,
2908                name,
2909                in_len,
2910                in_ty,
2911                arg_ty: args[2].layout.ty,
2912                out_len: element_len2
2913            }
2914        );
2915
2916        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,
        } {
    {
        let err =
            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(err);
    };
};require!(
2917            matches!(
2918                *element_ty1.kind(),
2919                ty::RawPtr(p_ty, p_mutbl)
2920                    if p_ty == in_elem && p_mutbl.is_mut() && p_ty.kind() == element_ty0.kind()
2921            ),
2922            InvalidMonomorphization::ExpectedElementType {
2923                span,
2924                name,
2925                expected_element: element_ty1,
2926                second_arg: args[1].layout.ty,
2927                in_elem,
2928                in_ty,
2929                mutability: ExpectedPointerMutability::Mut,
2930            }
2931        );
2932
2933        // The element type of the third argument must be an integer type of any width:
2934        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())
    }
    _ => {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::MaskWrongElementType {
                        span,
                        name,
                        ty: element_ty2,
                    });
            return Err(err);
        };
    }
}require_int_or_uint_ty!(
2935            element_ty2.kind(),
2936            InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 }
2937        );
2938
2939        // Alignment of T, must be a constant integer value:
2940        let alignment = bx.align_of(in_elem).bytes();
2941
2942        // Truncate the mask vector to a vector of i1s:
2943        let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
2944
2945        // Type of the vector of pointers:
2946        let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len);
2947
2948        // Type of the vector of elements:
2949        let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
2950        let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
2951            let alignment = bx.const_i32(alignment as i32);
2952            &[args[0].immediate(), args[1].immediate(), alignment, mask]
2953        } else {
2954            &[args[0].immediate(), args[1].immediate(), mask]
2955        };
2956        let call = bx.call_intrinsic(
2957            "llvm.masked.scatter",
2958            &[llvm_elem_vec_ty, llvm_pointer_vec_ty],
2959            args,
2960        );
2961        if llvm_version >= (22, 0, 0) {
2962            crate::attributes::apply_to_callsite(
2963                call,
2964                crate::llvm::AttributePlace::Argument(1),
2965                &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
2966            )
2967        }
2968        return Ok(call);
2969    }
2970
2971    macro_rules! arith_red {
2972        ($name:ident : $integer_reduce:ident, $float_reduce:ident, $ordered:expr, $op:ident,
2973         $identity:expr) => {
2974            if name == sym::$name {
2975                require!(
2976                    ret_ty == in_elem,
2977                    InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
2978                );
2979                return match in_elem.kind() {
2980                    ty::Int(_) | ty::Uint(_) => {
2981                        let r = bx.$integer_reduce(args[0].immediate());
2982                        if $ordered {
2983                            // if overflow occurs, the result is the
2984                            // mathematical result modulo 2^n:
2985                            Ok(bx.$op(args[1].immediate(), r))
2986                        } else {
2987                            Ok(bx.$integer_reduce(args[0].immediate()))
2988                        }
2989                    }
2990                    ty::Float(f) => {
2991                        let acc = if $ordered {
2992                            // ordered arithmetic reductions take an accumulator
2993                            args[1].immediate()
2994                        } else {
2995                            // unordered arithmetic reductions use the identity accumulator
2996                            match f.bit_width() {
2997                                32 => bx.const_real(bx.type_f32(), $identity),
2998                                64 => bx.const_real(bx.type_f64(), $identity),
2999                                v => return_error!(
3000                                    InvalidMonomorphization::UnsupportedSymbolOfSize {
3001                                        span,
3002                                        name,
3003                                        symbol: sym::$name,
3004                                        in_ty,
3005                                        in_elem,
3006                                        size: v,
3007                                        ret_ty
3008                                    }
3009                                ),
3010                            }
3011                        };
3012                        Ok(bx.$float_reduce(acc, args[0].immediate()))
3013                    }
3014                    _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
3015                        span,
3016                        name,
3017                        symbol: sym::$name,
3018                        in_ty,
3019                        in_elem,
3020                        ret_ty
3021                    }),
3022                };
3023            }
3024        };
3025    }
3026
3027    if name == sym::simd_reduce_add_ordered {
    if !(ret_ty == in_elem) {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                        span,
                        name,
                        in_elem,
                        in_ty,
                        ret_ty,
                    });
            return Err(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 => {
                                let err =
                                    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(err);
                            }
                        }
                    };
                Ok(bx.vector_reduce_fadd(acc, args[0].immediate()))
            }
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_add_ordered,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};arith_red!(simd_reduce_add_ordered: vector_reduce_add, vector_reduce_fadd, true, add, -0.0);
3028    if name == sym::simd_reduce_mul_ordered {
    if !(ret_ty == in_elem) {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                        span,
                        name,
                        in_elem,
                        in_ty,
                        ret_ty,
                    });
            return Err(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 => {
                                let err =
                                    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(err);
                            }
                        }
                    };
                Ok(bx.vector_reduce_fmul(acc, args[0].immediate()))
            }
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_mul_ordered,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};arith_red!(simd_reduce_mul_ordered: vector_reduce_mul, vector_reduce_fmul, true, mul, 1.0);
3029    if name == sym::simd_reduce_add_unordered {
    if !(ret_ty == in_elem) {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                        span,
                        name,
                        in_elem,
                        in_ty,
                        ret_ty,
                    });
            return Err(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 => {
                                let err =
                                    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(err);
                            }
                        }
                    };
                Ok(bx.vector_reduce_fadd_reassoc(acc, args[0].immediate()))
            }
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_add_unordered,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};arith_red!(
3030        simd_reduce_add_unordered: vector_reduce_add,
3031        vector_reduce_fadd_reassoc,
3032        false,
3033        add,
3034        -0.0
3035    );
3036    if name == sym::simd_reduce_mul_unordered {
    if !(ret_ty == in_elem) {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                        span,
                        name,
                        in_elem,
                        in_ty,
                        ret_ty,
                    });
            return Err(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 => {
                                let err =
                                    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(err);
                            }
                        }
                    };
                Ok(bx.vector_reduce_fmul_reassoc(acc, args[0].immediate()))
            }
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_mul_unordered,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};arith_red!(
3037        simd_reduce_mul_unordered: vector_reduce_mul,
3038        vector_reduce_fmul_reassoc,
3039        false,
3040        mul,
3041        1.0
3042    );
3043
3044    macro_rules! minmax_red {
3045        ($name:ident: $int_red:ident) => {
3046            if name == sym::$name {
3047                require!(
3048                    ret_ty == in_elem,
3049                    InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
3050                );
3051                return match in_elem.kind() {
3052                    ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)),
3053                    ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)),
3054                    _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
3055                        span,
3056                        name,
3057                        symbol: sym::$name,
3058                        in_ty,
3059                        in_elem,
3060                        ret_ty
3061                    }),
3062                };
3063            }
3064        };
3065    }
3066
3067    // Currently no support for float due to <https://github.com/llvm/llvm-project/issues/185827>.
3068    if name == sym::simd_reduce_min {
    if !(ret_ty == in_elem) {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                        span,
                        name,
                        in_elem,
                        in_ty,
                        ret_ty,
                    });
            return Err(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)),
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_min,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};minmax_red!(simd_reduce_min: vector_reduce_min);
3069    if name == sym::simd_reduce_max {
    if !(ret_ty == in_elem) {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                        span,
                        name,
                        in_elem,
                        in_ty,
                        ret_ty,
                    });
            return Err(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)),
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_max,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};minmax_red!(simd_reduce_max: vector_reduce_max);
3070
3071    macro_rules! bitwise_red {
3072        ($name:ident : $red:ident, $boolean:expr) => {
3073            if name == sym::$name {
3074                let input = if !$boolean {
3075                    require!(
3076                        ret_ty == in_elem,
3077                        InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
3078                    );
3079                    args[0].immediate()
3080                } else {
3081                    let bitwidth = match in_elem.kind() {
3082                        ty::Int(i) => {
3083                            i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size().bits())
3084                        }
3085                        ty::Uint(i) => {
3086                            i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size().bits())
3087                        }
3088                        _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
3089                            span,
3090                            name,
3091                            symbol: sym::$name,
3092                            in_ty,
3093                            in_elem,
3094                            ret_ty
3095                        }),
3096                    };
3097
3098                    vector_mask_to_bitmask(bx, args[0].immediate(), bitwidth, in_len as _)
3099                };
3100                return match in_elem.kind() {
3101                    ty::Int(_) | ty::Uint(_) => {
3102                        let r = bx.$red(input);
3103                        Ok(r)
3104                    }
3105                    _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
3106                        span,
3107                        name,
3108                        symbol: sym::$name,
3109                        in_ty,
3110                        in_elem,
3111                        ret_ty
3112                    }),
3113                };
3114            }
3115        };
3116    }
3117
3118    if name == sym::simd_reduce_and {
    let input =
        if !false {
            if !(ret_ty == in_elem) {
                {
                    let err =
                        bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                                span,
                                name,
                                in_elem,
                                in_ty,
                                ret_ty,
                            });
                    return Err(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())
                    }
                    _ => {
                        let err =
                            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                                    span,
                                    name,
                                    symbol: sym::simd_reduce_and,
                                    in_ty,
                                    in_elem,
                                    ret_ty,
                                });
                        return Err(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(r)
            }
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_and,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};bitwise_red!(simd_reduce_and: vector_reduce_and, false);
3119    if name == sym::simd_reduce_or {
    let input =
        if !false {
            if !(ret_ty == in_elem) {
                {
                    let err =
                        bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                                span,
                                name,
                                in_elem,
                                in_ty,
                                ret_ty,
                            });
                    return Err(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())
                    }
                    _ => {
                        let err =
                            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                                    span,
                                    name,
                                    symbol: sym::simd_reduce_or,
                                    in_ty,
                                    in_elem,
                                    ret_ty,
                                });
                        return Err(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(r)
            }
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_or,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};bitwise_red!(simd_reduce_or: vector_reduce_or, false);
3120    if name == sym::simd_reduce_xor {
    let input =
        if !false {
            if !(ret_ty == in_elem) {
                {
                    let err =
                        bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                                span,
                                name,
                                in_elem,
                                in_ty,
                                ret_ty,
                            });
                    return Err(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())
                    }
                    _ => {
                        let err =
                            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                                    span,
                                    name,
                                    symbol: sym::simd_reduce_xor,
                                    in_ty,
                                    in_elem,
                                    ret_ty,
                                });
                        return Err(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(r)
            }
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_xor,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};bitwise_red!(simd_reduce_xor: vector_reduce_xor, false);
3121    if name == sym::simd_reduce_all {
    let input =
        if !true {
            if !(ret_ty == in_elem) {
                {
                    let err =
                        bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                                span,
                                name,
                                in_elem,
                                in_ty,
                                ret_ty,
                            });
                    return Err(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())
                    }
                    _ => {
                        let err =
                            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                                    span,
                                    name,
                                    symbol: sym::simd_reduce_all,
                                    in_ty,
                                    in_elem,
                                    ret_ty,
                                });
                        return Err(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(r)
            }
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_all,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};bitwise_red!(simd_reduce_all: vector_reduce_and, true);
3122    if name == sym::simd_reduce_any {
    let input =
        if !true {
            if !(ret_ty == in_elem) {
                {
                    let err =
                        bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnType {
                                span,
                                name,
                                in_elem,
                                in_ty,
                                ret_ty,
                            });
                    return Err(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())
                    }
                    _ => {
                        let err =
                            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                                    span,
                                    name,
                                    symbol: sym::simd_reduce_any,
                                    in_ty,
                                    in_elem,
                                    ret_ty,
                                });
                        return Err(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(r)
            }
            _ => {
                let err =
                    bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedSymbol {
                            span,
                            name,
                            symbol: sym::simd_reduce_any,
                            in_ty,
                            in_elem,
                            ret_ty,
                        });
                return Err(err);
            }
        };
};bitwise_red!(simd_reduce_any: vector_reduce_or, true);
3123
3124    if name == sym::simd_cast_ptr {
3125        let (out_len, out_elem) = {
    if !ret_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    ret_ty.simd_size_and_type(bx.tcx())
}require_simd!(ret_ty, SimdReturn);
3126        if !(in_len == out_len) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnLengthInputType {
                    span,
                    name,
                    in_len,
                    in_ty,
                    ret_ty,
                    out_len,
                });
        return Err(err);
    };
};require!(
3127            in_len == out_len,
3128            InvalidMonomorphization::ReturnLengthInputType {
3129                span,
3130                name,
3131                in_len,
3132                in_ty,
3133                ret_ty,
3134                out_len
3135            }
3136        );
3137
3138        match in_elem.kind() {
3139            ty::RawPtr(p_ty, _) => {
3140                let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| {
3141                    bx.tcx.normalize_erasing_regions(bx.typing_env(), ty)
3142                });
3143                if !metadata.is_unit() {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::CastWidePointer {
                    span,
                    name,
                    ty: in_elem,
                });
        return Err(err);
    };
};require!(
3144                    metadata.is_unit(),
3145                    InvalidMonomorphization::CastWidePointer { span, name, ty: in_elem }
3146                );
3147            }
3148            _ => {
3149                {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::ExpectedPointer {
                span,
                name,
                ty: in_elem,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem })
3150            }
3151        }
3152        match out_elem.kind() {
3153            ty::RawPtr(p_ty, _) => {
3154                let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| {
3155                    bx.tcx.normalize_erasing_regions(bx.typing_env(), ty)
3156                });
3157                if !metadata.is_unit() {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::CastWidePointer {
                    span,
                    name,
                    ty: out_elem,
                });
        return Err(err);
    };
};require!(
3158                    metadata.is_unit(),
3159                    InvalidMonomorphization::CastWidePointer { span, name, ty: out_elem }
3160                );
3161            }
3162            _ => {
3163                {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::ExpectedPointer {
                span,
                name,
                ty: out_elem,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem })
3164            }
3165        }
3166
3167        return Ok(args[0].immediate());
3168    }
3169
3170    if name == sym::simd_expose_provenance {
3171        let (out_len, out_elem) = {
    if !ret_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    ret_ty.simd_size_and_type(bx.tcx())
}require_simd!(ret_ty, SimdReturn);
3172        if !(in_len == out_len) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnLengthInputType {
                    span,
                    name,
                    in_len,
                    in_ty,
                    ret_ty,
                    out_len,
                });
        return Err(err);
    };
};require!(
3173            in_len == out_len,
3174            InvalidMonomorphization::ReturnLengthInputType {
3175                span,
3176                name,
3177                in_len,
3178                in_ty,
3179                ret_ty,
3180                out_len
3181            }
3182        );
3183
3184        match in_elem.kind() {
3185            ty::RawPtr(_, _) => {}
3186            _ => {
3187                {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::ExpectedPointer {
                span,
                name,
                ty: in_elem,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem })
3188            }
3189        }
3190        match out_elem.kind() {
3191            ty::Uint(ty::UintTy::Usize) => {}
3192            _ => {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::ExpectedUsize {
                span,
                name,
                ty: out_elem,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: out_elem }),
3193        }
3194
3195        return Ok(bx.ptrtoint(args[0].immediate(), llret_ty));
3196    }
3197
3198    if name == sym::simd_with_exposed_provenance {
3199        let (out_len, out_elem) = {
    if !ret_ty.is_simd() {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    ret_ty.simd_size_and_type(bx.tcx())
}require_simd!(ret_ty, SimdReturn);
3200        if !(in_len == out_len) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnLengthInputType {
                    span,
                    name,
                    in_len,
                    in_ty,
                    ret_ty,
                    out_len,
                });
        return Err(err);
    };
};require!(
3201            in_len == out_len,
3202            InvalidMonomorphization::ReturnLengthInputType {
3203                span,
3204                name,
3205                in_len,
3206                in_ty,
3207                ret_ty,
3208                out_len
3209            }
3210        );
3211
3212        match in_elem.kind() {
3213            ty::Uint(ty::UintTy::Usize) => {}
3214            _ => {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::ExpectedUsize {
                span,
                name,
                ty: in_elem,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: in_elem }),
3215        }
3216        match out_elem.kind() {
3217            ty::RawPtr(_, _) => {}
3218            _ => {
3219                {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::ExpectedPointer {
                span,
                name,
                ty: out_elem,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem })
3220            }
3221        }
3222
3223        return Ok(bx.inttoptr(args[0].immediate(), llret_ty));
3224    }
3225
3226    if name == sym::simd_cast || name == sym::simd_as {
3227        let (out_len, out_elem, out_num_vecs) = {
    if !(ret_ty.is_simd() || ret_ty.is_scalable_vector()) {
        {
            let err =
                bx.sess().dcx().emit_err(InvalidMonomorphization::SimdReturn {
                        span,
                        name,
                        ty: ret_ty,
                    });
            return Err(err);
        };
    };
    if ret_ty.is_simd() {
        let (len, ty) = ret_ty.simd_size_and_type(bx.tcx());
        (len, ty, None)
    } else {
        let (count, ty, num_vecs) =
            ret_ty.scalable_vector_parts(bx.tcx()).expect("`is_scalable_vector` was wrong");
        (count as u64, ty, Some(num_vecs))
    }
}require_simd_or_scalable!(ret_ty, SimdReturn);
3228        if !(in_len == out_len) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnLengthInputType {
                    span,
                    name,
                    in_len,
                    in_ty,
                    ret_ty,
                    out_len,
                });
        return Err(err);
    };
};require!(
3229            in_len == out_len,
3230            InvalidMonomorphization::ReturnLengthInputType {
3231                span,
3232                name,
3233                in_len,
3234                in_ty,
3235                ret_ty,
3236                out_len
3237            }
3238        );
3239        if !(in_num_vecs == out_num_vecs) {
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::ReturnNumVecsInputType {
                    span,
                    name,
                    in_num_vecs: in_num_vecs.unwrap_or(NumScalableVectors(1)),
                    in_ty,
                    ret_ty,
                    out_num_vecs: out_num_vecs.unwrap_or(NumScalableVectors(1)),
                });
        return Err(err);
    };
};require!(
3240            in_num_vecs == out_num_vecs,
3241            InvalidMonomorphization::ReturnNumVecsInputType {
3242                span,
3243                name,
3244                in_num_vecs: in_num_vecs.unwrap_or(NumScalableVectors(1)),
3245                in_ty,
3246                ret_ty,
3247                out_num_vecs: out_num_vecs.unwrap_or(NumScalableVectors(1))
3248            }
3249        );
3250
3251        // Casting cares about nominal type, not just structural type
3252        if in_elem == out_elem {
3253            return Ok(args[0].immediate());
3254        }
3255
3256        #[derive(#[automatically_derived]
impl ::core::marker::Copy for Sign { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Sign { }
#[automatically_derived]
impl ::core::clone::Clone for Sign {
    #[inline]
    fn clone(&self) -> Sign { *self }
}Clone)]
3257        enum Sign {
3258            Unsigned,
3259            Signed,
3260        }
3261        use Sign::*;
3262
3263        enum Style {
3264            Float,
3265            Int(Sign),
3266            Unsupported,
3267        }
3268
3269        let (in_style, in_width) = match in_elem.kind() {
3270            // vectors of pointer-sized integers should've been
3271            // disallowed before here, so this unwrap is safe.
3272            ty::Int(i) => (
3273                Style::Int(Signed),
3274                i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
3275            ),
3276            ty::Uint(u) => (
3277                Style::Int(Unsigned),
3278                u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
3279            ),
3280            ty::Float(f) => (Style::Float, f.bit_width()),
3281            _ => (Style::Unsupported, 0),
3282        };
3283        let (out_style, out_width) = match out_elem.kind() {
3284            ty::Int(i) => (
3285                Style::Int(Signed),
3286                i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
3287            ),
3288            ty::Uint(u) => (
3289                Style::Int(Unsigned),
3290                u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
3291            ),
3292            ty::Float(f) => (Style::Float, f.bit_width()),
3293            _ => (Style::Unsupported, 0),
3294        };
3295
3296        match (in_style, out_style) {
3297            (Style::Int(sign), Style::Int(_)) => {
3298                return Ok(match in_width.cmp(&out_width) {
3299                    Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty),
3300                    Ordering::Equal => args[0].immediate(),
3301                    Ordering::Less => match sign {
3302                        Sign::Signed => bx.sext(args[0].immediate(), llret_ty),
3303                        Sign::Unsigned => bx.zext(args[0].immediate(), llret_ty),
3304                    },
3305                });
3306            }
3307            (Style::Int(Sign::Signed), Style::Float) => {
3308                return Ok(bx.sitofp(args[0].immediate(), llret_ty));
3309            }
3310            (Style::Int(Sign::Unsigned), Style::Float) => {
3311                return Ok(bx.uitofp(args[0].immediate(), llret_ty));
3312            }
3313            (Style::Float, Style::Int(sign)) => {
3314                return Ok(match (sign, name == sym::simd_as) {
3315                    (Sign::Unsigned, false) => bx.fptoui(args[0].immediate(), llret_ty),
3316                    (Sign::Signed, false) => bx.fptosi(args[0].immediate(), llret_ty),
3317                    (_, true) => bx.cast_float_to_int(
3318                        #[allow(non_exhaustive_omitted_patterns)] match sign {
    Sign::Signed => true,
    _ => false,
}matches!(sign, Sign::Signed),
3319                        args[0].immediate(),
3320                        llret_ty,
3321                    ),
3322                });
3323            }
3324            (Style::Float, Style::Float) => {
3325                return Ok(match in_width.cmp(&out_width) {
3326                    Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty),
3327                    Ordering::Equal => args[0].immediate(),
3328                    Ordering::Less => bx.fpext(args[0].immediate(), llret_ty),
3329                });
3330            }
3331            _ => {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedCast {
                span,
                name,
                in_ty,
                in_elem,
                ret_ty,
                out_elem,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::UnsupportedCast {
3332                span,
3333                name,
3334                in_ty,
3335                in_elem,
3336                ret_ty,
3337                out_elem
3338            }),
3339        }
3340    }
3341    macro_rules! arith_binary {
3342        ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
3343            $(if name == sym::$name {
3344                match in_elem.kind() {
3345                    $($(ty::$p(_))|* => {
3346                        return Ok(bx.$call(args[0].immediate(), args[1].immediate()))
3347                    })*
3348                    _ => {},
3349                }
3350                return_error!(
3351                    InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem }
3352                );
3353            })*
3354        }
3355    }
3356    if name == sym::simd_add {
    match in_elem.kind() {
        ty::Uint(_) | ty::Int(_) => {
            return Ok(bx.add(args[0].immediate(), args[1].immediate()))
        }
        ty::Float(_) => {
            return Ok(bx.fadd(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_sub {
    match in_elem.kind() {
        ty::Uint(_) | ty::Int(_) => {
            return Ok(bx.sub(args[0].immediate(), args[1].immediate()))
        }
        ty::Float(_) => {
            return Ok(bx.fsub(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_mul {
    match in_elem.kind() {
        ty::Uint(_) | ty::Int(_) => {
            return Ok(bx.mul(args[0].immediate(), args[1].immediate()))
        }
        ty::Float(_) => {
            return Ok(bx.fmul(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_div {
    match in_elem.kind() {
        ty::Uint(_) => {
            return Ok(bx.udiv(args[0].immediate(), args[1].immediate()))
        }
        ty::Int(_) => {
            return Ok(bx.sdiv(args[0].immediate(), args[1].immediate()))
        }
        ty::Float(_) => {
            return Ok(bx.fdiv(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_rem {
    match in_elem.kind() {
        ty::Uint(_) => {
            return Ok(bx.urem(args[0].immediate(), args[1].immediate()))
        }
        ty::Int(_) => {
            return Ok(bx.srem(args[0].immediate(), args[1].immediate()))
        }
        ty::Float(_) => {
            return Ok(bx.frem(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_shl {
    match in_elem.kind() {
        ty::Uint(_) | ty::Int(_) => {
            return Ok(bx.shl(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_shr {
    match in_elem.kind() {
        ty::Uint(_) => {
            return Ok(bx.lshr(args[0].immediate(), args[1].immediate()))
        }
        ty::Int(_) => {
            return Ok(bx.ashr(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_and {
    match in_elem.kind() {
        ty::Uint(_) | ty::Int(_) => {
            return Ok(bx.and(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_or {
    match in_elem.kind() {
        ty::Uint(_) | ty::Int(_) => {
            return Ok(bx.or(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_xor {
    match in_elem.kind() {
        ty::Uint(_) | ty::Int(_) => {
            return Ok(bx.xor(args[0].immediate(), args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_maximum_number_nsz {
    match in_elem.kind() {
        ty::Float(_) => {
            return Ok(bx.maximum_number_nsz(args[0].immediate(),
                        args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}
if name == sym::simd_minimum_number_nsz {
    match in_elem.kind() {
        ty::Float(_) => {
            return Ok(bx.minimum_number_nsz(args[0].immediate(),
                        args[1].immediate()))
        }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}arith_binary! {
3357        simd_add: Uint, Int => add, Float => fadd;
3358        simd_sub: Uint, Int => sub, Float => fsub;
3359        simd_mul: Uint, Int => mul, Float => fmul;
3360        simd_div: Uint => udiv, Int => sdiv, Float => fdiv;
3361        simd_rem: Uint => urem, Int => srem, Float => frem;
3362        simd_shl: Uint, Int => shl;
3363        simd_shr: Uint => lshr, Int => ashr;
3364        simd_and: Uint, Int => and;
3365        simd_or: Uint, Int => or;
3366        simd_xor: Uint, Int => xor;
3367        simd_maximum_number_nsz: Float => maximum_number_nsz;
3368        simd_minimum_number_nsz: Float => minimum_number_nsz;
3369
3370    }
3371    macro_rules! arith_unary {
3372        ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
3373            $(if name == sym::$name {
3374                match in_elem.kind() {
3375                    $($(ty::$p(_))|* => {
3376                        return Ok(bx.$call(args[0].immediate()))
3377                    })*
3378                    _ => {},
3379                }
3380                return_error!(
3381                    InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem }
3382                );
3383            })*
3384        }
3385    }
3386    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())) }
        _ => {}
    }
    {
        let err =
            bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                    span,
                    name,
                    in_ty,
                    in_elem,
                });
        return Err(err);
    };
}arith_unary! {
3387        simd_neg: Int => neg, Float => fneg;
3388    }
3389
3390    // Unary integer intrinsics
3391    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_carryless_mul | sym::simd_funnel_shl |
        sym::simd_funnel_shr => true,
    _ => false,
}matches!(
3392        name,
3393        sym::simd_bswap
3394            | sym::simd_bitreverse
3395            | sym::simd_ctlz
3396            | sym::simd_ctpop
3397            | sym::simd_cttz
3398            | sym::simd_carryless_mul
3399            | sym::simd_funnel_shl
3400            | sym::simd_funnel_shr
3401    ) {
3402        let vec_ty = bx.cx.type_vector(
3403            match *in_elem.kind() {
3404                ty::Int(i) => bx.cx.type_int_from_ty(i),
3405                ty::Uint(i) => bx.cx.type_uint_from_ty(i),
3406                _ => {
    let err =
        bx.sess().dcx().emit_err(InvalidMonomorphization::UnsupportedOperation {
                span,
                name,
                in_ty,
                in_elem,
            });
    return Err(err);
}return_error!(InvalidMonomorphization::UnsupportedOperation {
3407                    span,
3408                    name,
3409                    in_ty,
3410                    in_elem
3411                }),
3412            },
3413            in_len as u64,
3414        );
3415        let llvm_intrinsic = match name {
3416            sym::simd_bswap => "llvm.bswap",
3417            sym::simd_bitreverse => "llvm.bitreverse",
3418            sym::simd_ctlz => "llvm.ctlz",
3419            sym::simd_ctpop => "llvm.ctpop",
3420            sym::simd_cttz => "llvm.cttz",
3421            sym::simd_funnel_shl => "llvm.fshl",
3422            sym::simd_funnel_shr => "llvm.fshr",
3423            sym::simd_carryless_mul => "llvm.clmul",
3424            _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
3425        };
3426        let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits();
3427
3428        return match name {
3429            // byte swap is no-op for i8/u8
3430            sym::simd_bswap if int_size == 8 => Ok(args[0].immediate()),
3431            sym::simd_ctlz | sym::simd_cttz => {
3432                // for the (int, i1 immediate) pair, the second arg adds `(0, true) => poison`
3433                let dont_poison_on_zero = bx.const_int(bx.type_i1(), 0);
3434                Ok(bx.call_intrinsic(
3435                    llvm_intrinsic,
3436                    &[vec_ty],
3437                    &[args[0].immediate(), dont_poison_on_zero],
3438                ))
3439            }
3440            sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctpop => {
3441                // simple unary argument cases
3442                Ok(bx.call_intrinsic(llvm_intrinsic, &[vec_ty], &[args[0].immediate()]))
3443            }
3444            sym::simd_funnel_shl | sym::simd_funnel_shr => Ok(bx.call_intrinsic(
3445                llvm_intrinsic,
3446                &[vec_ty],
3447                &[args[0].immediate(), args[1].immediate(), args[2].immediate()],
3448            )),
3449            sym::simd_carryless_mul => {
3450                if crate::llvm_util::get_version() >= (22, 0, 0) {
3451                    Ok(bx.call_intrinsic(
3452                        llvm_intrinsic,
3453                        &[vec_ty],
3454                        &[args[0].immediate(), args[1].immediate()],
3455                    ))
3456                } else {
3457                    ::rustc_middle::util::bug::span_bug_fmt(span,
    format_args!("`simd_carryless_mul` needs LLVM 22 or higher"));span_bug!(span, "`simd_carryless_mul` needs LLVM 22 or higher");
3458                }
3459            }
3460            _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
3461        };
3462    }
3463
3464    if name == sym::simd_arith_offset {
3465        // This also checks that the first operand is a ptr type.
3466        let pointee = in_elem.builtin_deref(true).unwrap_or_else(|| {
3467            ::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")
3468        });
3469        let layout = bx.layout_of(pointee);
3470        let ptrs = args[0].immediate();
3471        // The second argument must be a ptr-sized integer.
3472        // (We don't care about the signedness, this is wrapping anyway.)
3473        let (_offsets_len, offsets_elem) = args[1].layout.ty.simd_size_and_type(bx.tcx());
3474        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)) {
3475            ::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!(
3476                span,
3477                "must be called with a vector of pointer-sized integers as second argument"
3478            );
3479        }
3480        let offsets = args[1].immediate();
3481
3482        return Ok(bx.gep(bx.backend_type(layout), ptrs, &[offsets]));
3483    }
3484
3485    if name == sym::simd_saturating_add || name == sym::simd_saturating_sub {
3486        let lhs = args[0].immediate();
3487        let rhs = args[1].immediate();
3488        let is_add = name == sym::simd_saturating_add;
3489        let (signed, elem_ty) = match *in_elem.kind() {
3490            ty::Int(i) => (true, bx.cx.type_int_from_ty(i)),
3491            ty::Uint(i) => (false, bx.cx.type_uint_from_ty(i)),
3492            _ => {
3493                {
    let err =
        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(err);
};return_error!(InvalidMonomorphization::ExpectedVectorElementType {
3494                    span,
3495                    name,
3496                    expected_element: args[0].layout.ty.simd_size_and_type(bx.tcx()).1,
3497                    vector_type: args[0].layout.ty
3498                });
3499            }
3500        };
3501        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!(
3502            "llvm.{}{}.sat",
3503            if signed { 's' } else { 'u' },
3504            if is_add { "add" } else { "sub" },
3505        );
3506        let vec_ty = bx.cx.type_vector(elem_ty, in_len as u64);
3507
3508        return Ok(bx.call_intrinsic(llvm_intrinsic, &[vec_ty], &[lhs, rhs]));
3509    }
3510
3511    ::rustc_middle::util::bug::span_bug_fmt(span,
    format_args!("unknown SIMD intrinsic"));span_bug!(span, "unknown SIMD intrinsic");
3512}