Skip to content

Commit 579bfbd

Browse files
committed
Try enabling precondition checks on ptr::{read,write}
1 parent f7b4354 commit 579bfbd

File tree

11 files changed

+22
-13
lines changed

11 files changed

+22
-13
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
10161016
rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes,
10171017
"#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen"
10181018
),
1019+
rustc_attr!(
1020+
rustc_no_ubchecks, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes,
1021+
"#[rustc_no_ubchecks] asks the compiler to delete UB checks from a function"
1022+
),
10191023
rustc_attr!(
10201024
rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes,
10211025
"#[rustc_force_inline] forces a free function to be inlined"

compiler/rustc_mir_transform/src/instsimplify.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,27 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
3030
}
3131

3232
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
33+
let def_id = body.source.def_id();
3334
let ctx = InstSimplifyContext {
3435
tcx,
3536
local_decls: &body.local_decls,
3637
typing_env: body.typing_env(tcx),
3738
};
3839
let preserve_ub_checks =
3940
attr::contains_name(tcx.hir_krate_attrs(), sym::rustc_preserve_ub_checks);
41+
let remove_ub_checks = if tcx.is_coroutine(def_id) {
42+
false
43+
} else {
44+
tcx.has_attr(def_id, sym::rustc_no_ubchecks)
45+
};
4046
for block in body.basic_blocks.as_mut() {
4147
for statement in block.statements.iter_mut() {
4248
match statement.kind {
4349
StatementKind::Assign(box (_place, ref mut rvalue)) => {
44-
if !preserve_ub_checks {
45-
ctx.simplify_ub_check(rvalue);
50+
if remove_ub_checks {
51+
ctx.simplify_ub_check(rvalue, false);
52+
} else if !preserve_ub_checks {
53+
ctx.simplify_ub_check(rvalue, tcx.sess.ub_checks());
4654
}
4755
ctx.simplify_bool_cmp(rvalue);
4856
ctx.simplify_ref_deref(rvalue);
@@ -181,9 +189,9 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
181189
}
182190
}
183191

184-
fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>) {
192+
fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>, ub_checks: bool) {
185193
if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue {
186-
let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks());
194+
let const_ = Const::from_bool(self.tcx, ub_checks);
187195
let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None };
188196
*rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
189197
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1800,6 +1800,7 @@ symbols! {
18001800
rustc_never_returns_null_ptr,
18011801
rustc_never_type_options,
18021802
rustc_no_mir_inline,
1803+
rustc_no_ubchecks,
18031804
rustc_nonnull_optimization_guaranteed,
18041805
rustc_nounwind,
18051806
rustc_object_lifetime_default,

library/alloc/src/boxed.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
13741374
#[must_use = "losing the pointer will leak memory"]
13751375
#[unstable(feature = "allocator_api", issue = "32838")]
13761376
#[inline]
1377+
#[cfg_attr(not(bootstrap), rustc_no_ubchecks)]
13771378
pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) {
13781379
let mut b = mem::ManuallyDrop::new(b);
13791380
// We carefully get the raw pointer out in a way that Miri's aliasing model understands what

library/core/src/mem/manually_drop.rs

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ impl<T> ManuallyDrop<T> {
217217
#[must_use = "if you don't need the value, you can use `ManuallyDrop::drop` instead"]
218218
#[stable(feature = "manually_drop_take", since = "1.42.0")]
219219
#[inline]
220+
#[cfg_attr(not(bootstrap), rustc_no_ubchecks)]
220221
pub unsafe fn take(slot: &mut ManuallyDrop<T>) -> T {
221222
// SAFETY: we are reading from a reference, which is guaranteed
222223
// to be valid for reads.

library/core/src/mem/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,7 @@ pub fn take<T: Default>(dest: &mut T) -> T {
847847
#[must_use = "if you don't need the old value, you can just assign the new value directly"]
848848
#[rustc_const_stable(feature = "const_replace", since = "1.83.0")]
849849
#[cfg_attr(not(test), rustc_diagnostic_item = "mem_replace")]
850+
#[cfg_attr(not(bootstrap), rustc_no_ubchecks)]
850851
pub const fn replace<T>(dest: &mut T, src: T) -> T {
851852
// It may be tempting to use `swap` to avoid `unsafe` here. Don't!
852853
// The compiler optimizes the implementation below to two `memcpy`s

library/core/src/ptr/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
11361136
/// LLVM can vectorize this (at least it can for the power-of-two-sized types
11371137
/// `swap_nonoverlapping` tries to use) so no need to manually SIMD it.
11381138
#[inline]
1139+
#[cfg_attr(not(bootstrap), rustc_no_ubchecks)]
11391140
const unsafe fn swap_nonoverlapping_simple_untyped<T>(x: *mut T, y: *mut T, count: usize) {
11401141
let x = x.cast::<MaybeUninit<T>>();
11411142
let y = y.cast::<MaybeUninit<T>>();
@@ -1367,7 +1368,6 @@ pub const unsafe fn read<T>(src: *const T) -> T {
13671368

13681369
// SAFETY: the caller must guarantee that `src` is valid for reads.
13691370
unsafe {
1370-
#[cfg(debug_assertions)] // Too expensive to always enable (for now?)
13711371
ub_checks::assert_unsafe_precondition!(
13721372
check_language_ub,
13731373
"ptr::read requires that the pointer argument is aligned and non-null",
@@ -1567,7 +1567,6 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
15671567
// `dst` cannot overlap `src` because the caller has mutable access
15681568
// to `dst` while `src` is owned by this function.
15691569
unsafe {
1570-
#[cfg(debug_assertions)] // Too expensive to always enable (for now?)
15711570
ub_checks::assert_unsafe_precondition!(
15721571
check_language_ub,
15731572
"ptr::write requires that the pointer argument is aligned and non-null",

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

-2
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/mem-replace-simple-type.rs

-2
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

-2
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"]

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// skip-filecheck
22
//@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -Zinline-mir
3-
//@ ignore-std-debug-assertions (precondition checks are under cfg(debug_assertions))
3+
//@ ignore-std-debug-assertions (precondition checks on ptr::add are under cfg(debug_assertions))
44
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
55

66
#![crate_type = "lib"]

0 commit comments

Comments
 (0)