Skip to content

Commit a416efa

Browse files
committed
Try enabling precondition checks on ptr::{read,write}
1 parent c268b39 commit a416efa

File tree

12 files changed

+31
-22
lines changed

12 files changed

+31
-22
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,6 +1408,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
14081408
rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes,
14091409
"`#[rustc_no_mir_inline]` prevents the MIR inliner from inlining a function while not affecting codegen"
14101410
),
1411+
rustc_attr!(
1412+
rustc_no_ubchecks, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes,
1413+
"#[rustc_no_ubchecks] asks the compiler to delete UB checks from a function"
1414+
),
14111415
rustc_attr!(
14121416
rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes,
14131417
"`#[rustc_force_inline]` forces a free function to be inlined"

compiler/rustc_mir_transform/src/instsimplify.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,29 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
2929
}
3030

3131
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
32+
let def_id = body.source.def_id();
3233
let ctx = InstSimplifyContext {
3334
tcx,
3435
local_decls: &body.local_decls,
3536
typing_env: body.typing_env(tcx),
3637
};
3738
let preserve_ub_checks =
3839
attr::contains_name(tcx.hir_krate_attrs(), sym::rustc_preserve_ub_checks);
40+
let remove_ub_checks = if tcx.is_coroutine(def_id) {
41+
false
42+
} else {
43+
tcx.has_attr(def_id, sym::rustc_no_ubchecks)
44+
};
3945
for block in body.basic_blocks.as_mut() {
4046
for statement in block.statements.iter_mut() {
4147
let StatementKind::Assign(box (.., rvalue)) = &mut statement.kind else {
4248
continue;
4349
};
4450

45-
if !preserve_ub_checks {
46-
ctx.simplify_ub_check(rvalue);
51+
if remove_ub_checks {
52+
ctx.simplify_ub_check(rvalue, false);
53+
} else if !preserve_ub_checks {
54+
ctx.simplify_ub_check(rvalue, tcx.sess.ub_checks());
4755
}
4856
ctx.simplify_bool_cmp(rvalue);
4957
ctx.simplify_ref_deref(rvalue);
@@ -168,13 +176,13 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
168176
}
169177
}
170178

171-
fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>) {
179+
fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>, ub_checks: bool) {
172180
// FIXME: Should we do the same for overflow checks?
173181
let Rvalue::NullaryOp(NullOp::RuntimeChecks(RuntimeChecks::UbChecks)) = *rvalue else {
174182
return;
175183
};
176184

177-
let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks());
185+
let const_ = Const::from_bool(self.tcx, ub_checks);
178186
let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None };
179187
*rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
180188
}

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,7 @@ symbols! {
19611961
rustc_no_implicit_autorefs,
19621962
rustc_no_implicit_bounds,
19631963
rustc_no_mir_inline,
1964+
rustc_no_ubchecks,
19641965
rustc_nonnull_optimization_guaranteed,
19651966
rustc_nounwind,
19661967
rustc_objc_class,

library/alloc/src/boxed.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
14751475
#[must_use = "losing the pointer will leak memory"]
14761476
#[unstable(feature = "allocator_api", issue = "32838")]
14771477
#[inline]
1478+
#[rustc_no_ubchecks]
14781479
pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) {
14791480
let mut b = mem::ManuallyDrop::new(b);
14801481
// We carefully get the raw pointer out in a way that Miri's aliasing model understands what

library/core/src/mem/manually_drop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ impl<T> ManuallyDrop<T> {
219219
#[stable(feature = "manually_drop_take", since = "1.42.0")]
220220
#[rustc_const_unstable(feature = "const_manually_drop_take", issue = "148773")]
221221
#[inline]
222+
#[rustc_no_ubchecks]
222223
pub const unsafe fn take(slot: &mut ManuallyDrop<T>) -> T {
223224
// SAFETY: we are reading from a reference, which is guaranteed
224225
// to be valid for reads.

library/core/src/mem/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ pub const fn take<T: [const] Default>(dest: &mut T) -> T {
876876
#[must_use = "if you don't need the old value, you can just assign the new value directly"]
877877
#[rustc_const_stable(feature = "const_replace", since = "1.83.0")]
878878
#[rustc_diagnostic_item = "mem_replace"]
879+
#[rustc_no_ubchecks]
879880
pub const fn replace<T>(dest: &mut T, src: T) -> T {
880881
// It may be tempting to use `swap` to avoid `unsafe` here. Don't!
881882
// The compiler optimizes the implementation below to two `memcpy`s

library/core/src/ptr/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
14001400

14011401
/// Same behavior and safety conditions as [`swap_nonoverlapping`]
14021402
#[inline]
1403+
#[rustc_no_ubchecks]
14031404
const unsafe fn swap_nonoverlapping_const<T>(x: *mut T, y: *mut T, count: usize) {
14041405
let mut i = 0;
14051406
while i < count {
@@ -1695,7 +1696,6 @@ pub const unsafe fn read<T>(src: *const T) -> T {
16951696

16961697
// SAFETY: the caller must guarantee that `src` is valid for reads.
16971698
unsafe {
1698-
#[cfg(debug_assertions)] // Too expensive to always enable (for now?)
16991699
ub_checks::assert_unsafe_precondition!(
17001700
check_language_ub,
17011701
"ptr::read requires that the pointer argument is aligned and non-null",
@@ -1895,7 +1895,6 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
18951895
// `dst` cannot overlap `src` because the caller has mutable access
18961896
// to `dst` while `src` is owned by this function.
18971897
unsafe {
1898-
#[cfg(debug_assertions)] // Too expensive to always enable (for now?)
18991898
ub_checks::assert_unsafe_precondition!(
19001899
check_language_ub,
19011900
"ptr::write requires that the pointer argument is aligned and non-null",

tests/codegen-llvm/mem-replace-big-type.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
// known to be `1` after inlining).
55

66
//@ compile-flags: -C no-prepopulate-passes -Zinline-mir=no
7-
//@ ignore-std-debug-assertions
8-
// Reason: precondition checks in ptr::read make them a bad candidate for MIR inlining
97
//@ needs-deterministic-layouts
108

119
#![crate_type = "lib"]

tests/codegen-llvm/mem-replace-simple-type.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes
22
//@ only-x86_64 (to not worry about usize differing)
3-
//@ ignore-std-debug-assertions
4-
// Reason: precondition checks make mem::replace not a candidate for MIR inlining
53

64
#![crate_type = "lib"]
75

tests/mir-opt/pre-codegen/mem_replace.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// skip-filecheck
22
//@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -Zinline-mir
3-
//@ ignore-std-debug-assertions
4-
// Reason: precondition checks on ptr::read/write are under cfg(debug_assertions)
53
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
64

75
#![crate_type = "lib"]

0 commit comments

Comments
 (0)