Skip to content

add a fallback implementation for the prefetch_* intrinsics #145626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,10 +330,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
_ => bug!(),
};
let ptr = args[0].immediate();
let locality = fn_args.const_at(1).to_value().valtree.unwrap_leaf().to_u32() as i32;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you use to_i32()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, I suspect my autocomplete only showed the unsigned versions? Or I missed the signed versions when looking through the docs somehow.

Anyway, to_i32 would be better, but also only the values 0..4 are supported.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoiding as seems good so can you make a PR?

self.call_intrinsic(
"llvm.prefetch",
&[self.val_ty(ptr)],
&[ptr, self.const_i32(rw), args[1].immediate(), self.const_i32(cache_type)],
&[
ptr,
self.const_i32(rw),
self.const_i32(locality),
self.const_i32(cache_type),
],
)
}
sym::carrying_mul_add => {
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::round_ties_even_f64
| sym::round_ties_even_f128
| sym::autodiff
| sym::prefetch_read_data
| sym::prefetch_write_data
| sym::prefetch_read_instruction
| sym::prefetch_write_instruction
| sym::const_eval_select => hir::Safety::Safe,
_ => hir::Safety::Unsafe,
};
Expand Down Expand Up @@ -218,7 +222,7 @@ pub(crate) fn check_intrinsic_type(
| sym::prefetch_write_data
| sym::prefetch_read_instruction
| sym::prefetch_write_instruction => {
(1, 0, vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.i32], tcx.types.unit)
(1, 1, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.unit)
}
sym::needs_drop => (1, 0, vec![], tcx.types.bool),

Expand Down
51 changes: 35 additions & 16 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,53 +261,72 @@ pub unsafe fn atomic_fence<const ORD: AtomicOrdering>();
pub unsafe fn atomic_singlethreadfence<const ORD: AtomicOrdering>();

/// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction
/// if supported; otherwise, it is a no-op.
/// for the given address if supported; otherwise, it is a no-op.
/// Prefetches have no effect on the behavior of the program but can change its performance
/// characteristics.
///
/// The `locality` argument must be a constant integer and is a temporal locality specifier
/// ranging from (0) - no locality, to (3) - extremely local keep in cache.
/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality,
/// to (3) - extremely local keep in cache.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn prefetch_read_data<T>(data: *const T, locality: i32);
#[miri::intrinsic_fallback_is_spec]
pub const fn prefetch_read_data<T, const LOCALITY: i32>(data: *const T) {
// This operation is a no-op, unless it is overridden by the backend.
let _ = data;
}

/// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction
/// if supported; otherwise, it is a no-op.
/// for the given address if supported; otherwise, it is a no-op.
/// Prefetches have no effect on the behavior of the program but can change its performance
/// characteristics.
///
/// The `locality` argument must be a constant integer and is a temporal locality specifier
/// ranging from (0) - no locality, to (3) - extremely local keep in cache.
/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality,
/// to (3) - extremely local keep in cache.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn prefetch_write_data<T>(data: *const T, locality: i32);
#[miri::intrinsic_fallback_is_spec]
pub const fn prefetch_write_data<T, const LOCALITY: i32>(data: *const T) {
// This operation is a no-op, unless it is overridden by the backend.
let _ = data;
}

/// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction
/// if supported; otherwise, it is a no-op.
/// for the given address if supported; otherwise, it is a no-op.
/// Prefetches have no effect on the behavior of the program but can change its performance
/// characteristics.
///
/// The `locality` argument must be a constant integer and is a temporal locality specifier
/// ranging from (0) - no locality, to (3) - extremely local keep in cache.
/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality,
/// to (3) - extremely local keep in cache.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn prefetch_read_instruction<T>(data: *const T, locality: i32);
#[miri::intrinsic_fallback_is_spec]
pub const fn prefetch_read_instruction<T, const LOCALITY: i32>(data: *const T) {
// This operation is a no-op, unless it is overridden by the backend.
let _ = data;
}

/// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction
/// if supported; otherwise, it is a no-op.
/// for the given address if supported; otherwise, it is a no-op.
/// Prefetches have no effect on the behavior of the program but can change its performance
/// characteristics.
///
/// The `locality` argument must be a constant integer and is a temporal locality specifier
/// ranging from (0) - no locality, to (3) - extremely local keep in cache.
/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality,
/// to (3) - extremely local keep in cache.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn prefetch_write_instruction<T>(data: *const T, locality: i32);
#[miri::intrinsic_fallback_is_spec]
pub const fn prefetch_write_instruction<T, const LOCALITY: i32>(data: *const T) {
// This operation is a no-op, unless it is overridden by the backend.
let _ = data;
}

/// Executes a breakpoint trap, for inspection by a debugger.
///
Expand Down
23 changes: 23 additions & 0 deletions src/tools/miri/tests/pass/prefetch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![feature(core_intrinsics)]

// Test that these intrinsics work. Their behavior should be a no-op.

fn main() {
static X: [u8; 8] = [0; 8];

::std::intrinsics::prefetch_read_data::<_, 1>(::std::ptr::null::<u8>());
::std::intrinsics::prefetch_read_data::<_, 2>(::std::ptr::dangling::<u8>());
::std::intrinsics::prefetch_read_data::<_, 3>(X.as_ptr());

::std::intrinsics::prefetch_write_data::<_, 1>(::std::ptr::null::<u8>());
::std::intrinsics::prefetch_write_data::<_, 2>(::std::ptr::dangling::<u8>());
::std::intrinsics::prefetch_write_data::<_, 3>(X.as_ptr());

::std::intrinsics::prefetch_read_instruction::<_, 1>(::std::ptr::null::<u8>());
::std::intrinsics::prefetch_read_instruction::<_, 2>(::std::ptr::dangling::<u8>());
::std::intrinsics::prefetch_read_instruction::<_, 3>(X.as_ptr());

::std::intrinsics::prefetch_write_instruction::<_, 1>(::std::ptr::null::<u8>());
::std::intrinsics::prefetch_write_instruction::<_, 2>(::std::ptr::dangling::<u8>());
::std::intrinsics::prefetch_write_instruction::<_, 3>(X.as_ptr());
}
72 changes: 32 additions & 40 deletions tests/codegen-llvm/intrinsics/prefetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,56 +9,48 @@ use std::intrinsics::{

#[no_mangle]
pub fn check_prefetch_read_data(data: &[i8]) {
unsafe {
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 1)
prefetch_read_data(data.as_ptr(), 0);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 1)
prefetch_read_data(data.as_ptr(), 1);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 1)
prefetch_read_data(data.as_ptr(), 2);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 1)
prefetch_read_data(data.as_ptr(), 3);
}
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 1)
prefetch_read_data::<_, 0>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 1)
prefetch_read_data::<_, 1>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 1)
prefetch_read_data::<_, 2>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 1)
prefetch_read_data::<_, 3>(data.as_ptr());
}

#[no_mangle]
pub fn check_prefetch_write_data(data: &[i8]) {
unsafe {
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 1)
prefetch_write_data(data.as_ptr(), 0);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 1)
prefetch_write_data(data.as_ptr(), 1);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 1)
prefetch_write_data(data.as_ptr(), 2);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 1)
prefetch_write_data(data.as_ptr(), 3);
}
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 1)
prefetch_write_data::<_, 0>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 1)
prefetch_write_data::<_, 1>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 1)
prefetch_write_data::<_, 2>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 1)
prefetch_write_data::<_, 3>(data.as_ptr());
}

#[no_mangle]
pub fn check_prefetch_read_instruction(data: &[i8]) {
unsafe {
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 0)
prefetch_read_instruction(data.as_ptr(), 0);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 0)
prefetch_read_instruction(data.as_ptr(), 1);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 0)
prefetch_read_instruction(data.as_ptr(), 2);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 0)
prefetch_read_instruction(data.as_ptr(), 3);
}
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 0)
prefetch_read_instruction::<_, 0>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 0)
prefetch_read_instruction::<_, 1>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 0)
prefetch_read_instruction::<_, 2>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 0)
prefetch_read_instruction::<_, 3>(data.as_ptr());
}

#[no_mangle]
pub fn check_prefetch_write_instruction(data: &[i8]) {
unsafe {
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 0)
prefetch_write_instruction(data.as_ptr(), 0);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 0)
prefetch_write_instruction(data.as_ptr(), 1);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 0)
prefetch_write_instruction(data.as_ptr(), 2);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 0)
prefetch_write_instruction(data.as_ptr(), 3);
}
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 0)
prefetch_write_instruction::<_, 0>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 0)
prefetch_write_instruction::<_, 1>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 0)
prefetch_write_instruction::<_, 2>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 0)
prefetch_write_instruction::<_, 3>(data.as_ptr());
}
Loading