1use std::collections::hash_map::Entry;
2use std::io::Write;
3use std::path::Path;
4
5use rustc_abi::{Align, CanonAbi, Size};
6use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE;
7use rustc_data_structures::either::Either;
8use rustc_hir::attrs::Linkage;
9use rustc_hir::def::DefKind;
10use rustc_hir::def_id::CrateNum;
11use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
12use rustc_middle::mir::interpret::AllocInit;
13use rustc_middle::ty::{Instance, Ty};
14use rustc_middle::{mir, ty};
15use rustc_session::config::OomStrategy;
16use rustc_span::Symbol;
17use rustc_target::callconv::FnAbi;
18
19use super::alloc::EvalContextExt as _;
20use super::backtrace::EvalContextExt as _;
21use crate::concurrency::GenmcEvalContextExt as _;
22use crate::helpers::EvalContextExt as _;
23use crate::*;
24
25#[derive(Debug, Copy, Clone)]
27pub struct DynSym(Symbol);
28
29#[expect(clippy::should_implement_trait)]
30impl DynSym {
31 pub fn from_str(name: &str) -> Self {
32 DynSym(Symbol::intern(name))
33 }
34}
35
36impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
37pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
38 fn emulate_foreign_item(
45 &mut self,
46 link_name: Symbol,
47 abi: &FnAbi<'tcx, Ty<'tcx>>,
48 args: &[OpTy<'tcx>],
49 dest: &PlaceTy<'tcx>,
50 ret: Option<mir::BasicBlock>,
51 unwind: mir::UnwindAction,
52 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
53 let this = self.eval_context_mut();
54
55 if let Some(shim) = this.machine.allocator_shim_symbols.get(&link_name) {
57 match *shim {
58 Either::Left(other_fn) => {
59 let handler = this
60 .lookup_exported_symbol(other_fn)?
61 .expect("missing alloc error handler symbol");
62 return interp_ok(Some(handler));
63 }
64 Either::Right(special) => {
65 this.rust_special_allocator_method(special, link_name, abi, args, dest)?;
66 this.return_to_block(ret)?;
67 return interp_ok(None);
68 }
69 }
70 }
71
72 let dest = this.force_allocation(dest)?;
74
75 match this.emulate_foreign_item_inner(link_name, abi, args, &dest)? {
77 EmulateItemResult::NeedsReturn => {
78 trace!("{:?}", this.dump_place(&dest.clone().into()));
79 this.return_to_block(ret)?;
80 }
81 EmulateItemResult::NeedsUnwind => {
82 this.unwind_to_block(unwind)?;
84 }
85 EmulateItemResult::AlreadyJumped => (),
86 EmulateItemResult::NotSupported => {
87 if let Some(body) = this.lookup_exported_symbol(link_name)? {
88 return interp_ok(Some(body));
89 }
90
91 throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!(
92 "can't call foreign function `{link_name}` on OS `{os}`",
93 os = this.tcx.sess.target.os,
94 )));
95 }
96 }
97
98 interp_ok(None)
99 }
100
101 fn is_dyn_sym(&self, name: &str) -> bool {
102 let this = self.eval_context_ref();
103 match this.tcx.sess.target.os.as_ref() {
104 os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os),
105 "wasi" => shims::wasi::foreign_items::is_dyn_sym(name),
106 "windows" => shims::windows::foreign_items::is_dyn_sym(name),
107 _ => false,
108 }
109 }
110
111 fn emulate_dyn_sym(
113 &mut self,
114 sym: DynSym,
115 abi: &FnAbi<'tcx, Ty<'tcx>>,
116 args: &[OpTy<'tcx>],
117 dest: &PlaceTy<'tcx>,
118 ret: Option<mir::BasicBlock>,
119 unwind: mir::UnwindAction,
120 ) -> InterpResult<'tcx> {
121 let res = self.emulate_foreign_item(sym.0, abi, args, dest, ret, unwind)?;
122 assert!(res.is_none(), "DynSyms that delegate are not supported");
123 interp_ok(())
124 }
125
126 fn lookup_exported_symbol(
128 &mut self,
129 link_name: Symbol,
130 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
131 let this = self.eval_context_mut();
132 let tcx = this.tcx.tcx;
133
134 let entry = this.machine.exported_symbols_cache.entry(link_name);
137 let instance = *match entry {
138 Entry::Occupied(e) => e.into_mut(),
139 Entry::Vacant(e) => {
140 struct SymbolTarget<'tcx> {
143 instance: ty::Instance<'tcx>,
144 cnum: CrateNum,
145 is_weak: bool,
146 }
147 let mut symbol_target: Option<SymbolTarget<'tcx>> = None;
148 helpers::iter_exported_symbols(tcx, |cnum, def_id| {
149 let attrs = tcx.codegen_fn_attrs(def_id);
150 if tcx.is_foreign_item(def_id) {
152 return interp_ok(());
153 }
154 if !(attrs.symbol_name.is_some()
156 || attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
157 || attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL))
158 {
159 return interp_ok(());
160 }
161
162 let instance = Instance::mono(tcx, def_id);
163 let symbol_name = tcx.symbol_name(instance).name;
164 let is_weak = attrs.linkage == Some(Linkage::WeakAny);
165 if symbol_name == link_name.as_str() {
166 if let Some(original) = &symbol_target {
167 match (is_weak, original.is_weak) {
170 (false, true) => {
171 symbol_target = Some(SymbolTarget {
174 instance: ty::Instance::mono(tcx, def_id),
175 cnum,
176 is_weak,
177 });
178 }
179 (true, false) => {
180 }
182 (true, true) | (false, false) => {
183 let original_span =
190 tcx.def_span(original.instance.def_id()).data();
191 let span = tcx.def_span(def_id).data();
192 if original_span < span {
193 throw_machine_stop!(
194 TerminationInfo::MultipleSymbolDefinitions {
195 link_name,
196 first: original_span,
197 first_crate: tcx.crate_name(original.cnum),
198 second: span,
199 second_crate: tcx.crate_name(cnum),
200 }
201 );
202 } else {
203 throw_machine_stop!(
204 TerminationInfo::MultipleSymbolDefinitions {
205 link_name,
206 first: span,
207 first_crate: tcx.crate_name(cnum),
208 second: original_span,
209 second_crate: tcx.crate_name(original.cnum),
210 }
211 );
212 }
213 }
214 }
215 } else {
216 symbol_target = Some(SymbolTarget {
217 instance: ty::Instance::mono(tcx, def_id),
218 cnum,
219 is_weak,
220 });
221 }
222 }
223 interp_ok(())
224 })?;
225
226 if let Some(SymbolTarget { instance, .. }) = symbol_target {
230 if !matches!(tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
231 throw_ub_format!(
232 "attempt to call an exported symbol that is not defined as a function"
233 );
234 }
235 }
236
237 e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
238 }
239 };
240 match instance {
241 None => interp_ok(None), Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))),
243 }
244 }
245}
246
247impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
248trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
249 fn emulate_foreign_item_inner(
250 &mut self,
251 link_name: Symbol,
252 abi: &FnAbi<'tcx, Ty<'tcx>>,
253 args: &[OpTy<'tcx>],
254 dest: &MPlaceTy<'tcx>,
255 ) -> InterpResult<'tcx, EmulateItemResult> {
256 let this = self.eval_context_mut();
257
258 #[cfg(all(unix, feature = "native-lib"))]
260 if !this.machine.native_lib.is_empty() {
261 use crate::shims::native_lib::EvalContextExt as _;
262 if this.call_native_fn(link_name, dest, args)? {
266 return interp_ok(EmulateItemResult::NeedsReturn);
267 }
268 }
269 match link_name.as_str() {
308 name if name == this.mangle_internal_symbol(NO_ALLOC_SHIM_IS_UNSTABLE) => {
310 let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
313 }
314 name if name == this.mangle_internal_symbol(OomStrategy::SYMBOL) => {
315 let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
317 let val = this.tcx.sess.opts.unstable_opts.oom.should_panic();
318 this.write_int(val, dest)?;
319 }
320
321 "miri_alloc" => {
323 let [size, align] =
324 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
325 let size = this.read_target_usize(size)?;
326 let align = this.read_target_usize(align)?;
327
328 this.check_rust_alloc_request(size, align)?;
329
330 let ptr = this.allocate_ptr(
331 Size::from_bytes(size),
332 Align::from_bytes(align).unwrap(),
333 MiriMemoryKind::Miri.into(),
334 AllocInit::Uninit,
335 )?;
336
337 this.write_pointer(ptr, dest)?;
338 }
339 "miri_dealloc" => {
340 let [ptr, old_size, align] =
341 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
342 let ptr = this.read_pointer(ptr)?;
343 let old_size = this.read_target_usize(old_size)?;
344 let align = this.read_target_usize(align)?;
345
346 this.deallocate_ptr(
348 ptr,
349 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
350 MiriMemoryKind::Miri.into(),
351 )?;
352 }
353 "miri_start_unwind" => {
354 let [payload] =
355 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
356 this.handle_miri_start_unwind(payload)?;
357 return interp_ok(EmulateItemResult::NeedsUnwind);
358 }
359 "miri_run_provenance_gc" => {
360 let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
361 this.run_provenance_gc();
362 }
363 "miri_get_alloc_id" => {
364 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
365 let ptr = this.read_pointer(ptr)?;
366 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
367 err_machine_stop!(TerminationInfo::Abort(format!(
368 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
369 )))
370 })?;
371 this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
372 }
373 "miri_print_borrow_state" => {
374 let [id, show_unnamed] =
375 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
376 let id = this.read_scalar(id)?.to_u64()?;
377 let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
378 if let Some(id) = std::num::NonZero::new(id).map(AllocId)
379 && this.get_alloc_info(id).kind == AllocKind::LiveData
380 {
381 this.print_borrow_state(id, show_unnamed)?;
382 } else {
383 eprintln!("{id} is not the ID of a live data allocation");
384 }
385 }
386 "miri_pointer_name" => {
387 let [ptr, nth_parent, name] =
390 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
391 let ptr = this.read_pointer(ptr)?;
392 let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
393 let name = this.read_immediate(name)?;
394
395 let name = this.read_byte_slice(&name)?;
396 let name = String::from_utf8_lossy(name).into_owned();
400 this.give_pointer_debug_name(ptr, nth_parent, &name)?;
401 }
402 "miri_static_root" => {
403 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
404 let ptr = this.read_pointer(ptr)?;
405 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
406 if offset != Size::ZERO {
407 throw_unsup_format!(
408 "pointer passed to `miri_static_root` must point to beginning of an allocated block"
409 );
410 }
411 this.machine.static_roots.push(alloc_id);
412 }
413 "miri_host_to_target_path" => {
414 let [ptr, out, out_size] =
415 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
416 let ptr = this.read_pointer(ptr)?;
417 let out = this.read_pointer(out)?;
418 let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
419
420 this.check_no_isolation("`miri_host_to_target_path`")?;
422
423 let path = this.read_os_str_from_c_str(ptr)?.to_owned();
425 let (success, needed_size) =
426 this.write_path_to_c_str(Path::new(&path), out, out_size)?;
427 this.write_int(if success { 0 } else { needed_size }, dest)?;
429 }
430 "miri_backtrace_size" => {
432 this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
433 }
434 "miri_get_backtrace" => {
436 this.handle_miri_get_backtrace(abi, link_name, args)?;
438 }
439 "miri_resolve_frame" => {
441 this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
443 }
444 "miri_resolve_frame_names" => {
446 this.handle_miri_resolve_frame_names(abi, link_name, args)?;
447 }
448 "miri_write_to_stdout" | "miri_write_to_stderr" => {
451 let [msg] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
452 let msg = this.read_immediate(msg)?;
453 let msg = this.read_byte_slice(&msg)?;
454 let _ignore = match link_name.as_str() {
456 "miri_write_to_stdout" => std::io::stdout().write_all(msg),
457 "miri_write_to_stderr" => std::io::stderr().write_all(msg),
458 _ => unreachable!(),
459 };
460 }
461 "miri_promise_symbolic_alignment" => {
463 use rustc_abi::AlignFromBytesError;
464
465 let [ptr, align] =
466 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
467 let ptr = this.read_pointer(ptr)?;
468 let align = this.read_target_usize(align)?;
469 if !align.is_power_of_two() {
470 throw_unsup_format!(
471 "`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
472 );
473 }
474 let align = Align::from_bytes(align).unwrap_or_else(|err| {
475 match err {
476 AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
477 AlignFromBytesError::TooLarge(_) => Align::MAX,
479 }
480 });
481 let addr = ptr.addr();
482 if addr.bytes().strict_rem(align.bytes()) != 0 {
484 throw_unsup_format!(
485 "`miri_promise_symbolic_alignment`: pointer is not actually aligned"
486 );
487 }
488 if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
489 let alloc_align = this.get_alloc_info(alloc_id).align;
490 if align > alloc_align
493 && this
494 .machine
495 .symbolic_alignment
496 .get_mut()
497 .get(&alloc_id)
498 .is_none_or(|&(_, old_align)| align > old_align)
499 {
500 this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align));
501 }
502 }
503 }
504 "miri_genmc_assume" => {
506 let [condition] =
507 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
508 if this.machine.data_race.as_genmc_ref().is_some() {
509 this.handle_genmc_verifier_assume(condition)?;
510 } else {
511 throw_unsup_format!("miri_genmc_assume is only supported in GenMC mode")
512 }
513 }
514
515 "exit" => {
517 let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
518 let code = this.read_scalar(code)?.to_i32()?;
519 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
520 genmc_ctx.handle_exit(
522 this.machine.threads.active_thread(),
523 code,
524 crate::concurrency::ExitType::ExitCalled,
525 )?;
526 todo!(); }
528 throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
529 }
530 "abort" => {
531 let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
532 throw_machine_stop!(TerminationInfo::Abort(
533 "the program aborted execution".to_owned()
534 ));
535 }
536
537 "malloc" => {
539 let [size] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
540 let size = this.read_target_usize(size)?;
541 if size <= this.max_size_of_val().bytes() {
542 let res = this.malloc(size, AllocInit::Uninit)?;
543 this.write_pointer(res, dest)?;
544 } else {
545 if this.target_os_is_unix() {
547 this.set_last_error(LibcError("ENOMEM"))?;
548 }
549 this.write_null(dest)?;
550 }
551 }
552 "calloc" => {
553 let [items, elem_size] =
554 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
555 let items = this.read_target_usize(items)?;
556 let elem_size = this.read_target_usize(elem_size)?;
557 if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
558 let res = this.malloc(size.bytes(), AllocInit::Zero)?;
559 this.write_pointer(res, dest)?;
560 } else {
561 if this.target_os_is_unix() {
563 this.set_last_error(LibcError("ENOMEM"))?;
564 }
565 this.write_null(dest)?;
566 }
567 }
568 "free" => {
569 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
570 let ptr = this.read_pointer(ptr)?;
571 this.free(ptr)?;
572 }
573 "realloc" => {
574 let [old_ptr, new_size] =
575 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
576 let old_ptr = this.read_pointer(old_ptr)?;
577 let new_size = this.read_target_usize(new_size)?;
578 if new_size <= this.max_size_of_val().bytes() {
579 let res = this.realloc(old_ptr, new_size)?;
580 this.write_pointer(res, dest)?;
581 } else {
582 if this.target_os_is_unix() {
584 this.set_last_error(LibcError("ENOMEM"))?;
585 }
586 this.write_null(dest)?;
587 }
588 }
589
590 "memcmp" => {
592 let [left, right, n] =
593 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
594 let left = this.read_pointer(left)?;
595 let right = this.read_pointer(right)?;
596 let n = Size::from_bytes(this.read_target_usize(n)?);
597
598 this.ptr_get_alloc_id(left, 0)?;
600 this.ptr_get_alloc_id(right, 0)?;
601
602 let result = {
603 let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
604 let right_bytes = this.read_bytes_ptr_strip_provenance(right, n)?;
605
606 use std::cmp::Ordering::*;
607 match left_bytes.cmp(right_bytes) {
608 Less => -1i32,
609 Equal => 0,
610 Greater => 1,
611 }
612 };
613
614 this.write_scalar(Scalar::from_i32(result), dest)?;
615 }
616 "memrchr" => {
617 let [ptr, val, num] =
618 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
619 let ptr = this.read_pointer(ptr)?;
620 let val = this.read_scalar(val)?.to_i32()?;
621 let num = this.read_target_usize(num)?;
622 #[expect(clippy::as_conversions)]
624 let val = val as u8;
625
626 this.ptr_get_alloc_id(ptr, 0)?;
628
629 if let Some(idx) = this
630 .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
631 .iter()
632 .rev()
633 .position(|&c| c == val)
634 {
635 let idx = u64::try_from(idx).unwrap();
636 #[expect(clippy::arithmetic_side_effects)] let new_ptr = ptr.wrapping_offset(Size::from_bytes(num - idx - 1), this);
638 this.write_pointer(new_ptr, dest)?;
639 } else {
640 this.write_null(dest)?;
641 }
642 }
643 "memchr" => {
644 let [ptr, val, num] =
645 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
646 let ptr = this.read_pointer(ptr)?;
647 let val = this.read_scalar(val)?.to_i32()?;
648 let num = this.read_target_usize(num)?;
649 #[expect(clippy::as_conversions)]
651 let val = val as u8;
652
653 this.ptr_get_alloc_id(ptr, 0)?;
655
656 let idx = this
657 .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
658 .iter()
659 .position(|&c| c == val);
660 if let Some(idx) = idx {
661 let new_ptr = ptr.wrapping_offset(Size::from_bytes(idx), this);
662 this.write_pointer(new_ptr, dest)?;
663 } else {
664 this.write_null(dest)?;
665 }
666 }
667 "strlen" => {
668 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
669 let ptr = this.read_pointer(ptr)?;
670 let n = this.read_c_str(ptr)?.len();
672 this.write_scalar(
673 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
674 dest,
675 )?;
676 }
677 "wcslen" => {
678 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
679 let ptr = this.read_pointer(ptr)?;
680 let n = this.read_wchar_t_str(ptr)?.len();
682 this.write_scalar(
683 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
684 dest,
685 )?;
686 }
687 "memcpy" => {
688 let [ptr_dest, ptr_src, n] =
689 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
690 let ptr_dest = this.read_pointer(ptr_dest)?;
691 let ptr_src = this.read_pointer(ptr_src)?;
692 let n = this.read_target_usize(n)?;
693
694 this.ptr_get_alloc_id(ptr_dest, 0)?;
697 this.ptr_get_alloc_id(ptr_src, 0)?;
698
699 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
700 this.write_pointer(ptr_dest, dest)?;
701 }
702 "strcpy" => {
703 let [ptr_dest, ptr_src] =
704 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
705 let ptr_dest = this.read_pointer(ptr_dest)?;
706 let ptr_src = this.read_pointer(ptr_src)?;
707
708 let n = this.read_c_str(ptr_src)?.len().strict_add(1);
715 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
716 this.write_pointer(ptr_dest, dest)?;
717 }
718 "memset" => {
719 let [ptr_dest, val, n] =
720 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
721 let ptr_dest = this.read_pointer(ptr_dest)?;
722 let val = this.read_scalar(val)?.to_i32()?;
723 let n = this.read_target_usize(n)?;
724 #[expect(clippy::as_conversions)]
726 let val = val as u8;
727
728 this.ptr_get_alloc_id(ptr_dest, 0)?;
730
731 let bytes = std::iter::repeat_n(val, n.try_into().unwrap());
732 this.write_bytes_ptr(ptr_dest, bytes)?;
733 this.write_pointer(ptr_dest, dest)?;
734 }
735
736 "llvm.prefetch" => {
738 let [p, rw, loc, ty] =
739 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
740
741 let _ = this.read_pointer(p)?;
742 let rw = this.read_scalar(rw)?.to_i32()?;
743 let loc = this.read_scalar(loc)?.to_i32()?;
744 let ty = this.read_scalar(ty)?.to_i32()?;
745
746 if ty == 1 {
747 if !matches!(rw, 0 | 1) {
751 throw_unsup_format!("invalid `rw` value passed to `llvm.prefetch`: {}", rw);
752 }
753 if !matches!(loc, 0..=3) {
754 throw_unsup_format!(
755 "invalid `loc` value passed to `llvm.prefetch`: {}",
756 loc
757 );
758 }
759 } else {
760 throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty);
761 }
762 }
763 name if name.starts_with("llvm.ctpop.v") => {
766 let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
767
768 let (op, op_len) = this.project_to_simd(op)?;
769 let (dest, dest_len) = this.project_to_simd(dest)?;
770
771 assert_eq!(dest_len, op_len);
772
773 for i in 0..dest_len {
774 let op = this.read_immediate(&this.project_index(&op, i)?)?;
775 let res = op.to_scalar().to_uint(op.layout.size)?.count_ones();
778
779 this.write_scalar(
780 Scalar::from_uint(res, op.layout.size),
781 &this.project_index(&dest, i)?,
782 )?;
783 }
784 }
785
786 name if name.starts_with("llvm.x86.")
788 && (this.tcx.sess.target.arch == "x86"
789 || this.tcx.sess.target.arch == "x86_64") =>
790 {
791 return shims::x86::EvalContextExt::emulate_x86_intrinsic(
792 this, link_name, abi, args, dest,
793 );
794 }
795 name if name.starts_with("llvm.aarch64.") && this.tcx.sess.target.arch == "aarch64" => {
796 return shims::aarch64::EvalContextExt::emulate_aarch64_intrinsic(
797 this, link_name, abi, args, dest,
798 );
799 }
800 "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => {
802 let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
803 let arg = this.read_scalar(arg)?.to_i32()?;
804 match arg {
806 1 => {
808 this.expect_target_feature_for_intrinsic(link_name, "v6")?;
809 this.yield_active_thread();
810 }
811 _ => {
812 throw_unsup_format!("unsupported llvm.arm.hint argument {}", arg);
813 }
814 }
815 }
816
817 _ => {
819 #[expect(irrefutable_let_patterns)]
821 if let res = shims::math::EvalContextExt::emulate_foreign_item_inner(
822 this, link_name, abi, args, dest,
823 )? && !matches!(res, EmulateItemResult::NotSupported)
824 {
825 return interp_ok(res);
826 }
827
828 return match this.tcx.sess.target.os.as_ref() {
830 _ if this.target_os_is_unix() =>
831 shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
832 this, link_name, abi, args, dest,
833 ),
834 "wasi" =>
835 shims::wasi::foreign_items::EvalContextExt::emulate_foreign_item_inner(
836 this, link_name, abi, args, dest,
837 ),
838 "windows" =>
839 shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
840 this, link_name, abi, args, dest,
841 ),
842 _ => interp_ok(EmulateItemResult::NotSupported),
843 };
844 }
845 };
846 interp_ok(EmulateItemResult::NeedsReturn)
849 }
850}