<commit_before>//! Intrinsics and other functions that the miri engine executes without
//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
//! and miri.

use std::convert::TryFrom;

use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
    self,
    interpret::{ConstValue, GlobalId, InterpResult, Scalar},
    BinOp,
};
use rustc_middle::ty;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_span::symbol::{sym, Symbol};
use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size};

use super::{
    util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
};

mod caller_location;
mod type_name;

fn numeric_intrinsic<Tag>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Tag> {
    let size = match kind {
        Primitive::Int(integer, _) => integer.size(),
        _ => bug!("invalid `{}` argument: {:?}", name, bits),
    };
    let extra = 128 - u128::from(size.bits());
    let bits_out = match name {
        sym::ctpop => u128::from(bits.count_ones()),
        sym::ctlz => u128::from(bits.leading_zeros()) - extra,
        sym::cttz => u128::from((bits << extra).trailing_zeros()) - extra,
        sym::bswap => (bits << extra).swap_bytes(),
        sym::bitreverse => (bits << extra).reverse_bits(),
        _ => bug!("not a numeric intrinsic: {}", name),
    };
    Scalar::from_uint(bits_out, size)
}

/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated
/// inside an `InterpCx` and instead have their value computed directly from rustc internal info.
crate fn eval_nullary_intrinsic<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    def_id: DefId,
    substs: SubstsRef<'tcx>,
) -> InterpResult<'tcx, ConstValue<'tcx>> {
    let tp_ty = substs.type_at(0);
    let name = tcx.item_name(def_id);
    Ok(match name {
        sym::type_name => {
            ensure_monomorphic_enough(tcx, tp_ty)?;
            let alloc = type_name::alloc_type_name(tcx, tp_ty);
            ConstValue::Slice { data: alloc, start: 0, end: alloc.len() }
        }
        sym::needs_drop => ConstValue::from_bool(tp_ty.needs_drop(tcx, param_env)),
        sym::min_align_of | sym::pref_align_of => {
            let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?;
            let n = match name {
                sym::pref_align_of => layout.align.pref.bytes(),
                sym::min_align_of => layout.align.abi.bytes(),
                _ => bug!(),
            };
            ConstValue::from_machine_usize(n, &tcx)
        }
        sym::type_id => {
            ensure_monomorphic_enough(tcx, tp_ty)?;
            ConstValue::from_u64(tcx.type_id_hash(tp_ty))
        }
        sym::variant_count => match tp_ty.kind() {
            ty::Adt(ref adt, _) => ConstValue::from_machine_usize(adt.variants.len() as u64, &tcx),
            ty::Projection(_)
            | ty::Opaque(_, _)
            | ty::Param(_)
            | ty::Bound(_, _)
            | ty::Placeholder(_)
            | ty::Infer(_) => throw_inval!(TooGeneric),
            ty::Bool
            | ty::Char
            | ty::Int(_)
            | ty::Uint(_)
            | ty::Float(_)
            | ty::Foreign(_)
            | ty::Str
            | ty::Array(_, _)
            | ty::Slice(_)
            | ty::RawPtr(_)
            | ty::Ref(_, _, _)
            | ty::FnDef(_, _)
            | ty::FnPtr(_)
            | ty::Dynamic(_, _)
            | ty::Closure(_, _)
            | ty::Generator(_, _, _)
            | ty::GeneratorWitness(_)
            | ty::Never
            | ty::Tuple(_)
            | ty::Error(_) => ConstValue::from_machine_usize(0u64, &tcx),
        },
        other => bug!("`{}` is not a zero arg intrinsic", other),
    })
}

impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    /// Returns `true` if emulation happened.
    /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
    /// intrinsic handling.
    pub fn emulate_intrinsic(
        &mut self,
        instance: ty::Instance<'tcx>,
        args: &[OpTy<'tcx, M::PointerTag>],
        ret: Option<(&PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
    ) -> InterpResult<'tcx, bool> {
        let substs = instance.substs;
        let intrinsic_name = self.tcx.item_name(instance.def_id());

        // First handle intrinsics without return place.
        let (dest, ret) = match ret {
            None => match intrinsic_name {
                sym::transmute => throw_ub_format!("transmuting to uninhabited type"),
                sym::abort => M::abort(self, "the program aborted execution".to_owned())?,
                // Unsupported diverging intrinsic.
                _ => return Ok(false),
            },
            Some(p) => p,
        };

        // Keep the patterns in this match ordered the same as the list in
        // `src/librustc_middle/ty/constness.rs`
        match intrinsic_name {
            sym::caller_location => {
                let span = self.find_closest_untracked_caller_location();
                let location = self.alloc_caller_location_for_span(span);
                self.write_scalar(location.ptr, dest)?;
            }

            sym::min_align_of_val | sym::size_of_val => {
                // Avoid `deref_operand` -- this is not a deref, the ptr does not have to be
                // dereferencable!
                let place = self.ref_to_mplace(&self.read_immediate(&args[0])?)?;
                let (size, align) = self
                    .size_and_align_of_mplace(&place)?
                    .ok_or_else(|| err_unsup_format!("`extern type` does not have known layout"))?;

                let result = match intrinsic_name {
                    sym::min_align_of_val => align.bytes(),
                    sym::size_of_val => size.bytes(),
                    _ => bug!(),
                };

                self.write_scalar(Scalar::from_machine_usize(result, self), dest)?;
            }

            sym::min_align_of
            | sym::pref_align_of
            | sym::needs_drop
            | sym::type_id
            | sym::type_name
            | sym::variant_count => {
                let gid = GlobalId { instance, promoted: None };
                let ty = match intrinsic_name {
                    sym::min_align_of | sym::pref_align_of | sym::variant_count => {
                        self.tcx.types.usize
                    }
                    sym::needs_drop => self.tcx.types.bool,
                    sym::type_id => self.tcx.types.u64,
                    sym::type_name => self.tcx.mk_static_str(),
                    _ => bug!("already checked for nullary intrinsics"),
                };
                let val =
                    self.tcx.const_eval_global_id(self.param_env, gid, Some(self.tcx.span))?;
                let const_ = ty::Const { val: ty::ConstKind::Value(val), ty };
                let val = self.const_to_op(&const_, None)?;
                self.copy_op(&val, dest)?;
            }

            sym::ctpop
            | sym::cttz
            | sym::cttz_nonzero
            | sym::ctlz
            | sym::ctlz_nonzero
            | sym::bswap
            | sym::bitreverse => {
                let ty = substs.type_at(0);
                let layout_of = self.layout_of(ty)?;
                let val = self.read_scalar(&args[0])?.check_init()?;
                let bits = self.force_bits(val, layout_of.size)?;
                let kind = match layout_of.abi {
                    Abi::Scalar(ref scalar) => scalar.value,
                    _ => span_bug!(
                        self.cur_span(),
                        "{} called on invalid type {:?}",
                        intrinsic_name,
                        ty
                    ),
                };
                let (nonzero, intrinsic_name) = match intrinsic_name {
                    sym::cttz_nonzero => (true, sym::cttz),
                    sym::ctlz_nonzero => (true, sym::ctlz),<commit_msg>Don't build a ty::Const just to take it apart again<commit_after>//! Intrinsics and other functions that the miri engine executes without
//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
//! and miri.

use std::convert::TryFrom;

use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
    self,
    interpret::{ConstValue, GlobalId, InterpResult, Scalar},
    BinOp,
};
use rustc_middle::ty;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_span::symbol::{sym, Symbol};
use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size};

use super::{
    util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
};

mod caller_location;
mod type_name;

fn numeric_intrinsic<Tag>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Tag> {
    let size = match kind {
        Primitive::Int(integer, _) => integer.size(),
        _ => bug!("invalid `{}` argument: {:?}", name, bits),
    };
    let extra = 128 - u128::from(size.bits());
    let bits_out = match name {
        sym::ctpop => u128::from(bits.count_ones()),
        sym::ctlz => u128::from(bits.leading_zeros()) - extra,
        sym::cttz => u128::from((bits << extra).trailing_zeros()) - extra,
        sym::bswap => (bits << extra).swap_bytes(),
        sym::bitreverse => (bits << extra).reverse_bits(),
        _ => bug!("not a numeric intrinsic: {}", name),
    };
    Scalar::from_uint(bits_out, size)
}

/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated
/// inside an `InterpCx` and instead have their value computed directly from rustc internal info.
crate fn eval_nullary_intrinsic<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    def_id: DefId,
    substs: SubstsRef<'tcx>,
) -> InterpResult<'tcx, ConstValue<'tcx>> {
    let tp_ty = substs.type_at(0);
    let name = tcx.item_name(def_id);
    Ok(match name {
        sym::type_name => {
            ensure_monomorphic_enough(tcx, tp_ty)?;
            let alloc = type_name::alloc_type_name(tcx, tp_ty);
            ConstValue::Slice { data: alloc, start: 0, end: alloc.len() }
        }
        sym::needs_drop => ConstValue::from_bool(tp_ty.needs_drop(tcx, param_env)),
        sym::min_align_of | sym::pref_align_of => {
            let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?;
            let n = match name {
                sym::pref_align_of => layout.align.pref.bytes(),
                sym::min_align_of => layout.align.abi.bytes(),
                _ => bug!(),
            };
            ConstValue::from_machine_usize(n, &tcx)
        }
        sym::type_id => {
            ensure_monomorphic_enough(tcx, tp_ty)?;
            ConstValue::from_u64(tcx.type_id_hash(tp_ty))
        }
        sym::variant_count => match tp_ty.kind() {
            ty::Adt(ref adt, _) => ConstValue::from_machine_usize(adt.variants.len() as u64, &tcx),
            ty::Projection(_)
            | ty::Opaque(_, _)
            | ty::Param(_)
            | ty::Bound(_, _)
            | ty::Placeholder(_)
            | ty::Infer(_) => throw_inval!(TooGeneric),
            ty::Bool
            | ty::Char
            | ty::Int(_)
            | ty::Uint(_)
            | ty::Float(_)
            | ty::Foreign(_)
            | ty::Str
            | ty::Array(_, _)
            | ty::Slice(_)
            | ty::RawPtr(_)
            | ty::Ref(_, _, _)
            | ty::FnDef(_, _)
            | ty::FnPtr(_)
            | ty::Dynamic(_, _)
            | ty::Closure(_, _)
            | ty::Generator(_, _, _)
            | ty::GeneratorWitness(_)
            | ty::Never
            | ty::Tuple(_)
            | ty::Error(_) => ConstValue::from_machine_usize(0u64, &tcx),
        },
        other => bug!("`{}` is not a zero arg intrinsic", other),
    })
}

impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    /// Returns `true` if emulation happened.
    /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
    /// intrinsic handling.
    pub fn emulate_intrinsic(
        &mut self,
        instance: ty::Instance<'tcx>,
        args: &[OpTy<'tcx, M::PointerTag>],
        ret: Option<(&PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
    ) -> InterpResult<'tcx, bool> {
        let substs = instance.substs;
        let intrinsic_name = self.tcx.item_name(instance.def_id());

        // First handle intrinsics without return place.
        let (dest, ret) = match ret {
            None => match intrinsic_name {
                sym::transmute => throw_ub_format!("transmuting to uninhabited type"),
                sym::abort => M::abort(self, "the program aborted execution".to_owned())?,
                // Unsupported diverging intrinsic.
                _ => return Ok(false),
            },
            Some(p) => p,
        };

        // Keep the patterns in this match ordered the same as the list in
        // `src/librustc_middle/ty/constness.rs`
        match intrinsic_name {
            sym::caller_location => {
                let span = self.find_closest_untracked_caller_location();
                let location = self.alloc_caller_location_for_span(span);
                self.write_scalar(location.ptr, dest)?;
            }

            sym::min_align_of_val | sym::size_of_val => {
                // Avoid `deref_operand` -- this is not a deref, the ptr does not have to be
                // dereferencable!
                let place = self.ref_to_mplace(&self.read_immediate(&args[0])?)?;
                let (size, align) = self
                    .size_and_align_of_mplace(&place)?
                    .ok_or_else(|| err_unsup_format!("`extern type` does not have known layout"))?;

                let result = match intrinsic_name {
                    sym::min_align_of_val => align.bytes(),
                    sym::size_of_val => size.bytes(),
                    _ => bug!(),
                };

                self.write_scalar(Scalar::from_machine_usize(result, self), dest)?;
            }

            sym::min_align_of
            | sym::pref_align_of
            | sym::needs_drop
            | sym::type_id
            | sym::type_name
            | sym::variant_count => {
                let gid = GlobalId { instance, promoted: None };
                let ty = match intrinsic_name {
                    sym::min_align_of | sym::pref_align_of | sym::variant_count => {
                        self.tcx.types.usize
                    }
                    sym::needs_drop => self.tcx.types.bool,
                    sym::type_id => self.tcx.types.u64,
                    sym::type_name => self.tcx.mk_static_str(),
                    _ => bug!("already checked for nullary intrinsics"),
                };
                let val =
                    self.tcx.const_eval_global_id(self.param_env, gid, Some(self.tcx.span))?;
                let val = self.const_val_to_op(val, ty, None)?;
                self.copy_op(&val, dest)?;
            }

            sym::ctpop
            | sym::cttz
            | sym::cttz_nonzero
            | sym::ctlz
            | sym::ctlz_nonzero
            | sym::bswap
            | sym::bitreverse => {
                let ty = substs.type_at(0);
                let layout_of = self.layout_of(ty)?;
                let val = self.read_scalar(&args[0])?.check_init()?;
                let bits = self.force_bits(val, layout_of.size)?;
                let kind = match layout_of.abi {
                    Abi::Scalar(ref scalar) => scalar.value,
                    _ => span_bug!(
                        self.cur_span(),
                        "{} called on invalid type {:?}",
                        intrinsic_name,
                        ty
                    ),
                };
                let (nonzero, intrinsic_name) = match intrinsic_name {
                    sym::cttz_nonzero => (true, sym::cttz),
                    sym::ctlz_nonzero => (true, sym::ctlz),
{'fraction_non_alphanumeric': 0.13742672014810245, 'fraction_numerical': 0.003949398333847578, 'mean_word_length': 1.8867117919486998, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 123, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1553113', 'n_tokens_mistral': 4998, 'n_tokens_neox': 4913, 'n_words': 1001}