//! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation
//! is not available.
//!
use std::cmp::{self, Ordering};

use hir_def::{attrs::AttrFlags, signatures::FunctionSignature};
use rustc_abi::ExternAbi;
use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, SliceLike, Ty as _};
use stdx::never;

use crate::{
    display::DisplayTarget,
    drop::{DropGlue, has_drop_glue},
    mir::eval::{
        Address, AdtId, Arc, Evaluator, FunctionId, GenericArgs, HasModule, HirDisplay, Interval,
        IntervalAndTy, IntervalOrOwned, IsSigned, ItemContainerId, Layout, Locals, Lookup,
        MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, from_bytes, not_supported, pad16,
    },
    next_solver::Region,
};

mod simd;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EvalLangItem {
    BeginPanic,
    SliceLen,
    DropInPlace,
}

impl<'a, 'db> Evaluator<'a, 'db> {
    pub(super) fn detect_and_exec_special_function(
        &mut self,
        def: FunctionId,
        args: &[IntervalAndTy<'db>],
        generic_args: GenericArgs<'db>,
        locals: &Locals<'a, 'db>,
        destination: Interval,
        span: MirSpan,
    ) -> Result<'db, bool> {
        if self.not_special_fn_cache.borrow().contains(&def) {
            return Ok(false);
        }

        let function_data = FunctionSignature::of(self.db, def);
        let attrs = AttrFlags::query(self.db, def.into());
        let is_intrinsic = FunctionSignature::is_intrinsic(self.db, def);

        if is_intrinsic {
            return self.exec_intrinsic(
                function_data.name.as_str(),
                args,
                generic_args,
                destination,
                locals,
                span,
                !function_data.has_body()
                    || attrs.contains(AttrFlags::RUSTC_INTRINSIC_MUST_BE_OVERRIDDEN),
            );
        }
        let is_extern_c = match def.lookup(self.db).container {
            hir_def::ItemContainerId::ExternBlockId(block) => {
                matches!(block.abi(self.db), ExternAbi::C { .. })
            }
            _ => false,
        };
        if is_extern_c {
            return self
                .exec_extern_c(
                    function_data.name.as_str(),
                    args,
                    generic_args,
                    destination,
                    locals,
                    span,
                )
                .map(|()| true);
        }

        if attrs.intersects(
            AttrFlags::RUSTC_ALLOCATOR
                | AttrFlags::RUSTC_DEALLOCATOR
                | AttrFlags::RUSTC_REALLOCATOR
                | AttrFlags::RUSTC_ALLOCATOR_ZEROED,
        ) {
            self.exec_alloc_fn(attrs, args, destination)?;
            return Ok(true);
        }
        if let Some(it) = self.detect_lang_function(def) {
            let result = self.exec_lang_item(it, generic_args, args, locals, span)?;
            destination.write_from_bytes(self, &result)?;
            return Ok(true);
        }
        if let ItemContainerId::TraitId(t) = def.lookup(self.db).container
            && Some(t) == self.lang_items().Clone
        {
            let [self_ty] = generic_args.as_slice() else {
                not_supported!("wrong generic arg count for clone");
            };
            let Some(self_ty) = self_ty.ty() else {
                not_supported!("wrong generic arg kind for clone");
            };
            // Clone has special impls for tuples and function pointers
            if matches!(self_ty.kind(), TyKind::FnPtr(..) | TyKind::Tuple(..) | TyKind::Closure(..))
            {
                self.exec_clone(def, args, self_ty, locals, destination, span)?;
                return Ok(true);
            }
            // Return early to prevent caching clone as non special fn.
            return Ok(false);
        }
        self.not_special_fn_cache.borrow_mut().insert(def);
        Ok(false)
    }

    pub(super) fn detect_and_redirect_special_function(
        &mut self,
        def: FunctionId,
    ) -> Result<'db, Option<FunctionId>> {
        // `PanicFmt` is redirected to `ConstPanicFmt`
        if Some(def) == self.lang_items().PanicFmt {
            let Some(const_panic_fmt) = self.lang_items().ConstPanicFmt else {
                not_supported!("const_panic_fmt lang item not found or not a function");
            };
            return Ok(Some(const_panic_fmt));
        }
        Ok(None)
    }

    /// Clone has special impls for tuples and function pointers
    fn exec_clone(
        &mut self,
        def: FunctionId,
        args: &[IntervalAndTy<'db>],
        self_ty: Ty<'db>,
        locals: &Locals<'a, 'db>,
        destination: Interval,
        span: MirSpan,
    ) -> Result<'db, ()> {
        match self_ty.kind() {
            TyKind::FnPtr(..) => {
                let [arg] = args else {
                    not_supported!("wrong arg count for clone");
                };
                let addr = Address::from_bytes(arg.get(self)?)?;
                return destination
                    .write_from_interval(self, Interval { addr, size: destination.size });
            }
            TyKind::Closure(_, closure_args) => self.exec_clone(
                def,
                args,
                closure_args.as_closure().tupled_upvars_ty(),
                locals,
                destination,
                span,
            )?,
            TyKind::Tuple(subst) => {
                let [arg] = args else {
                    not_supported!("wrong arg count for clone");
                };
                let addr = Address::from_bytes(arg.get(self)?)?;
                let layout = self.layout(self_ty)?;
                self.exec_clone_for_fields(
                    subst.iter(),
                    layout,
                    addr,
                    def,
                    locals,
                    destination,
                    span,
                )?;
            }
            _ => {
                self.exec_fn_with_args(
                    def,
                    args,
                    GenericArgs::new_from_slice(&[self_ty.into()]),
                    locals,
                    destination,
                    None,
                    span,
                )?;
            }
        }
        Ok(())
    }

    fn exec_clone_for_fields(
        &mut self,
        ty_iter: impl Iterator<Item = Ty<'db>>,
        layout: Arc<Layout>,
        addr: Address,
        def: FunctionId,
        locals: &Locals<'a, 'db>,
        destination: Interval,
        span: MirSpan,
    ) -> Result<'db, ()> {
        for (i, ty) in ty_iter.enumerate() {
            let size = self.layout(ty)?.size.bytes_usize();
            let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
            let arg = IntervalAndTy {
                interval: Interval { addr: tmp, size: self.ptr_size() },
                ty: Ty::new_ref(
                    self.interner(),
                    Region::error(self.interner()),
                    ty,
                    Mutability::Not,
                ),
            };
            let offset = layout.fields.offset(i).bytes_usize();
            self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
            self.exec_clone(
                def,
                &[arg],
                ty,
                locals,
                destination.slice(offset..offset + size),
                span,
            )?;
        }
        Ok(())
    }

    fn exec_alloc_fn(
        &mut self,
        alloc_fn: AttrFlags,
        args: &[IntervalAndTy<'db>],
        destination: Interval,
    ) -> Result<'db, ()> {
        match alloc_fn {
            _ if alloc_fn
                .intersects(AttrFlags::RUSTC_ALLOCATOR_ZEROED | AttrFlags::RUSTC_ALLOCATOR) =>
            {
                let [size, align] = args else {
                    return Err(MirEvalError::InternalError(
                        "rustc_allocator args are not provided".into(),
                    ));
                };
                let size = from_bytes!(usize, size.get(self)?);
                let align = from_bytes!(usize, align.get(self)?);
                let result = self.heap_allocate(size, align)?;
                destination.write_from_bytes(self, &result.to_bytes())?;
            }
            _ if alloc_fn.contains(AttrFlags::RUSTC_DEALLOCATOR) => { /* no-op for now */ }
            _ if alloc_fn.contains(AttrFlags::RUSTC_REALLOCATOR) => {
                let [ptr, old_size, align, new_size] = args else {
                    return Err(MirEvalError::InternalError(
                        "rustc_allocator args are not provided".into(),
                    ));
                };
                let old_size = from_bytes!(usize, old_size.get(self)?);
                let new_size = from_bytes!(usize, new_size.get(self)?);
                if old_size >= new_size {
                    destination.write_from_interval(self, ptr.interval)?;
                } else {
                    let ptr = Address::from_bytes(ptr.get(self)?)?;
                    let align = from_bytes!(usize, align.get(self)?);
                    let result = self.heap_allocate(new_size, align)?;
                    Interval { addr: result, size: old_size }
                        .write_from_interval(self, Interval { addr: ptr, size: old_size })?;
                    destination.write_from_bytes(self, &result.to_bytes())?;
                }
            }
            _ => not_supported!("unknown alloc function"),
        }
        Ok(())
    }

    fn detect_lang_function(&self, def: FunctionId) -> Option<EvalLangItem> {
        use EvalLangItem::*;
        let lang_items = self.lang_items();
        let attrs = AttrFlags::query(self.db, def.into());

        if attrs.contains(AttrFlags::RUSTC_CONST_PANIC_STR) {
            // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
            return Some(BeginPanic);
        }

        // We want to execute these functions with special logic
        // `PanicFmt` is not detected here as it's redirected later.
        if let Some((_, candidate)) = [
            (lang_items.BeginPanic, BeginPanic),
            (lang_items.SliceLen, SliceLen),
            (lang_items.DropInPlace, DropInPlace),
        ]
        .iter()
        .find(|&(candidate, _)| candidate == Some(def))
        {
            return Some(candidate);
        }

        None
    }

    fn exec_lang_item(
        &mut self,
        it: EvalLangItem,
        generic_args: GenericArgs<'db>,
        args: &[IntervalAndTy<'db>],
        locals: &Locals<'a, 'db>,
        span: MirSpan,
    ) -> Result<'db, Vec<u8>> {
        use EvalLangItem::*;
        let mut args = args.iter();
        match it {
            BeginPanic => {
                let mut arg = args
                    .next()
                    .ok_or(MirEvalError::InternalError(
                        "argument of BeginPanic is not provided".into(),
                    ))?
                    .clone();
                while let TyKind::Ref(_, ty, _) = arg.ty.kind() {
                    if ty.is_str() {
                        let (pointee, metadata) = arg.interval.get(self)?.split_at(self.ptr_size());
                        let len = from_bytes!(usize, metadata);

                        return {
                            Err(MirEvalError::Panic(
                                std::str::from_utf8(
                                    self.read_memory(Address::from_bytes(pointee)?, len)?,
                                )
                                .unwrap()
                                .to_owned(),
                            ))
                        };
                    }
                    let size = self.size_of_sized(ty, locals, "begin panic arg")?;
                    let pointee = arg.interval.get(self)?;
                    arg = IntervalAndTy {
                        interval: Interval::new(Address::from_bytes(pointee)?, size),
                        ty,
                    };
                }
                Err(MirEvalError::Panic(format!("unknown-panic-payload: {:?}", arg.ty.kind())))
            }
            SliceLen => {
                let arg = args.next().ok_or(MirEvalError::InternalError(
                    "argument of <[T]>::len() is not provided".into(),
                ))?;
                let arg = arg.get(self)?;
                let ptr_size = arg.len() / 2;
                Ok(arg[ptr_size..].into())
            }
            DropInPlace => {
                let ty = generic_args.as_slice().first().and_then(|it| it.ty()).ok_or(
                    MirEvalError::InternalError(
                        "generic argument of drop_in_place is not provided".into(),
                    ),
                )?;
                let arg = args.next().ok_or(MirEvalError::InternalError(
                    "argument of drop_in_place is not provided".into(),
                ))?;
                let arg = arg.interval.get(self)?.to_owned();
                self.run_drop_glue_deep(
                    ty,
                    locals,
                    Address::from_bytes(&arg[0..self.ptr_size()])?,
                    &arg[self.ptr_size()..],
                    span,
                )?;
                Ok(vec![])
            }
        }
    }

    fn exec_syscall(
        &mut self,
        id: i64,
        args: &[IntervalAndTy<'db>],
        destination: Interval,
        _locals: &Locals<'a, 'db>,
        _span: MirSpan,
    ) -> Result<'db, ()> {
        match id {
            318 => {
                // SYS_getrandom
                let [buf, len, _flags] = args else {
                    return Err(MirEvalError::InternalError(
                        "SYS_getrandom args are not provided".into(),
                    ));
                };
                let addr = Address::from_bytes(buf.get(self)?)?;
                let size = from_bytes!(usize, len.get(self)?);
                for i in 0..size {
                    let rand_byte = self.random_state.rand_u64() as u8;
                    self.write_memory(addr.offset(i), &[rand_byte])?;
                }
                destination.write_from_interval(self, len.interval)
            }
            _ => {
                not_supported!("Unknown syscall id {id:?}")
            }
        }
    }

    fn exec_extern_c(
        &mut self,
        as_str: &str,
        args: &[IntervalAndTy<'db>],
        _generic_args: GenericArgs<'db>,
        destination: Interval,
        locals: &Locals<'a, 'db>,
        span: MirSpan,
    ) -> Result<'db, ()> {
        match as_str {
            "memcmp" => {
                let [ptr1, ptr2, size] = args else {
                    return Err(MirEvalError::InternalError("memcmp args are not provided".into()));
                };
                let addr1 = Address::from_bytes(ptr1.get(self)?)?;
                let addr2 = Address::from_bytes(ptr2.get(self)?)?;
                let size = from_bytes!(usize, size.get(self)?);
                let slice1 = self.read_memory(addr1, size)?;
                let slice2 = self.read_memory(addr2, size)?;
                let r: i128 = match slice1.cmp(slice2) {
                    cmp::Ordering::Less => -1,
                    cmp::Ordering::Equal => 0,
                    cmp::Ordering::Greater => 1,
                };
                destination.write_from_bytes(self, &r.to_le_bytes()[..destination.size])
            }
            "write" => {
                let [fd, ptr, len] = args else {
                    return Err(MirEvalError::InternalError(
                        "libc::write args are not provided".into(),
                    ));
                };
                let fd = u128::from_le_bytes(pad16(fd.get(self)?, IsSigned::No));
                let interval = Interval {
                    addr: Address::from_bytes(ptr.get(self)?)?,
                    size: from_bytes!(usize, len.get(self)?),
                };
                match fd {
                    1 => {
                        self.write_to_stdout(interval)?;
                    }
                    2 => {
                        self.write_to_stderr(interval)?;
                    }
                    _ => not_supported!("write to arbitrary file descriptor"),
                }
                destination.write_from_interval(self, len.interval)?;
                Ok(())
            }
            "pthread_key_create" => {
                let key = self.thread_local_storage.create_key();
                let Some(arg0) = args.first() else {
                    return Err(MirEvalError::InternalError(
                        "pthread_key_create arg0 is not provided".into(),
                    ));
                };
                let arg0_addr = Address::from_bytes(arg0.get(self)?)?;
                let key_ty = if let Some((ty, ..)) = arg0.ty.as_reference_or_ptr() {
                    ty
                } else {
                    return Err(MirEvalError::InternalError(
                        "pthread_key_create arg0 is not a pointer".into(),
                    ));
                };
                let arg0_interval = Interval::new(
                    arg0_addr,
                    self.size_of_sized(key_ty, locals, "pthread_key_create key arg")?,
                );
                arg0_interval.write_from_bytes(self, &key.to_le_bytes()[0..arg0_interval.size])?;
                // return 0 as success
                destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
                Ok(())
            }
            "pthread_getspecific" => {
                let Some(arg0) = args.first() else {
                    return Err(MirEvalError::InternalError(
                        "pthread_getspecific arg0 is not provided".into(),
                    ));
                };
                let key = from_bytes!(usize, &pad16(arg0.get(self)?, IsSigned::No)[0..8]);
                let value = self.thread_local_storage.get_key(key)?;
                destination.write_from_bytes(self, &value.to_le_bytes()[0..destination.size])?;
                Ok(())
            }
            "pthread_setspecific" => {
                let Some(arg0) = args.first() else {
                    return Err(MirEvalError::InternalError(
                        "pthread_setspecific arg0 is not provided".into(),
                    ));
                };
                let key = from_bytes!(usize, &pad16(arg0.get(self)?, IsSigned::No)[0..8]);
                let Some(arg1) = args.get(1) else {
                    return Err(MirEvalError::InternalError(
                        "pthread_setspecific arg1 is not provided".into(),
                    ));
                };
                let value = from_bytes!(u128, pad16(arg1.get(self)?, IsSigned::No));
                self.thread_local_storage.set_key(key, value)?;
                // return 0 as success
                destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
                Ok(())
            }
            "pthread_key_delete" => {
                // we ignore this currently
                // return 0 as success
                destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
                Ok(())
            }
            "syscall" => {
                let Some((id, rest)) = args.split_first() else {
                    return Err(MirEvalError::InternalError("syscall arg1 is not provided".into()));
                };
                let id = from_bytes!(i64, id.get(self)?);
                self.exec_syscall(id, rest, destination, locals, span)
            }
            "sched_getaffinity" => {
                let [_pid, _set_size, set] = args else {
                    return Err(MirEvalError::InternalError(
                        "sched_getaffinity args are not provided".into(),
                    ));
                };
                let set = Address::from_bytes(set.get(self)?)?;
                // Only enable core 0 (we are single threaded anyway), which is bitset 0x0000001
                self.write_memory(set, &[1])?;
                // return 0 as success
                self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
                Ok(())
            }
            "getenv" => {
                let [name] = args else {
                    return Err(MirEvalError::InternalError("getenv args are not provided".into()));
                };
                let mut name_buf = vec![];
                let name = {
                    let mut index = Address::from_bytes(name.get(self)?)?;
                    loop {
                        let byte = self.read_memory(index, 1)?[0];
                        index = index.offset(1);
                        if byte == 0 {
                            break;
                        }
                        name_buf.push(byte);
                    }
                    String::from_utf8_lossy(&name_buf)
                };
                let value = self.crate_id.env(self.db).get(&name);
                match value {
                    None => {
                        // Write null as fail
                        self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
                    }
                    Some(mut value) => {
                        value.push('\0');
                        let addr = self.heap_allocate(value.len(), 1)?;
                        self.write_memory(addr, value.as_bytes())?;
                        self.write_memory(destination.addr, &addr.to_bytes())?;
                    }
                }
                Ok(())
            }
            _ => not_supported!("unknown external function {as_str}"),
        }
    }

    fn exec_intrinsic(
        &mut self,
        name: &str,
        args: &[IntervalAndTy<'db>],
        generic_args: GenericArgs<'db>,
        destination: Interval,
        locals: &Locals<'a, 'db>,
        span: MirSpan,
        needs_override: bool,
    ) -> Result<'db, bool> {
        if let Some(name) = name.strip_prefix("atomic_") {
            return self
                .exec_atomic_intrinsic(name, args, generic_args, destination, locals, span)
                .map(|()| true);
        }
        if let Some(name) = name.strip_prefix("simd_") {
            return self
                .exec_simd_intrinsic(name, args, generic_args, destination, locals, span)
                .map(|()| true);
        }
        // FIXME(#17451): Add `f16` and `f128` intrinsics.
        if let Some(name) = name.strip_suffix("f64") {
            let result = match name {
                "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"
                | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => {
                    let [arg] = args else {
                        return Err(MirEvalError::InternalError(
                            "f64 intrinsic signature doesn't match fn (f64) -> f64".into(),
                        ));
                    };
                    let arg = from_bytes!(f64, arg.get(self)?);
                    match name {
                        "sqrt" => arg.sqrt(),
                        "sin" => arg.sin(),
                        "cos" => arg.cos(),
                        "exp" => arg.exp(),
                        "exp2" => arg.exp2(),
                        "log" => arg.ln(),
                        "log10" => arg.log10(),
                        "log2" => arg.log2(),
                        "fabs" => arg.abs(),
                        "floor" => arg.floor(),
                        "ceil" => arg.ceil(),
                        "trunc" => arg.trunc(),
                        // FIXME: these rounds should be different, but only `.round()` is stable now.
                        "rint" => arg.round(),
                        "nearbyint" => arg.round(),
                        "round" => arg.round(),
                        "roundeven" => arg.round(),
                        _ => unreachable!(),
                    }
                }
                "pow" | "minnum" | "maxnum" | "copysign" => {
                    let [arg1, arg2] = args else {
                        return Err(MirEvalError::InternalError(
                            "f64 intrinsic signature doesn't match fn (f64, f64) -> f64".into(),
                        ));
                    };
                    let arg1 = from_bytes!(f64, arg1.get(self)?);
                    let arg2 = from_bytes!(f64, arg2.get(self)?);
                    match name {
                        "pow" => arg1.powf(arg2),
                        "minnum" => arg1.min(arg2),
                        "maxnum" => arg1.max(arg2),
                        "copysign" => arg1.copysign(arg2),
                        _ => unreachable!(),
                    }
                }
                "powi" => {
                    let [arg1, arg2] = args else {
                        return Err(MirEvalError::InternalError(
                            "powif64 signature doesn't match fn (f64, i32) -> f64".into(),
                        ));
                    };
                    let arg1 = from_bytes!(f64, arg1.get(self)?);
                    let arg2 = from_bytes!(i32, arg2.get(self)?);
                    arg1.powi(arg2)
                }
                "fma" => {
                    let [arg1, arg2, arg3] = args else {
                        return Err(MirEvalError::InternalError(
                            "fmaf64 signature doesn't match fn (f64, f64, f64) -> f64".into(),
                        ));
                    };
                    let arg1 = from_bytes!(f64, arg1.get(self)?);
                    let arg2 = from_bytes!(f64, arg2.get(self)?);
                    let arg3 = from_bytes!(f64, arg3.get(self)?);
                    arg1.mul_add(arg2, arg3)
                }
                _ => not_supported!("unknown f64 intrinsic {name}"),
            };
            return destination.write_from_bytes(self, &result.to_le_bytes()).map(|()| true);
        }
        if let Some(name) = name.strip_suffix("f32") {
            let result = match name {
                "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"
                | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => {
                    let [arg] = args else {
                        return Err(MirEvalError::InternalError(
                            "f32 intrinsic signature doesn't match fn (f32) -> f32".into(),
                        ));
                    };
                    let arg = from_bytes!(f32, arg.get(self)?);
                    match name {
                        "sqrt" => arg.sqrt(),
                        "sin" => arg.sin(),
                        "cos" => arg.cos(),
                        "exp" => arg.exp(),
                        "exp2" => arg.exp2(),
                        "log" => arg.ln(),
                        "log10" => arg.log10(),
                        "log2" => arg.log2(),
                        "fabs" => arg.abs(),
                        "floor" => arg.floor(),
                        "ceil" => arg.ceil(),
                        "trunc" => arg.trunc(),
                        // FIXME: these rounds should be different, but only `.round()` is stable now.
                        "rint" => arg.round(),
                        "nearbyint" => arg.round(),
                        "round" => arg.round(),
                        "roundeven" => arg.round(),
                        _ => unreachable!(),
                    }
                }
                "pow" | "minnum" | "maxnum" | "copysign" => {
                    let [arg1, arg2] = args else {
                        return Err(MirEvalError::InternalError(
                            "f32 intrinsic signature doesn't match fn (f32, f32) -> f32".into(),
                        ));
                    };
                    let arg1 = from_bytes!(f32, arg1.get(self)?);
                    let arg2 = from_bytes!(f32, arg2.get(self)?);
                    match name {
                        "pow" => arg1.powf(arg2),
                        "minnum" => arg1.min(arg2),
                        "maxnum" => arg1.max(arg2),
                        "copysign" => arg1.copysign(arg2),
                        _ => unreachable!(),
                    }
                }
                "powi" => {
                    let [arg1, arg2] = args else {
                        return Err(MirEvalError::InternalError(
                            "powif32 signature doesn't match fn (f32, i32) -> f32".into(),
                        ));
                    };
                    let arg1 = from_bytes!(f32, arg1.get(self)?);
                    let arg2 = from_bytes!(i32, arg2.get(self)?);
                    arg1.powi(arg2)
                }
                "fma" => {
                    let [arg1, arg2, arg3] = args else {
                        return Err(MirEvalError::InternalError(
                            "fmaf32 signature doesn't match fn (f32, f32, f32) -> f32".into(),
                        ));
                    };
                    let arg1 = from_bytes!(f32, arg1.get(self)?);
                    let arg2 = from_bytes!(f32, arg2.get(self)?);
                    let arg3 = from_bytes!(f32, arg3.get(self)?);
                    arg1.mul_add(arg2, arg3)
                }
                _ => not_supported!("unknown f32 intrinsic {name}"),
            };
            return destination.write_from_bytes(self, &result.to_le_bytes()).map(|()| true);
        }
        match name {
            "size_of" => {
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "size_of generic arg is not provided".into(),
                    ));
                };
                let size = self.size_of_sized(ty, locals, "size_of arg")?;
                destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
            }
            "align_of" => {
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "align_of generic arg is not provided".into(),
                    ));
                };
                let align = self.layout(ty)?.align.bytes();
                destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
            }
            "size_of_val" => {
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "size_of_val generic arg is not provided".into(),
                    ));
                };
                let [arg] = args else {
                    return Err(MirEvalError::InternalError(
                        "size_of_val args are not provided".into(),
                    ));
                };
                if let Some((size, _)) = self.size_align_of(ty, locals)? {
                    destination.write_from_bytes(self, &size.to_le_bytes())
                } else {
                    let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2);
                    let (size, _) = self.size_align_of_unsized(ty, metadata, locals)?;
                    destination.write_from_bytes(self, &size.to_le_bytes())
                }
            }
            "align_of_val" => {
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "align_of_val generic arg is not provided".into(),
                    ));
                };
                let [arg] = args else {
                    return Err(MirEvalError::InternalError(
                        "align_of_val args are not provided".into(),
                    ));
                };
                if let Some((_, align)) = self.size_align_of(ty, locals)? {
                    destination.write_from_bytes(self, &align.to_le_bytes())
                } else {
                    let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2);
                    let (_, align) = self.size_align_of_unsized(ty, metadata, locals)?;
                    destination.write_from_bytes(self, &align.to_le_bytes())
                }
            }
            "type_name" => {
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "type_name generic arg is not provided".into(),
                    ));
                };
                let ty_name = match ty.display_source_code(
                    self.db,
                    locals.body.owner.module(self.db),
                    true,
                ) {
                    Ok(ty_name) => ty_name,
                    // Fallback to human readable display in case of `Err`. Ideally we want to use `display_source_code` to
                    // render full paths.
                    Err(_) => {
                        let krate = locals.body.owner.krate(self.db);
                        ty.display(self.db, DisplayTarget::from_crate(self.db, krate)).to_string()
                    }
                };
                let len = ty_name.len();
                let addr = self.heap_allocate(len, 1)?;
                self.write_memory(addr, ty_name.as_bytes())?;
                destination.slice(0..self.ptr_size()).write_from_bytes(self, &addr.to_bytes())?;
                destination
                    .slice(self.ptr_size()..2 * self.ptr_size())
                    .write_from_bytes(self, &len.to_le_bytes())
            }
            "needs_drop" => {
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "size_of generic arg is not provided".into(),
                    ));
                };
                let result = match has_drop_glue(&self.infcx, ty, self.param_env.param_env) {
                    DropGlue::HasDropGlue => true,
                    DropGlue::None => false,
                    DropGlue::DependOnParams => {
                        never!("should be fully monomorphized now");
                        true
                    }
                };
                destination.write_from_bytes(self, &[u8::from(result)])
            }
            "ptr_guaranteed_cmp" => {
                // FIXME: this is wrong for const eval, it should return 2 in some
                // cases.
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "ptr_guaranteed_cmp args are not provided".into(),
                    ));
                };
                let ans = lhs.get(self)? == rhs.get(self)?;
                destination.write_from_bytes(self, &[u8::from(ans)])
            }
            "saturating_add" | "saturating_sub" => {
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "saturating_add args are not provided".into(),
                    ));
                };
                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let ans = match name {
                    "saturating_add" => lhs.saturating_add(rhs),
                    "saturating_sub" => lhs.saturating_sub(rhs),
                    _ => unreachable!(),
                };
                let bits = destination.size * 8;
                // FIXME: signed
                let is_signed = false;
                let mx: u128 = if is_signed { (1 << (bits - 1)) - 1 } else { (1 << bits) - 1 };
                // FIXME: signed
                let mn: u128 = 0;
                let ans = cmp::min(mx, cmp::max(mn, ans));
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "wrapping_add" | "unchecked_add" => {
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "wrapping_add args are not provided".into(),
                    ));
                };
                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let ans = lhs.wrapping_add(rhs);
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "ptr_offset_from_unsigned" | "ptr_offset_from" => {
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "wrapping_sub args are not provided".into(),
                    ));
                };
                let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let ans = lhs.wrapping_sub(rhs);
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "ptr_offset_from generic arg is not provided".into(),
                    ));
                };
                let size = self.size_of_sized(ty, locals, "ptr_offset_from arg")? as i128;
                let ans = ans / size;
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "wrapping_sub" | "unchecked_sub" => {
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "wrapping_sub args are not provided".into(),
                    ));
                };
                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let ans = lhs.wrapping_sub(rhs);
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "wrapping_mul" | "unchecked_mul" => {
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "wrapping_mul args are not provided".into(),
                    ));
                };
                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let ans = lhs.wrapping_mul(rhs);
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "wrapping_shl" | "unchecked_shl" => {
                // FIXME: signed
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "unchecked_shl args are not provided".into(),
                    ));
                };
                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let ans = lhs.wrapping_shl(rhs as u32);
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "wrapping_shr" | "unchecked_shr" => {
                // FIXME: signed
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "unchecked_shr args are not provided".into(),
                    ));
                };
                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let ans = lhs.wrapping_shr(rhs as u32);
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "unchecked_rem" => {
                // FIXME: signed
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "unchecked_rem args are not provided".into(),
                    ));
                };
                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let ans = lhs.checked_rem(rhs).ok_or_else(|| {
                    MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned())
                })?;
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "unchecked_div" | "exact_div" => {
                // FIXME: signed
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "unchecked_div args are not provided".into(),
                    ));
                };
                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let ans = lhs.checked_div(rhs).ok_or_else(|| {
                    MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned())
                })?;
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => {
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "const_eval_select args are not provided".into(),
                    ));
                };
                let result_ty = Ty::new_tup_from_iter(
                    self.interner(),
                    [lhs.ty, Ty::new_bool(self.interner())].into_iter(),
                );
                let op_size = self.size_of_sized(lhs.ty, locals, "operand of add_with_overflow")?;
                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, IsSigned::No));
                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, IsSigned::No));
                let (ans, u128overflow) = match name {
                    "add_with_overflow" => lhs.overflowing_add(rhs),
                    "sub_with_overflow" => lhs.overflowing_sub(rhs),
                    "mul_with_overflow" => lhs.overflowing_mul(rhs),
                    _ => unreachable!(),
                };
                let is_overflow = u128overflow
                    || ans.to_le_bytes()[op_size..].iter().any(|&it| it != 0 && it != 255);
                let is_overflow = vec![u8::from(is_overflow)];
                let layout = self.layout(result_ty)?;
                let result = self.construct_with_layout(
                    layout.size.bytes_usize(),
                    &layout,
                    None,
                    [ans.to_le_bytes()[0..op_size].to_vec(), is_overflow]
                        .into_iter()
                        .map(IntervalOrOwned::Owned),
                )?;
                destination.write_from_bytes(self, &result)
            }
            "copy" | "copy_nonoverlapping" => {
                let [src, dst, offset] = args else {
                    return Err(MirEvalError::InternalError(
                        "copy_nonoverlapping args are not provided".into(),
                    ));
                };
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "copy_nonoverlapping generic arg is not provided".into(),
                    ));
                };
                let src = Address::from_bytes(src.get(self)?)?;
                let dst = Address::from_bytes(dst.get(self)?)?;
                let offset = from_bytes!(usize, offset.get(self)?);
                let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?;
                let size = offset * size;
                let src = Interval { addr: src, size };
                let dst = Interval { addr: dst, size };
                dst.write_from_interval(self, src)
            }
            "slice_get_unchecked" => {
                let [slice_ptr, index] = args else {
                    return Err(MirEvalError::InternalError(
                        "slice_get_unchecked args are not provided".into(),
                    ));
                };
                let Some(ty) = generic_args.as_slice().get(2).and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "slice_get_unchecked item type is not provided".into(),
                    ));
                };
                let slice_ptr = slice_ptr.get(self)?;
                let ptr_size = self.ptr_size();
                let Some(data) = slice_ptr.get(..ptr_size) else {
                    return Err(MirEvalError::InternalError(
                        "slice_get_unchecked slice pointer is too small".into(),
                    ));
                };
                let Some(len) = slice_ptr.get(ptr_size..2 * ptr_size) else {
                    return Err(MirEvalError::InternalError(
                        "slice_get_unchecked slice metadata is missing".into(),
                    ));
                };
                let slice_ptr = Address::from_bytes(data)?;
                let len = from_bytes!(usize, len);
                let index = from_bytes!(usize, index.get(self)?);
                if index >= len {
                    return Err(MirEvalError::UndefinedBehavior(format!(
                        "slice_get_unchecked index {index} is out of bounds for slice of length {len}"
                    )));
                }
                let size = self.size_of_sized(ty, locals, "slice_get_unchecked item type")?;
                let offset = index* size;
                let addr = slice_ptr.to_usize() + offset;
                let addr = Address::from_usize(addr);
                destination.write_from_bytes(self, &addr.to_bytes()[..destination.size])
            }
            "offset" | "arith_offset" => {
                let [ptr, offset] = args else {
                    return Err(MirEvalError::InternalError("offset args are not provided".into()));
                };
                let ty = if name == "offset" {
                    let Some(ty0) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                        return Err(MirEvalError::InternalError(
                            "offset generic arg is not provided".into(),
                        ));
                    };
                    let Some(ty1) = generic_args.as_slice().get(1).and_then(|it| it.ty()) else {
                        return Err(MirEvalError::InternalError(
                            "offset generic arg is not provided".into(),
                        ));
                    };
                    if !matches!(
                        ty1.kind(),
                        TyKind::Int(rustc_type_ir::IntTy::Isize)
                            | TyKind::Uint(rustc_type_ir::UintTy::Usize)
                    ) {
                        return Err(MirEvalError::InternalError(
                            "offset generic arg is not usize or isize".into(),
                        ));
                    }
                    match ty0.kind() {
                        TyKind::RawPtr(ty, _) => ty,
                        _ => {
                            return Err(MirEvalError::InternalError(
                                "offset generic arg is not a raw pointer".into(),
                            ));
                        }
                    }
                } else {
                    let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                        return Err(MirEvalError::InternalError(
                            "arith_offset generic arg is not provided".into(),
                        ));
                    };
                    ty
                };
                let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, IsSigned::No));
                let offset = u128::from_le_bytes(pad16(offset.get(self)?, IsSigned::No));
                let size = self.size_of_sized(ty, locals, "offset ptr type")? as u128;
                let ans = ptr + offset * size;
                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
            }
            "assert_inhabited"
            | "assert_zero_valid"
            | "assert_uninit_valid"
            | "assert_mem_uninitialized_valid" => {
                // FIXME: We should actually implement these checks
                Ok(())
            }
            "forget" => {
                // FIXME
                Ok(())
            }
            "transmute" | "transmute_unchecked" => {
                let [arg] = args else {
                    return Err(MirEvalError::InternalError(
                        "transmute arg is not provided".into(),
                    ));
                };
                destination.write_from_interval(self, arg.interval)
            }
            "ctpop" => {
                let [arg] = args else {
                    return Err(MirEvalError::InternalError("ctpop arg is not provided".into()));
                };
                let result = u128::from_le_bytes(pad16(arg.get(self)?, IsSigned::No)).count_ones();
                destination
                    .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
            }
            "ctlz" | "ctlz_nonzero" => {
                let [arg] = args else {
                    return Err(MirEvalError::InternalError("ctlz arg is not provided".into()));
                };
                let result =
                    u128::from_le_bytes(pad16(arg.get(self)?, IsSigned::No)).leading_zeros() as usize;
                let result = result - (128 - arg.interval.size * 8);
                destination
                    .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
            }
            "cttz" | "cttz_nonzero" => {
                let [arg] = args else {
                    return Err(MirEvalError::InternalError("cttz arg is not provided".into()));
                };
                let arg: &[u8] = arg.get(self)?;
                let bit_count = arg.len() as u32 * 8;
                let result = u128::from_le_bytes(pad16(arg, IsSigned::No)).trailing_zeros().min(bit_count);
                destination
                    .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
            }
            "rotate_left" => {
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "rotate_left args are not provided".into(),
                    ));
                };
                let lhs = &lhs.get(self)?[0..destination.size];
                let rhs = rhs.get(self)?[0] as u32;
                match destination.size {
                    1 => {
                        let r = from_bytes!(u8, lhs).rotate_left(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    2 => {
                        let r = from_bytes!(u16, lhs).rotate_left(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    4 => {
                        let r = from_bytes!(u32, lhs).rotate_left(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    8 => {
                        let r = from_bytes!(u64, lhs).rotate_left(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    16 => {
                        let r = from_bytes!(u128, lhs).rotate_left(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    s => not_supported!("destination with size {s} for rotate_left"),
                }
            }
            "rotate_right" => {
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "rotate_right args are not provided".into(),
                    ));
                };
                let lhs = &lhs.get(self)?[0..destination.size];
                let rhs = rhs.get(self)?[0] as u32;
                match destination.size {
                    1 => {
                        let r = from_bytes!(u8, lhs).rotate_right(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    2 => {
                        let r = from_bytes!(u16, lhs).rotate_right(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    4 => {
                        let r = from_bytes!(u32, lhs).rotate_right(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    8 => {
                        let r = from_bytes!(u64, lhs).rotate_right(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    16 => {
                        let r = from_bytes!(u128, lhs).rotate_right(rhs);
                        destination.write_from_bytes(self, &r.to_le_bytes())
                    }
                    s => not_supported!("destination with size {s} for rotate_right"),
                }
            }
            "discriminant_value" => {
                let [arg] = args else {
                    return Err(MirEvalError::InternalError(
                        "discriminant_value arg is not provided".into(),
                    ));
                };
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "discriminant_value generic arg is not provided".into(),
                    ));
                };
                let addr = Address::from_bytes(arg.get(self)?)?;
                let size = self.size_of_sized(ty, locals, "discriminant_value ptr type")?;
                let interval = Interval { addr, size };
                let r = self.compute_discriminant(ty, interval.get(self)?)?;
                destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])
            }
            "const_eval_select" => {
                let [tuple, const_fn, _] = args else {
                    return Err(MirEvalError::InternalError(
                        "const_eval_select args are not provided".into(),
                    ));
                };
                let mut args = vec![const_fn.clone()];
                let TyKind::Tuple(fields) = tuple.ty.kind() else {
                    return Err(MirEvalError::InternalError(
                        "const_eval_select arg[0] is not a tuple".into(),
                    ));
                };
                let layout = self.layout(tuple.ty)?;
                for (i, field) in fields.iter().enumerate() {
                    let offset = layout.fields.offset(i).bytes_usize();
                    let addr = tuple.interval.addr.offset(offset);
                    args.push(IntervalAndTy::new(addr, field, self, locals)?);
                }
                if let Some(def) = self.lang_items().FnOnce_call_once {
                    self.exec_fn_trait(
                        def,
                        &args,
                        // FIXME: wrong for manual impls of `FnOnce`
                        GenericArgs::empty(self.interner()),
                        locals,
                        destination,
                        None,
                        span,
                    )?;
                    return Ok(true);
                }
                not_supported!("FnOnce was not available for executing const_eval_select");
            }
            "read_via_copy" | "volatile_load" => {
                let [arg] = args else {
                    return Err(MirEvalError::InternalError(
                        "read_via_copy args are not provided".into(),
                    ));
                };
                let addr = Address::from_bytes(arg.interval.get(self)?)?;
                destination.write_from_interval(self, Interval { addr, size: destination.size })
            }
            "write_via_move" => {
                let [ptr, val] = args else {
                    return Err(MirEvalError::InternalError(
                        "write_via_move args are not provided".into(),
                    ));
                };
                let dst = Address::from_bytes(ptr.get(self)?)?;
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "write_via_copy generic arg is not provided".into(),
                    ));
                };
                let size = self.size_of_sized(ty, locals, "write_via_move ptr type")?;
                Interval { addr: dst, size }.write_from_interval(self, val.interval)?;
                Ok(())
            }
            "write_bytes" => {
                let [dst, val, count] = args else {
                    return Err(MirEvalError::InternalError(
                        "write_bytes args are not provided".into(),
                    ));
                };
                let count = from_bytes!(usize, count.get(self)?);
                let val = from_bytes!(u8, val.get(self)?);
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "write_bytes generic arg is not provided".into(),
                    ));
                };
                let dst = Address::from_bytes(dst.get(self)?)?;
                let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?;
                let size = count * size;
                self.write_memory_using_ref(dst, size)?.fill(val);
                Ok(())
            }
            "ptr_metadata" => {
                let [ptr] = args else {
                    return Err(MirEvalError::InternalError(
                        "ptr_metadata args are not provided".into(),
                    ));
                };
                let arg = ptr.interval.get(self)?.to_owned();
                let metadata = &arg[self.ptr_size()..];
                destination.write_from_bytes(self, metadata)?;
                Ok(())
            }
            "three_way_compare" => {
                let [lhs, rhs] = args else {
                    return Err(MirEvalError::InternalError(
                        "three_way_compare args are not provided".into(),
                    ));
                };
                let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
                    return Err(MirEvalError::InternalError(
                        "three_way_compare generic arg is not provided".into(),
                    ));
                };
                let signed = match ty.kind() {
                    TyKind::Int(_) => true,
                    TyKind::Uint(_) => false,
                    _ => {
                        return Err(MirEvalError::InternalError(
                            "three_way_compare expects an integral type".into(),
                        ));
                    }
                };
                let rhs = rhs.get(self)?;
                let lhs = lhs.get(self)?;
                let mut result = Ordering::Equal;
                for (l, r) in lhs.iter().zip(rhs).rev() {
                    let it = l.cmp(r);
                    if it != Ordering::Equal {
                        result = it;
                        break;
                    }
                }
                if signed
                    && let Some((&l, &r)) = lhs.iter().zip(rhs).next_back()
                    && l != r
                {
                    result = (l as i8).cmp(&(r as i8));
                }
                if let Some(e) = self.lang_items().Ordering {
                    let ty = self.db.ty(e.into()).skip_binder();
                    let r = self.compute_discriminant(ty, &[result as i8 as u8])?;
                    destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])?;
                    Ok(())
                } else {
                    Err(MirEvalError::InternalError("Ordering enum not found".into()))
                }
            }
            "aggregate_raw_ptr" => {
                let [data, meta] = args else {
                    return Err(MirEvalError::InternalError(
                        "aggregate_raw_ptr args are not provided".into(),
                    ));
                };
                destination.write_from_interval(self, data.interval)?;
                Interval {
                    addr: destination.addr.offset(data.interval.size),
                    size: destination.size - data.interval.size,
                }
                .write_from_interval(self, meta.interval)?;
                Ok(())
            }
            "fabs" => {
                let [arg] = args else {
                    return Err(MirEvalError::InternalError(
                        "fabs intrinsic signature doesn't match fn (T) -> T".into(),
                    ));
                };
                let mut bytes = arg.get(self)?.to_vec();
                if let Some(sign_byte) = bytes.last_mut() {
                    *sign_byte &= 0x7f;
                }
                destination.write_from_bytes(self, &bytes)
            }
            "unreachable" => {
                return Err(MirEvalError::UndefinedBehavior(
                    "`unreachable` intrinsic executed".to_owned(),
                ));
            }
            "const_allocate" => {
                let [size, align] = args else {
                    return Err(MirEvalError::InternalError(
                        "const_allocate args are not provided".into(),
                    ));
                };
                let size = from_bytes!(usize, size.get(self)?);
                let align = from_bytes!(usize, align.get(self)?);
                let result = self.heap_allocate(size, align)?;
                destination.write_from_bytes(self, &result.to_bytes())
            }
            "const_deallocate" => Ok(()),
            "caller_location" => {
                let Some(location_adt) = self.lang_items().PanicLocation else {
                    not_supported!("`caller_location` requires the `panic_location` lang item");
                };
                let location_ty = self.db.ty(location_adt.into()).skip_binder();
                let TyKind::Adt(_, subst) = location_ty.kind() else {
                    return Err(MirEvalError::InternalError(
                        "`panic_location` lang item is not an ADT".into(),
                    ));
                };
                let layout = self.layout(location_ty)?;
                let (file, line, col) = self.caller_location_fields(locals.body.owner, span);
                let file_len = file.len();
                let file_addr = self.heap_allocate(file_len + 1, 1)?;
                self.write_memory(file_addr, file.as_bytes())?;
                let ptr_size = self.ptr_size();
                let field_types = self.db.field_types(location_adt.into());
                let mut line_col = [line, col].into_iter();
                let mut fields = Vec::with_capacity(field_types.iter().count());
                for (_, field) in field_types.iter() {
                    let field_ty = field.ty().instantiate(self.interner(), subst).skip_norm_wip();
                    let bytes =
                        if matches!(field_ty.kind(), TyKind::Uint(rustc_type_ir::UintTy::U32)) {
                            line_col.next().unwrap_or(0).to_le_bytes().to_vec()
                        } else {
                            let size =
                                self.size_of_sized(field_ty, locals, "caller_location field")?;
                            if size == ptr_size * 2 {
                                // The string slice pointing at the file name: (data pointer, length).
                                let mut bytes = file_addr.to_bytes()[..ptr_size].to_vec();
                                bytes.extend_from_slice(&file_len.to_le_bytes()[..ptr_size]);
                                bytes
                            } else {
                                vec![0; size]
                            }
                        };
                    fields.push(IntervalOrOwned::Owned(bytes));
                }
                let location = self.construct_with_layout(
                    layout.size.bytes_usize(),
                    &layout,
                    None,
                    fields.into_iter(),
                )?;
                let location_addr =
                    self.heap_allocate(layout.size.bytes_usize(), layout.align.bytes() as usize)?;
                self.write_memory(location_addr, &location)?;
                destination.write_from_bytes(self, &location_addr.to_bytes()[..ptr_size])
            }
            "box_new" => {
                let ty = generic_args.type_at(0);
                let Some((size, align)) = self.size_align_of(ty, locals)? else {
                    not_supported!("unsized box initialization");
                };
                let addr = self.heap_allocate(size, align)?;
                self.copy_from_interval(addr, args[0].interval)?;
                destination.write_from_bytes(self, &addr.to_bytes()[..self.ptr_size()])
            }
            _ if needs_override => not_supported!("intrinsic {name} is not implemented"),
            _ => return Ok(false),
        }
        .map(|()| true)
    }

    fn size_align_of_unsized(
        &mut self,
        ty: Ty<'db>,
        metadata: Interval,
        locals: &Locals<'a, 'db>,
    ) -> Result<'db, (usize, usize)> {
        Ok(match ty.kind() {
            TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1),
            TyKind::Slice(inner) => {
                let len = from_bytes!(usize, metadata.get(self)?);
                let (size, align) = self.size_align_of_sized(inner, locals, "slice inner type")?;
                (size * len, align)
            }
            TyKind::Dynamic(..) => self.size_align_of_sized(
                self.vtable_map.ty_of_bytes(metadata.get(self)?)?,
                locals,
                "dyn concrete type",
            )?,
            TyKind::Adt(adt_def, subst) => {
                let id = adt_def.def_id();
                let layout = self.layout_adt(id, subst)?;
                let id = match id {
                    AdtId::StructId(s) => s,
                    _ => not_supported!("unsized enum or union"),
                };
                let field_types = self.db.field_types(id.into());
                let last_field_ty = field_types
                    .iter()
                    .next_back()
                    .unwrap()
                    .1
                    .ty()
                    .instantiate(self.interner(), subst)
                    .skip_norm_wip();
                let sized_part_size =
                    layout.fields.offset(field_types.iter().count() - 1).bytes_usize();
                let sized_part_align = layout.align.bytes() as usize;
                let (unsized_part_size, unsized_part_align) =
                    self.size_align_of_unsized(last_field_ty, metadata, locals)?;
                let align = sized_part_align.max(unsized_part_align) as isize;
                let size = (sized_part_size + unsized_part_size) as isize;
                // Must add any necessary padding to `size`
                // (to make it a multiple of `align`) before returning it.
                //
                // Namely, the returned size should be, in C notation:
                //
                //   `size + ((size & (align-1)) ? align : 0)`
                //
                // emulated via the semi-standard fast bit trick:
                //
                //   `(size + (align-1)) & -align`
                let size = (size + (align - 1)) & (-align);
                (size as usize, align as usize)
            }
            _ => not_supported!("unsized type other than str, slice, struct and dyn"),
        })
    }

    fn exec_atomic_intrinsic(
        &mut self,
        name: &str,
        args: &[IntervalAndTy<'db>],
        generic_args: GenericArgs<'db>,
        destination: Interval,
        locals: &Locals<'a, 'db>,
        _span: MirSpan,
    ) -> Result<'db, ()> {
        // We are a single threaded runtime with no UB checking and no optimization, so
        // we can implement atomic intrinsics as normal functions.

        if name.starts_with("singlethreadfence_") || name.starts_with("fence_") {
            return Ok(());
        }

        // The rest of atomic intrinsics have exactly one generic arg

        let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
            return Err(MirEvalError::InternalError(
                "atomic intrinsic generic arg is not provided".into(),
            ));
        };
        let Some(arg0) = args.first() else {
            return Err(MirEvalError::InternalError(
                "atomic intrinsic arg0 is not provided".into(),
            ));
        };
        let arg0_addr = Address::from_bytes(arg0.get(self)?)?;
        let arg0_interval =
            Interval::new(arg0_addr, self.size_of_sized(ty, locals, "atomic intrinsic type arg")?);
        if name.starts_with("load_") {
            return destination.write_from_interval(self, arg0_interval);
        }
        let Some(arg1) = args.get(1) else {
            return Err(MirEvalError::InternalError(
                "atomic intrinsic arg1 is not provided".into(),
            ));
        };
        if name.starts_with("store_") {
            return arg0_interval.write_from_interval(self, arg1.interval);
        }
        if name.starts_with("xchg_") {
            destination.write_from_interval(self, arg0_interval)?;
            return arg0_interval.write_from_interval(self, arg1.interval);
        }
        if name.starts_with("xadd_") {
            destination.write_from_interval(self, arg0_interval)?;
            let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, IsSigned::No));
            let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, IsSigned::No));
            let ans = lhs.wrapping_add(rhs);
            return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
        }
        if name.starts_with("xsub_") {
            destination.write_from_interval(self, arg0_interval)?;
            let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, IsSigned::No));
            let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, IsSigned::No));
            let ans = lhs.wrapping_sub(rhs);
            return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
        }
        if name.starts_with("and_") {
            destination.write_from_interval(self, arg0_interval)?;
            let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, IsSigned::No));
            let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, IsSigned::No));
            let ans = lhs & rhs;
            return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
        }
        if name.starts_with("or_") {
            destination.write_from_interval(self, arg0_interval)?;
            let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, IsSigned::No));
            let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, IsSigned::No));
            let ans = lhs | rhs;
            return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
        }
        if name.starts_with("xor_") {
            destination.write_from_interval(self, arg0_interval)?;
            let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, IsSigned::No));
            let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, IsSigned::No));
            let ans = lhs ^ rhs;
            return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
        }
        if name.starts_with("nand_") {
            destination.write_from_interval(self, arg0_interval)?;
            let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, IsSigned::No));
            let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, IsSigned::No));
            let ans = !(lhs & rhs);
            return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
        }
        let Some(arg2) = args.get(2) else {
            return Err(MirEvalError::InternalError(
                "atomic intrinsic arg2 is not provided".into(),
            ));
        };
        if name.starts_with("cxchg_") || name.starts_with("cxchgweak_") {
            let dest = if arg1.get(self)? == arg0_interval.get(self)? {
                arg0_interval.write_from_interval(self, arg2.interval)?;
                (arg1.interval, true)
            } else {
                (arg0_interval, false)
            };
            let result_ty = Ty::new_tup_from_iter(
                self.interner(),
                [ty, Ty::new_bool(self.interner())].into_iter(),
            );
            let layout = self.layout(result_ty)?;
            let result = self.construct_with_layout(
                layout.size.bytes_usize(),
                &layout,
                None,
                [IntervalOrOwned::Borrowed(dest.0), IntervalOrOwned::Owned(vec![u8::from(dest.1)])]
                    .into_iter(),
            )?;
            return destination.write_from_bytes(self, &result);
        }
        not_supported!("unknown atomic intrinsic {name}");
    }
}
