rustc_const_eval/interpret/
intrinsics.rs

1//! Intrinsics and other functions that the interpreter executes without
2//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
3//! and miri.
4
5use std::assert_matches::assert_matches;
6
7use rustc_abi::Size;
8use rustc_apfloat::ieee::{Double, Half, Quad, Single};
9use rustc_hir::def_id::DefId;
10use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
11use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement};
12use rustc_middle::ty::{GenericArgsRef, Ty, TyCtxt};
13use rustc_middle::{bug, ty};
14use rustc_span::{Symbol, sym};
15use tracing::trace;
16
17use super::memory::MemoryKind;
18use super::util::ensure_monomorphic_enough;
19use super::{
20    Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult,
21    MPlaceTy, Machine, OpTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval,
22    err_ub_custom, err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
23};
24use crate::fluent_generated as fluent;
25
26/// Directly returns an `Allocation` containing an absolute path representation of the given type.
27pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> {
28    let path = crate::util::type_name(tcx, ty);
29    let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes());
30    tcx.mk_const_alloc(alloc)
31}
32
33/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated
34/// inside an `InterpCx` and instead have their value computed directly from rustc internal info.
35pub(crate) fn eval_nullary_intrinsic<'tcx>(
36    tcx: TyCtxt<'tcx>,
37    typing_env: ty::TypingEnv<'tcx>,
38    def_id: DefId,
39    args: GenericArgsRef<'tcx>,
40) -> InterpResult<'tcx, ConstValue<'tcx>> {
41    let tp_ty = args.type_at(0);
42    let name = tcx.item_name(def_id);
43    interp_ok(match name {
44        sym::type_name => {
45            ensure_monomorphic_enough(tcx, tp_ty)?;
46            let alloc = alloc_type_name(tcx, tp_ty);
47            ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() }
48        }
49        sym::needs_drop => {
50            ensure_monomorphic_enough(tcx, tp_ty)?;
51            ConstValue::from_bool(tp_ty.needs_drop(tcx, typing_env))
52        }
53        sym::pref_align_of => {
54            // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
55            let layout = tcx
56                .layout_of(typing_env.as_query_input(tp_ty))
57                .map_err(|e| err_inval!(Layout(*e)))?;
58            ConstValue::from_target_usize(layout.align.pref.bytes(), &tcx)
59        }
60        sym::type_id => {
61            ensure_monomorphic_enough(tcx, tp_ty)?;
62            ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128())
63        }
64        sym::variant_count => match match tp_ty.kind() {
65            // Pattern types have the same number of variants as their base type.
66            // Even if we restrict e.g. which variants are valid, the variants are essentially just uninhabited.
67            // And `Result<(), !>` still has two variants according to `variant_count`.
68            ty::Pat(base, _) => *base,
69            _ => tp_ty,
70        }
71        .kind()
72        {
73            // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
74            ty::Adt(adt, _) => ConstValue::from_target_usize(adt.variants().len() as u64, &tcx),
75            ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => {
76                throw_inval!(TooGeneric)
77            }
78            ty::Pat(..) => unreachable!(),
79            ty::Bound(_, _) => bug!("bound ty during ctfe"),
80            ty::Bool
81            | ty::Char
82            | ty::Int(_)
83            | ty::Uint(_)
84            | ty::Float(_)
85            | ty::Foreign(_)
86            | ty::Str
87            | ty::Array(_, _)
88            | ty::Slice(_)
89            | ty::RawPtr(_, _)
90            | ty::Ref(_, _, _)
91            | ty::FnDef(_, _)
92            | ty::FnPtr(..)
93            | ty::Dynamic(_, _, _)
94            | ty::Closure(_, _)
95            | ty::CoroutineClosure(_, _)
96            | ty::Coroutine(_, _)
97            | ty::CoroutineWitness(..)
98            | ty::UnsafeBinder(_)
99            | ty::Never
100            | ty::Tuple(_)
101            | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx),
102        },
103        other => bug!("`{}` is not a zero arg intrinsic", other),
104    })
105}
106
107impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
108    /// Returns `true` if emulation happened.
109    /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
110    /// intrinsic handling.
111    pub fn eval_intrinsic(
112        &mut self,
113        instance: ty::Instance<'tcx>,
114        args: &[OpTy<'tcx, M::Provenance>],
115        dest: &MPlaceTy<'tcx, M::Provenance>,
116        ret: Option<mir::BasicBlock>,
117    ) -> InterpResult<'tcx, bool> {
118        let instance_args = instance.args;
119        let intrinsic_name = self.tcx.item_name(instance.def_id());
120
121        match intrinsic_name {
122            sym::caller_location => {
123                let span = self.find_closest_untracked_caller_location();
124                let val = self.tcx.span_as_caller_location(span);
125                let val =
126                    self.const_val_to_op(val, self.tcx.caller_location_ty(), Some(dest.layout))?;
127                self.copy_op(&val, dest)?;
128            }
129
130            sym::min_align_of_val | sym::size_of_val => {
131                // Avoid `deref_pointer` -- this is not a deref, the ptr does not have to be
132                // dereferenceable!
133                let place = self.ref_to_mplace(&self.read_immediate(&args[0])?)?;
134                let (size, align) = self
135                    .size_and_align_of_mplace(&place)?
136                    .ok_or_else(|| err_unsup_format!("`extern type` does not have known layout"))?;
137
138                let result = match intrinsic_name {
139                    sym::min_align_of_val => align.bytes(),
140                    sym::size_of_val => size.bytes(),
141                    _ => bug!(),
142                };
143
144                self.write_scalar(Scalar::from_target_usize(result, self), dest)?;
145            }
146
147            sym::pref_align_of
148            | sym::needs_drop
149            | sym::type_id
150            | sym::type_name
151            | sym::variant_count => {
152                let gid = GlobalId { instance, promoted: None };
153                let ty = match intrinsic_name {
154                    sym::pref_align_of | sym::variant_count => self.tcx.types.usize,
155                    sym::needs_drop => self.tcx.types.bool,
156                    sym::type_id => self.tcx.types.u128,
157                    sym::type_name => Ty::new_static_str(self.tcx.tcx),
158                    _ => bug!(),
159                };
160                let val = self
161                    .ctfe_query(|tcx| tcx.const_eval_global_id(self.typing_env, gid, tcx.span))?;
162                let val = self.const_val_to_op(val, ty, Some(dest.layout))?;
163                self.copy_op(&val, dest)?;
164            }
165
166            sym::fadd_algebraic
167            | sym::fsub_algebraic
168            | sym::fmul_algebraic
169            | sym::fdiv_algebraic
170            | sym::frem_algebraic => {
171                let a = self.read_immediate(&args[0])?;
172                let b = self.read_immediate(&args[1])?;
173
174                let op = match intrinsic_name {
175                    sym::fadd_algebraic => BinOp::Add,
176                    sym::fsub_algebraic => BinOp::Sub,
177                    sym::fmul_algebraic => BinOp::Mul,
178                    sym::fdiv_algebraic => BinOp::Div,
179                    sym::frem_algebraic => BinOp::Rem,
180
181                    _ => bug!(),
182                };
183
184                let res = self.binary_op(op, &a, &b)?;
185                // `binary_op` already called `generate_nan` if needed.
186                let res = M::apply_float_nondet(self, res)?;
187                self.write_immediate(*res, dest)?;
188            }
189
190            sym::ctpop
191            | sym::cttz
192            | sym::cttz_nonzero
193            | sym::ctlz
194            | sym::ctlz_nonzero
195            | sym::bswap
196            | sym::bitreverse => {
197                let ty = instance_args.type_at(0);
198                let layout = self.layout_of(ty)?;
199                let val = self.read_scalar(&args[0])?;
200
201                let out_val = self.numeric_intrinsic(intrinsic_name, val, layout, dest.layout)?;
202                self.write_scalar(out_val, dest)?;
203            }
204            sym::saturating_add | sym::saturating_sub => {
205                let l = self.read_immediate(&args[0])?;
206                let r = self.read_immediate(&args[1])?;
207                let val = self.saturating_arith(
208                    if intrinsic_name == sym::saturating_add { BinOp::Add } else { BinOp::Sub },
209                    &l,
210                    &r,
211                )?;
212                self.write_scalar(val, dest)?;
213            }
214            sym::discriminant_value => {
215                let place = self.deref_pointer(&args[0])?;
216                let variant = self.read_discriminant(&place)?;
217                let discr = self.discriminant_for_variant(place.layout.ty, variant)?;
218                self.write_immediate(*discr, dest)?;
219            }
220            sym::exact_div => {
221                let l = self.read_immediate(&args[0])?;
222                let r = self.read_immediate(&args[1])?;
223                self.exact_div(&l, &r, dest)?;
224            }
225            sym::rotate_left | sym::rotate_right => {
226                // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
227                // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
228                let layout_val = self.layout_of(instance_args.type_at(0))?;
229                let val = self.read_scalar(&args[0])?;
230                let val_bits = val.to_bits(layout_val.size)?; // sign is ignored here
231
232                let layout_raw_shift = self.layout_of(self.tcx.types.u32)?;
233                let raw_shift = self.read_scalar(&args[1])?;
234                let raw_shift_bits = raw_shift.to_bits(layout_raw_shift.size)?;
235
236                let width_bits = u128::from(layout_val.size.bits());
237                let shift_bits = raw_shift_bits % width_bits;
238                let inv_shift_bits = (width_bits - shift_bits) % width_bits;
239                let result_bits = if intrinsic_name == sym::rotate_left {
240                    (val_bits << shift_bits) | (val_bits >> inv_shift_bits)
241                } else {
242                    (val_bits >> shift_bits) | (val_bits << inv_shift_bits)
243                };
244                let truncated_bits = layout_val.size.truncate(result_bits);
245                let result = Scalar::from_uint(truncated_bits, layout_val.size);
246                self.write_scalar(result, dest)?;
247            }
248            sym::copy => {
249                self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
250            }
251            sym::write_bytes => {
252                self.write_bytes_intrinsic(&args[0], &args[1], &args[2], "write_bytes")?;
253            }
254            sym::compare_bytes => {
255                let result = self.compare_bytes_intrinsic(&args[0], &args[1], &args[2])?;
256                self.write_scalar(result, dest)?;
257            }
258            sym::arith_offset => {
259                let ptr = self.read_pointer(&args[0])?;
260                let offset_count = self.read_target_isize(&args[1])?;
261                let pointee_ty = instance_args.type_at(0);
262
263                let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
264                let offset_bytes = offset_count.wrapping_mul(pointee_size);
265                let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self);
266                self.write_pointer(offset_ptr, dest)?;
267            }
268            sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
269                let a = self.read_pointer(&args[0])?;
270                let b = self.read_pointer(&args[1])?;
271
272                let usize_layout = self.layout_of(self.tcx.types.usize)?;
273                let isize_layout = self.layout_of(self.tcx.types.isize)?;
274
275                // Get offsets for both that are at least relative to the same base.
276                // With `OFFSET_IS_ADDR` this is trivial; without it we need either
277                // two integers or two pointers into the same allocation.
278                let (a_offset, b_offset, is_addr) = if M::Provenance::OFFSET_IS_ADDR {
279                    (a.addr().bytes(), b.addr().bytes(), /*is_addr*/ true)
280                } else {
281                    match (self.ptr_try_get_alloc_id(a, 0), self.ptr_try_get_alloc_id(b, 0)) {
282                        (Err(a), Err(b)) => {
283                            // Neither pointer points to an allocation, so they are both absolute.
284                            (a, b, /*is_addr*/ true)
285                        }
286                        (Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _)))
287                            if a_alloc_id == b_alloc_id =>
288                        {
289                            // Found allocation for both, and it's the same.
290                            // Use these offsets for distance calculation.
291                            (a_offset.bytes(), b_offset.bytes(), /*is_addr*/ false)
292                        }
293                        _ => {
294                            // Not into the same allocation -- this is UB.
295                            throw_ub_custom!(
296                                fluent::const_eval_offset_from_different_allocations,
297                                name = intrinsic_name,
298                            );
299                        }
300                    }
301                };
302
303                // Compute distance: a - b.
304                let dist = {
305                    // Addresses are unsigned, so this is a `usize` computation. We have to do the
306                    // overflow check separately anyway.
307                    let (val, overflowed) = {
308                        let a_offset = ImmTy::from_uint(a_offset, usize_layout);
309                        let b_offset = ImmTy::from_uint(b_offset, usize_layout);
310                        self.binary_op(BinOp::SubWithOverflow, &a_offset, &b_offset)?
311                            .to_scalar_pair()
312                    };
313                    if overflowed.to_bool()? {
314                        // a < b
315                        if intrinsic_name == sym::ptr_offset_from_unsigned {
316                            throw_ub_custom!(
317                                fluent::const_eval_offset_from_unsigned_overflow,
318                                a_offset = a_offset,
319                                b_offset = b_offset,
320                                is_addr = is_addr,
321                            );
322                        }
323                        // The signed form of the intrinsic allows this. If we interpret the
324                        // difference as isize, we'll get the proper signed difference. If that
325                        // seems *positive* or equal to isize::MIN, they were more than isize::MAX apart.
326                        let dist = val.to_target_isize(self)?;
327                        if dist >= 0 || i128::from(dist) == self.pointer_size().signed_int_min() {
328                            throw_ub_custom!(
329                                fluent::const_eval_offset_from_underflow,
330                                name = intrinsic_name,
331                            );
332                        }
333                        dist
334                    } else {
335                        // b >= a
336                        let dist = val.to_target_isize(self)?;
337                        // If converting to isize produced a *negative* result, we had an overflow
338                        // because they were more than isize::MAX apart.
339                        if dist < 0 {
340                            throw_ub_custom!(
341                                fluent::const_eval_offset_from_overflow,
342                                name = intrinsic_name,
343                            );
344                        }
345                        dist
346                    }
347                };
348
349                // Check that the memory between them is dereferenceable at all, starting from the
350                // origin pointer: `dist` is `a - b`, so it is based on `b`.
351                self.check_ptr_access_signed(b, dist, CheckInAllocMsg::Dereferenceable)
352                    .map_err_kind(|_| {
353                        // This could mean they point to different allocations, or they point to the same allocation
354                        // but not the entire range between the pointers is in-bounds.
355                        if let Ok((a_alloc_id, ..)) = self.ptr_try_get_alloc_id(a, 0)
356                            && let Ok((b_alloc_id, ..)) = self.ptr_try_get_alloc_id(b, 0)
357                            && a_alloc_id == b_alloc_id
358                        {
359                            err_ub_custom!(
360                                fluent::const_eval_offset_from_out_of_bounds,
361                                name = intrinsic_name,
362                            )
363                        } else {
364                            err_ub_custom!(
365                                fluent::const_eval_offset_from_different_allocations,
366                                name = intrinsic_name,
367                            )
368                        }
369                    })?;
370                // Then check that this is also dereferenceable from `a`. This ensures that they are
371                // derived from the same allocation.
372                self.check_ptr_access_signed(
373                    a,
374                    dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large
375                    CheckInAllocMsg::Dereferenceable,
376                )
377                .map_err_kind(|_| {
378                    // Make the error more specific.
379                    err_ub_custom!(
380                        fluent::const_eval_offset_from_different_allocations,
381                        name = intrinsic_name,
382                    )
383                })?;
384
385                // Perform division by size to compute return value.
386                let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned {
387                    assert!(0 <= dist && dist <= self.target_isize_max());
388                    usize_layout
389                } else {
390                    assert!(self.target_isize_min() <= dist && dist <= self.target_isize_max());
391                    isize_layout
392                };
393                let pointee_layout = self.layout_of(instance_args.type_at(0))?;
394                // If ret_layout is unsigned, we checked that so is the distance, so we are good.
395                let val = ImmTy::from_int(dist, ret_layout);
396                let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout);
397                self.exact_div(&val, &size, dest)?;
398            }
399
400            sym::assert_inhabited
401            | sym::assert_zero_valid
402            | sym::assert_mem_uninitialized_valid => {
403                let ty = instance.args.type_at(0);
404                let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap();
405
406                let should_panic = !self
407                    .tcx
408                    .check_validity_requirement((requirement, self.typing_env.as_query_input(ty)))
409                    .map_err(|_| err_inval!(TooGeneric))?;
410
411                if should_panic {
412                    let layout = self.layout_of(ty)?;
413
414                    let msg = match requirement {
415                        // For *all* intrinsics we first check `is_uninhabited` to give a more specific
416                        // error message.
417                        _ if layout.is_uninhabited() => format!(
418                            "aborted execution: attempted to instantiate uninhabited type `{ty}`"
419                        ),
420                        ValidityRequirement::Inhabited => bug!("handled earlier"),
421                        ValidityRequirement::Zero => format!(
422                            "aborted execution: attempted to zero-initialize type `{ty}`, which is invalid"
423                        ),
424                        ValidityRequirement::UninitMitigated0x01Fill => format!(
425                            "aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid"
426                        ),
427                        ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
428                    };
429
430                    M::panic_nounwind(self, &msg)?;
431                    // Skip the `return_to_block` at the end (we panicked, we do not return).
432                    return interp_ok(true);
433                }
434            }
435            sym::simd_insert => {
436                let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
437                let elem = &args[2];
438                let (input, input_len) = self.project_to_simd(&args[0])?;
439                let (dest, dest_len) = self.project_to_simd(dest)?;
440                assert_eq!(input_len, dest_len, "Return vector length must match input length");
441                // Bounds are not checked by typeck so we have to do it ourselves.
442                if index >= input_len {
443                    throw_ub_format!(
444                        "`simd_insert` index {index} is out-of-bounds of vector with length {input_len}"
445                    );
446                }
447
448                for i in 0..dest_len {
449                    let place = self.project_index(&dest, i)?;
450                    let value =
451                        if i == index { elem.clone() } else { self.project_index(&input, i)? };
452                    self.copy_op(&value, &place)?;
453                }
454            }
455            sym::simd_extract => {
456                let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
457                let (input, input_len) = self.project_to_simd(&args[0])?;
458                // Bounds are not checked by typeck so we have to do it ourselves.
459                if index >= input_len {
460                    throw_ub_format!(
461                        "`simd_extract` index {index} is out-of-bounds of vector with length {input_len}"
462                    );
463                }
464                self.copy_op(&self.project_index(&input, index)?, dest)?;
465            }
466            sym::black_box => {
467                // These just return their argument
468                self.copy_op(&args[0], dest)?;
469            }
470            sym::raw_eq => {
471                let result = self.raw_eq_intrinsic(&args[0], &args[1])?;
472                self.write_scalar(result, dest)?;
473            }
474            sym::typed_swap_nonoverlapping => {
475                self.typed_swap_nonoverlapping_intrinsic(&args[0], &args[1])?;
476            }
477
478            sym::vtable_size => {
479                let ptr = self.read_pointer(&args[0])?;
480                // `None` because we don't know which trait to expect here; any vtable is okay.
481                let (size, _align) = self.get_vtable_size_and_align(ptr, None)?;
482                self.write_scalar(Scalar::from_target_usize(size.bytes(), self), dest)?;
483            }
484            sym::vtable_align => {
485                let ptr = self.read_pointer(&args[0])?;
486                // `None` because we don't know which trait to expect here; any vtable is okay.
487                let (_size, align) = self.get_vtable_size_and_align(ptr, None)?;
488                self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
489            }
490
491            sym::minnumf16 => self.float_min_intrinsic::<Half>(args, dest)?,
492            sym::minnumf32 => self.float_min_intrinsic::<Single>(args, dest)?,
493            sym::minnumf64 => self.float_min_intrinsic::<Double>(args, dest)?,
494            sym::minnumf128 => self.float_min_intrinsic::<Quad>(args, dest)?,
495
496            sym::maxnumf16 => self.float_max_intrinsic::<Half>(args, dest)?,
497            sym::maxnumf32 => self.float_max_intrinsic::<Single>(args, dest)?,
498            sym::maxnumf64 => self.float_max_intrinsic::<Double>(args, dest)?,
499            sym::maxnumf128 => self.float_max_intrinsic::<Quad>(args, dest)?,
500
501            sym::copysignf16 => self.float_copysign_intrinsic::<Half>(args, dest)?,
502            sym::copysignf32 => self.float_copysign_intrinsic::<Single>(args, dest)?,
503            sym::copysignf64 => self.float_copysign_intrinsic::<Double>(args, dest)?,
504            sym::copysignf128 => self.float_copysign_intrinsic::<Quad>(args, dest)?,
505
506            sym::fabsf16 => self.float_abs_intrinsic::<Half>(args, dest)?,
507            sym::fabsf32 => self.float_abs_intrinsic::<Single>(args, dest)?,
508            sym::fabsf64 => self.float_abs_intrinsic::<Double>(args, dest)?,
509            sym::fabsf128 => self.float_abs_intrinsic::<Quad>(args, dest)?,
510
511            // Unsupported intrinsic: skip the return_to_block below.
512            _ => return interp_ok(false),
513        }
514
515        trace!("{:?}", self.dump_place(&dest.clone().into()));
516        self.return_to_block(ret)?;
517        interp_ok(true)
518    }
519
520    pub(super) fn eval_nondiverging_intrinsic(
521        &mut self,
522        intrinsic: &NonDivergingIntrinsic<'tcx>,
523    ) -> InterpResult<'tcx> {
524        match intrinsic {
525            NonDivergingIntrinsic::Assume(op) => {
526                let op = self.eval_operand(op, None)?;
527                let cond = self.read_scalar(&op)?.to_bool()?;
528                if !cond {
529                    throw_ub_custom!(fluent::const_eval_assume_false);
530                }
531                interp_ok(())
532            }
533            NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping {
534                count,
535                src,
536                dst,
537            }) => {
538                let src = self.eval_operand(src, None)?;
539                let dst = self.eval_operand(dst, None)?;
540                let count = self.eval_operand(count, None)?;
541                self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true)
542            }
543        }
544    }
545
546    pub fn numeric_intrinsic(
547        &self,
548        name: Symbol,
549        val: Scalar<M::Provenance>,
550        layout: TyAndLayout<'tcx>,
551        ret_layout: TyAndLayout<'tcx>,
552    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
553        assert!(layout.ty.is_integral(), "invalid type for numeric intrinsic: {}", layout.ty);
554        let bits = val.to_bits(layout.size)?; // these operations all ignore the sign
555        let extra = 128 - u128::from(layout.size.bits());
556        let bits_out = match name {
557            sym::ctpop => u128::from(bits.count_ones()),
558            sym::ctlz_nonzero | sym::cttz_nonzero if bits == 0 => {
559                throw_ub_custom!(fluent::const_eval_call_nonzero_intrinsic, name = name,);
560            }
561            sym::ctlz | sym::ctlz_nonzero => u128::from(bits.leading_zeros()) - extra,
562            sym::cttz | sym::cttz_nonzero => u128::from((bits << extra).trailing_zeros()) - extra,
563            sym::bswap => {
564                assert_eq!(layout, ret_layout);
565                (bits << extra).swap_bytes()
566            }
567            sym::bitreverse => {
568                assert_eq!(layout, ret_layout);
569                (bits << extra).reverse_bits()
570            }
571            _ => bug!("not a numeric intrinsic: {}", name),
572        };
573        interp_ok(Scalar::from_uint(bits_out, ret_layout.size))
574    }
575
576    pub fn exact_div(
577        &mut self,
578        a: &ImmTy<'tcx, M::Provenance>,
579        b: &ImmTy<'tcx, M::Provenance>,
580        dest: &MPlaceTy<'tcx, M::Provenance>,
581    ) -> InterpResult<'tcx> {
582        assert_eq!(a.layout.ty, b.layout.ty);
583        assert_matches!(a.layout.ty.kind(), ty::Int(..) | ty::Uint(..));
584
585        // Performs an exact division, resulting in undefined behavior where
586        // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`.
587        // First, check x % y != 0 (or if that computation overflows).
588        let rem = self.binary_op(BinOp::Rem, a, b)?;
589        // sign does not matter for 0 test, so `to_bits` is fine
590        if rem.to_scalar().to_bits(a.layout.size)? != 0 {
591            throw_ub_custom!(
592                fluent::const_eval_exact_div_has_remainder,
593                a = format!("{a}"),
594                b = format!("{b}")
595            )
596        }
597        // `Rem` says this is all right, so we can let `Div` do its job.
598        let res = self.binary_op(BinOp::Div, a, b)?;
599        self.write_immediate(*res, dest)
600    }
601
602    pub fn saturating_arith(
603        &self,
604        mir_op: BinOp,
605        l: &ImmTy<'tcx, M::Provenance>,
606        r: &ImmTy<'tcx, M::Provenance>,
607    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
608        assert_eq!(l.layout.ty, r.layout.ty);
609        assert_matches!(l.layout.ty.kind(), ty::Int(..) | ty::Uint(..));
610        assert_matches!(mir_op, BinOp::Add | BinOp::Sub);
611
612        let (val, overflowed) =
613            self.binary_op(mir_op.wrapping_to_overflowing().unwrap(), l, r)?.to_scalar_pair();
614        interp_ok(if overflowed.to_bool()? {
615            let size = l.layout.size;
616            if l.layout.backend_repr.is_signed() {
617                // For signed ints the saturated value depends on the sign of the first
618                // term since the sign of the second term can be inferred from this and
619                // the fact that the operation has overflowed (if either is 0 no
620                // overflow can occur)
621                let first_term: i128 = l.to_scalar().to_int(l.layout.size)?;
622                if first_term >= 0 {
623                    // Negative overflow not possible since the positive first term
624                    // can only increase an (in range) negative term for addition
625                    // or corresponding negated positive term for subtraction.
626                    Scalar::from_int(size.signed_int_max(), size)
627                } else {
628                    // Positive overflow not possible for similar reason.
629                    Scalar::from_int(size.signed_int_min(), size)
630                }
631            } else {
632                // unsigned
633                if matches!(mir_op, BinOp::Add) {
634                    // max unsigned
635                    Scalar::from_uint(size.unsigned_int_max(), size)
636                } else {
637                    // underflow to 0
638                    Scalar::from_uint(0u128, size)
639                }
640            }
641        } else {
642            val
643        })
644    }
645
646    /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
647    /// allocation.
648    pub fn ptr_offset_inbounds(
649        &self,
650        ptr: Pointer<Option<M::Provenance>>,
651        offset_bytes: i64,
652    ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
653        // The offset must be in bounds starting from `ptr`.
654        self.check_ptr_access_signed(
655            ptr,
656            offset_bytes,
657            CheckInAllocMsg::InboundsPointerArithmetic,
658        )?;
659        // This also implies that there is no overflow, so we are done.
660        interp_ok(ptr.wrapping_signed_offset(offset_bytes, self))
661    }
662
663    /// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
664    pub(crate) fn copy_intrinsic(
665        &mut self,
666        src: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
667        dst: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
668        count: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
669        nonoverlapping: bool,
670    ) -> InterpResult<'tcx> {
671        let count = self.read_target_usize(count)?;
672        let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap())?;
673        let (size, align) = (layout.size, layout.align.abi);
674
675        let size = self.compute_size_in_bytes(size, count).ok_or_else(|| {
676            err_ub_custom!(
677                fluent::const_eval_size_overflow,
678                name = if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
679            )
680        })?;
681
682        let src = self.read_pointer(src)?;
683        let dst = self.read_pointer(dst)?;
684
685        self.check_ptr_align(src, align)?;
686        self.check_ptr_align(dst, align)?;
687
688        self.mem_copy(src, dst, size, nonoverlapping)
689    }
690
691    /// Does a *typed* swap of `*left` and `*right`.
692    fn typed_swap_nonoverlapping_intrinsic(
693        &mut self,
694        left: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
695        right: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
696    ) -> InterpResult<'tcx> {
697        let left = self.deref_pointer(left)?;
698        let right = self.deref_pointer(right)?;
699        assert_eq!(left.layout, right.layout);
700        assert!(left.layout.is_sized());
701        let kind = MemoryKind::Stack;
702        let temp = self.allocate(left.layout, kind)?;
703        self.copy_op(&left, &temp)?; // checks alignment of `left`
704
705        // We want to always enforce non-overlapping, even if this is a scalar type.
706        // Therefore we directly use the underlying `mem_copy` here.
707        self.mem_copy(right.ptr(), left.ptr(), left.layout.size, /*nonoverlapping*/ true)?;
708        // This means we also need to do the validation of the value that used to be in `right`
709        // ourselves. This value is now in `left.` The one that started out in `left` already got
710        // validated by the copy above.
711        if M::enforce_validity(self, left.layout) {
712            self.validate_operand(
713                &left.clone().into(),
714                M::enforce_validity_recursively(self, left.layout),
715                /*reset_provenance_and_padding*/ true,
716            )?;
717        }
718
719        self.copy_op(&temp, &right)?; // checks alignment of `right`
720
721        self.deallocate_ptr(temp.ptr(), None, kind)?;
722        interp_ok(())
723    }
724
725    pub fn write_bytes_intrinsic(
726        &mut self,
727        dst: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
728        byte: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
729        count: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
730        name: &'static str,
731    ) -> InterpResult<'tcx> {
732        let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap())?;
733
734        let dst = self.read_pointer(dst)?;
735        let byte = self.read_scalar(byte)?.to_u8()?;
736        let count = self.read_target_usize(count)?;
737
738        // `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
739        // but no actual allocation can be big enough for the difference to be noticeable.
740        let len = self
741            .compute_size_in_bytes(layout.size, count)
742            .ok_or_else(|| err_ub_custom!(fluent::const_eval_size_overflow, name = name))?;
743
744        let bytes = std::iter::repeat(byte).take(len.bytes_usize());
745        self.write_bytes_ptr(dst, bytes)
746    }
747
748    pub(crate) fn compare_bytes_intrinsic(
749        &mut self,
750        left: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
751        right: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
752        byte_count: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
753    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
754        let left = self.read_pointer(left)?;
755        let right = self.read_pointer(right)?;
756        let n = Size::from_bytes(self.read_target_usize(byte_count)?);
757
758        let left_bytes = self.read_bytes_ptr_strip_provenance(left, n)?;
759        let right_bytes = self.read_bytes_ptr_strip_provenance(right, n)?;
760
761        // `Ordering`'s discriminants are -1/0/+1, so casting does the right thing.
762        let result = Ord::cmp(left_bytes, right_bytes) as i32;
763        interp_ok(Scalar::from_i32(result))
764    }
765
766    pub(crate) fn raw_eq_intrinsic(
767        &mut self,
768        lhs: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
769        rhs: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
770    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
771        let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap())?;
772        assert!(layout.is_sized());
773
774        let get_bytes = |this: &InterpCx<'tcx, M>,
775                         op: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>|
776         -> InterpResult<'tcx, &[u8]> {
777            let ptr = this.read_pointer(op)?;
778            this.check_ptr_align(ptr, layout.align.abi)?;
779            let Some(alloc_ref) = self.get_ptr_alloc(ptr, layout.size)? else {
780                // zero-sized access
781                return interp_ok(&[]);
782            };
783            alloc_ref.get_bytes_strip_provenance()
784        };
785
786        let lhs_bytes = get_bytes(self, lhs)?;
787        let rhs_bytes = get_bytes(self, rhs)?;
788        interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
789    }
790
791    fn float_min_intrinsic<F>(
792        &mut self,
793        args: &[OpTy<'tcx, M::Provenance>],
794        dest: &MPlaceTy<'tcx, M::Provenance>,
795    ) -> InterpResult<'tcx, ()>
796    where
797        F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
798    {
799        let a: F = self.read_scalar(&args[0])?.to_float()?;
800        let b: F = self.read_scalar(&args[1])?.to_float()?;
801        let res = if a == b {
802            // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
803            // Let the machine decide which one to return.
804            M::equal_float_min_max(self, a, b)
805        } else {
806            self.adjust_nan(a.min(b), &[a, b])
807        };
808        self.write_scalar(res, dest)?;
809        interp_ok(())
810    }
811
812    fn float_max_intrinsic<F>(
813        &mut self,
814        args: &[OpTy<'tcx, M::Provenance>],
815        dest: &MPlaceTy<'tcx, M::Provenance>,
816    ) -> InterpResult<'tcx, ()>
817    where
818        F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
819    {
820        let a: F = self.read_scalar(&args[0])?.to_float()?;
821        let b: F = self.read_scalar(&args[1])?.to_float()?;
822        let res = if a == b {
823            // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
824            // Let the machine decide which one to return.
825            M::equal_float_min_max(self, a, b)
826        } else {
827            self.adjust_nan(a.max(b), &[a, b])
828        };
829        self.write_scalar(res, dest)?;
830        interp_ok(())
831    }
832
833    fn float_copysign_intrinsic<F>(
834        &mut self,
835        args: &[OpTy<'tcx, M::Provenance>],
836        dest: &MPlaceTy<'tcx, M::Provenance>,
837    ) -> InterpResult<'tcx, ()>
838    where
839        F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
840    {
841        let a: F = self.read_scalar(&args[0])?.to_float()?;
842        let b: F = self.read_scalar(&args[1])?.to_float()?;
843        // bitwise, no NaN adjustments
844        self.write_scalar(a.copy_sign(b), dest)?;
845        interp_ok(())
846    }
847
848    fn float_abs_intrinsic<F>(
849        &mut self,
850        args: &[OpTy<'tcx, M::Provenance>],
851        dest: &MPlaceTy<'tcx, M::Provenance>,
852    ) -> InterpResult<'tcx, ()>
853    where
854        F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
855    {
856        let x: F = self.read_scalar(&args[0])?.to_float()?;
857        // bitwise, no NaN adjustments
858        self.write_scalar(x.abs(), dest)?;
859        interp_ok(())
860    }
861}