Eliminate UbCheck
for non-standard libraries
This commit is contained in:
parent
10a7aa14fe
commit
47ed73a7b5
@ -821,6 +821,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||||||
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No,
|
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No,
|
||||||
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
|
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
|
||||||
),
|
),
|
||||||
|
rustc_attr!(
|
||||||
|
rustc_preserve_ub_checks, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No,
|
||||||
|
"`#![rustc_preserve_ub_checks]` prevents the designated crate from evaluating whether UB checks are enabled when optimizing MIR",
|
||||||
|
),
|
||||||
rustc_attr!(
|
rustc_attr!(
|
||||||
rustc_deny_explicit_impl,
|
rustc_deny_explicit_impl,
|
||||||
AttributeType::Normal,
|
AttributeType::Normal,
|
||||||
|
@ -1361,8 +1361,8 @@ pub enum NullOp<'tcx> {
|
|||||||
AlignOf,
|
AlignOf,
|
||||||
/// Returns the offset of a field
|
/// Returns the offset of a field
|
||||||
OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
|
OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
|
||||||
/// Returns whether we want to check for UB.
|
/// Returns whether we should perform some UB-checking at runtime.
|
||||||
/// This returns the value of `cfg!(debug_assertions)` at monomorphization time.
|
/// See the `ub_checks` intrinsic docs for details.
|
||||||
UbChecks,
|
UbChecks,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
//! Performs various peephole optimizations.
|
//! Performs various peephole optimizations.
|
||||||
|
|
||||||
use crate::simplify::simplify_duplicate_switch_targets;
|
use crate::simplify::simplify_duplicate_switch_targets;
|
||||||
|
use rustc_ast::attr;
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::ty::layout;
|
use rustc_middle::ty::layout;
|
||||||
use rustc_middle::ty::layout::ValidityRequirement;
|
use rustc_middle::ty::layout::ValidityRequirement;
|
||||||
use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt};
|
use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt};
|
||||||
|
use rustc_span::sym;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
use rustc_target::abi::FieldIdx;
|
use rustc_target::abi::FieldIdx;
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
@ -22,10 +24,15 @@ impl<'tcx> MirPass<'tcx> for InstSimplify {
|
|||||||
local_decls: &body.local_decls,
|
local_decls: &body.local_decls,
|
||||||
param_env: tcx.param_env_reveal_all_normalized(body.source.def_id()),
|
param_env: tcx.param_env_reveal_all_normalized(body.source.def_id()),
|
||||||
};
|
};
|
||||||
|
let preserve_ub_checks =
|
||||||
|
attr::contains_name(tcx.hir().krate_attrs(), sym::rustc_preserve_ub_checks);
|
||||||
for block in body.basic_blocks.as_mut() {
|
for block in body.basic_blocks.as_mut() {
|
||||||
for statement in block.statements.iter_mut() {
|
for statement in block.statements.iter_mut() {
|
||||||
match statement.kind {
|
match statement.kind {
|
||||||
StatementKind::Assign(box (_place, ref mut rvalue)) => {
|
StatementKind::Assign(box (_place, ref mut rvalue)) => {
|
||||||
|
if !preserve_ub_checks {
|
||||||
|
ctx.simplify_ub_check(&statement.source_info, rvalue);
|
||||||
|
}
|
||||||
ctx.simplify_bool_cmp(&statement.source_info, rvalue);
|
ctx.simplify_bool_cmp(&statement.source_info, rvalue);
|
||||||
ctx.simplify_ref_deref(&statement.source_info, rvalue);
|
ctx.simplify_ref_deref(&statement.source_info, rvalue);
|
||||||
ctx.simplify_len(&statement.source_info, rvalue);
|
ctx.simplify_len(&statement.source_info, rvalue);
|
||||||
@ -140,6 +147,14 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
|
||||||
|
if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue {
|
||||||
|
let const_ = Const::from_bool(self.tcx, self.tcx.sess.opts.debug_assertions);
|
||||||
|
let constant = ConstOperand { span: source_info.span, const_, user_ty: None };
|
||||||
|
*rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn simplify_cast(&self, rvalue: &mut Rvalue<'tcx>) {
|
fn simplify_cast(&self, rvalue: &mut Rvalue<'tcx>) {
|
||||||
if let Rvalue::Cast(kind, operand, cast_ty) = rvalue {
|
if let Rvalue::Cast(kind, operand, cast_ty) = rvalue {
|
||||||
let operand_ty = operand.ty(self.local_decls, self.tcx);
|
let operand_ty = operand.ty(self.local_decls, self.tcx);
|
||||||
|
@ -1572,6 +1572,7 @@ symbols! {
|
|||||||
rustc_peek_maybe_init,
|
rustc_peek_maybe_init,
|
||||||
rustc_peek_maybe_uninit,
|
rustc_peek_maybe_uninit,
|
||||||
rustc_polymorphize_error,
|
rustc_polymorphize_error,
|
||||||
|
rustc_preserve_ub_checks,
|
||||||
rustc_private,
|
rustc_private,
|
||||||
rustc_proc_macro_decls,
|
rustc_proc_macro_decls,
|
||||||
rustc_promotable,
|
rustc_promotable,
|
||||||
|
@ -176,6 +176,7 @@
|
|||||||
// Language features:
|
// Language features:
|
||||||
// tidy-alphabetical-start
|
// tidy-alphabetical-start
|
||||||
#![cfg_attr(bootstrap, feature(associated_type_bounds))]
|
#![cfg_attr(bootstrap, feature(associated_type_bounds))]
|
||||||
|
#![cfg_attr(not(bootstrap), rustc_preserve_ub_checks)]
|
||||||
#![cfg_attr(not(test), feature(coroutine_trait))]
|
#![cfg_attr(not(test), feature(coroutine_trait))]
|
||||||
#![cfg_attr(test, feature(panic_update_hook))]
|
#![cfg_attr(test, feature(panic_update_hook))]
|
||||||
#![cfg_attr(test, feature(test))]
|
#![cfg_attr(test, feature(test))]
|
||||||
|
@ -2686,12 +2686,14 @@ pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
|
|||||||
unsafe { ptr::swap_nonoverlapping(x, y, 1) };
|
unsafe { ptr::swap_nonoverlapping(x, y, 1) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether we should perform some UB-checking at runtime. This evaluate to the value of
|
/// Returns whether we should perform some UB-checking at runtime. This eventually evaluates to
|
||||||
/// `cfg!(debug_assertions)` during monomorphization.
|
/// `cfg!(debug_assertions)`, but behaves different from `cfg!` when mixing crates built with different
|
||||||
|
/// flags: if the crate has debug assertions enabled or carries the `#[rustc_preserve_ub_checks]`
|
||||||
|
/// attribute, evaluation is delayed until monomorphization (or until the call gets inlined into
|
||||||
|
/// a crate that does not delay evaluation further); otherwise it can happen any time.
|
||||||
///
|
///
|
||||||
/// This intrinsic is evaluated after monomorphization, which is relevant when mixing crates
|
/// The common case here is a user program built with debug_assertions linked against the distributed
|
||||||
/// compiled with and without debug_assertions. The common case here is a user program built with
|
/// sysroot which is built without debug_assertions but with `#[rustc_preserve_ub_checks]`.
|
||||||
/// debug_assertions linked against the distributed sysroot which is built without debug_assertions.
|
|
||||||
/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
|
/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
|
||||||
/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(debug_assertions)` means that
|
/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(debug_assertions)` means that
|
||||||
/// assertions are enabled whenever the *user crate* has debug assertions enabled. However if the
|
/// assertions are enabled whenever the *user crate* has debug assertions enabled. However if the
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
))]
|
))]
|
||||||
#![no_core]
|
#![no_core]
|
||||||
#![rustc_coherence_is_core]
|
#![rustc_coherence_is_core]
|
||||||
|
#![cfg_attr(not(bootstrap), rustc_preserve_ub_checks)]
|
||||||
//
|
//
|
||||||
// Lints:
|
// Lints:
|
||||||
#![deny(rust_2021_incompatible_or_patterns)]
|
#![deny(rust_2021_incompatible_or_patterns)]
|
||||||
|
@ -221,6 +221,7 @@
|
|||||||
//
|
//
|
||||||
#![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))]
|
#![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))]
|
||||||
#![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))]
|
#![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))]
|
||||||
|
#![cfg_attr(not(bootstrap), rustc_preserve_ub_checks)]
|
||||||
#![doc(
|
#![doc(
|
||||||
html_playground_url = "https://play.rust-lang.org/",
|
html_playground_url = "https://play.rust-lang.org/",
|
||||||
issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/",
|
issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/",
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
Function name: unreachable::UNREACHABLE_CLOSURE::{closure#0}
|
Function name: unreachable::UNREACHABLE_CLOSURE::{closure#0} (unused)
|
||||||
Raw bytes (9): 0x[01, 01, 00, 01, 01, 0f, 27, 00, 47]
|
Raw bytes (9): 0x[01, 01, 00, 01, 00, 0f, 27, 00, 47]
|
||||||
Number of files: 1
|
Number of files: 1
|
||||||
- file 0 => global file 1
|
- file 0 => global file 1
|
||||||
Number of expressions: 0
|
Number of expressions: 0
|
||||||
Number of file 0 mappings: 1
|
Number of file 0 mappings: 1
|
||||||
- Code(Counter(0)) at (prev + 15, 39) to (start + 0, 71)
|
- Code(Zero) at (prev + 15, 39) to (start + 0, 71)
|
||||||
|
|
||||||
Function name: unreachable::unreachable_function
|
Function name: unreachable::unreachable_function (unused)
|
||||||
Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 01, 01, 25]
|
Raw bytes (9): 0x[01, 01, 00, 01, 00, 11, 01, 01, 25]
|
||||||
Number of files: 1
|
Number of files: 1
|
||||||
- file 0 => global file 1
|
- file 0 => global file 1
|
||||||
Number of expressions: 0
|
Number of expressions: 0
|
||||||
Number of file 0 mappings: 1
|
Number of file 0 mappings: 1
|
||||||
- Code(Counter(0)) at (prev + 17, 1) to (start + 1, 37)
|
- Code(Zero) at (prev + 17, 1) to (start + 1, 37)
|
||||||
|
|
||||||
Function name: unreachable::unreachable_intrinsic (unused)
|
Function name: unreachable::unreachable_intrinsic (unused)
|
||||||
Raw bytes (9): 0x[01, 01, 00, 01, 00, 16, 01, 01, 2c]
|
Raw bytes (9): 0x[01, 01, 00, 01, 00, 16, 01, 01, 2c]
|
||||||
|
@ -11,8 +11,6 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
|
|||||||
}
|
}
|
||||||
scope 3 {
|
scope 3 {
|
||||||
scope 4 (inlined unreachable_unchecked) {
|
scope 4 (inlined unreachable_unchecked) {
|
||||||
let mut _3: bool;
|
|
||||||
let _4: ();
|
|
||||||
scope 5 {
|
scope 5 {
|
||||||
}
|
}
|
||||||
scope 6 (inlined core::ub_checks::check_language_ub) {
|
scope 6 (inlined core::ub_checks::check_language_ub) {
|
||||||
@ -26,23 +24,16 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
|
|||||||
bb0: {
|
bb0: {
|
||||||
StorageLive(_2);
|
StorageLive(_2);
|
||||||
_2 = discriminant(_1);
|
_2 = discriminant(_1);
|
||||||
switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3];
|
switchInt(move _2) -> [0: bb2, 1: bb1, otherwise: bb2];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
StorageLive(_3);
|
|
||||||
_3 = UbChecks();
|
|
||||||
assume(_3);
|
|
||||||
_4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb2: {
|
|
||||||
_0 = ((_1 as Some).0: T);
|
_0 = ((_1 as Some).0: T);
|
||||||
StorageDead(_2);
|
StorageDead(_2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb3: {
|
bb2: {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,6 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
|
|||||||
}
|
}
|
||||||
scope 3 {
|
scope 3 {
|
||||||
scope 4 (inlined unreachable_unchecked) {
|
scope 4 (inlined unreachable_unchecked) {
|
||||||
let mut _3: bool;
|
|
||||||
let _4: ();
|
|
||||||
scope 5 {
|
scope 5 {
|
||||||
}
|
}
|
||||||
scope 6 (inlined core::ub_checks::check_language_ub) {
|
scope 6 (inlined core::ub_checks::check_language_ub) {
|
||||||
@ -26,23 +24,16 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
|
|||||||
bb0: {
|
bb0: {
|
||||||
StorageLive(_2);
|
StorageLive(_2);
|
||||||
_2 = discriminant(_1);
|
_2 = discriminant(_1);
|
||||||
switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3];
|
switchInt(move _2) -> [0: bb2, 1: bb1, otherwise: bb2];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
StorageLive(_3);
|
|
||||||
_3 = UbChecks();
|
|
||||||
assume(_3);
|
|
||||||
_4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb2: {
|
|
||||||
_0 = ((_1 as Some).0: T);
|
_0 = ((_1 as Some).0: T);
|
||||||
StorageDead(_2);
|
StorageDead(_2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb3: {
|
bb2: {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
tests/mir-opt/instsimplify/ub_check.rs
Normal file
16
tests/mir-opt/instsimplify/ub_check.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//@ unit-test: InstSimplify
|
||||||
|
//@ compile-flags: -Cdebug-assertions=no -Zinline-mir
|
||||||
|
|
||||||
|
// EMIT_MIR ub_check.unwrap_unchecked.InstSimplify.diff
|
||||||
|
pub fn unwrap_unchecked(x: Option<i32>) -> i32 {
|
||||||
|
// CHECK-LABEL: fn unwrap_unchecked(
|
||||||
|
// CHECK-NOT: UbChecks()
|
||||||
|
// CHECK: [[assume:_.*]] = const false;
|
||||||
|
// CHECK-NEXT: assume([[assume]]);
|
||||||
|
// CHECK-NEXT: unreachable_unchecked::precondition_check
|
||||||
|
unsafe { x.unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
unwrap_unchecked(None);
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
- // MIR for `unwrap_unchecked` before InstSimplify
|
||||||
|
+ // MIR for `unwrap_unchecked` after InstSimplify
|
||||||
|
|
||||||
|
fn unwrap_unchecked(_1: Option<i32>) -> i32 {
|
||||||
|
debug x => _1;
|
||||||
|
let mut _0: i32;
|
||||||
|
let mut _2: std::option::Option<i32>;
|
||||||
|
scope 1 {
|
||||||
|
scope 2 (inlined #[track_caller] Option::<i32>::unwrap_unchecked) {
|
||||||
|
debug self => _2;
|
||||||
|
let mut _3: isize;
|
||||||
|
scope 3 {
|
||||||
|
debug val => _0;
|
||||||
|
}
|
||||||
|
scope 4 {
|
||||||
|
scope 5 (inlined unreachable_unchecked) {
|
||||||
|
let mut _4: bool;
|
||||||
|
let _5: ();
|
||||||
|
scope 6 {
|
||||||
|
}
|
||||||
|
scope 7 (inlined core::ub_checks::check_language_ub) {
|
||||||
|
scope 8 (inlined core::ub_checks::check_language_ub::runtime) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_2);
|
||||||
|
_2 = _1;
|
||||||
|
StorageLive(_3);
|
||||||
|
StorageLive(_5);
|
||||||
|
_3 = discriminant(_2);
|
||||||
|
switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
StorageLive(_4);
|
||||||
|
- _4 = UbChecks();
|
||||||
|
+ _4 = const false;
|
||||||
|
assume(_4);
|
||||||
|
_5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3: {
|
||||||
|
_0 = move ((_2 as Some).0: i32);
|
||||||
|
StorageDead(_5);
|
||||||
|
StorageDead(_3);
|
||||||
|
StorageDead(_2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,8 +5,6 @@ fn ub_if_b(_1: Thing) -> Thing {
|
|||||||
let mut _0: Thing;
|
let mut _0: Thing;
|
||||||
let mut _2: isize;
|
let mut _2: isize;
|
||||||
scope 1 (inlined unreachable_unchecked) {
|
scope 1 (inlined unreachable_unchecked) {
|
||||||
let mut _3: bool;
|
|
||||||
let _4: ();
|
|
||||||
scope 2 {
|
scope 2 {
|
||||||
}
|
}
|
||||||
scope 3 (inlined core::ub_checks::check_language_ub) {
|
scope 3 (inlined core::ub_checks::check_language_ub) {
|
||||||
@ -17,7 +15,7 @@ fn ub_if_b(_1: Thing) -> Thing {
|
|||||||
|
|
||||||
bb0: {
|
bb0: {
|
||||||
_2 = discriminant(_1);
|
_2 = discriminant(_1);
|
||||||
switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3];
|
switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb2];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
@ -26,13 +24,6 @@ fn ub_if_b(_1: Thing) -> Thing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
bb2: {
|
||||||
StorageLive(_3);
|
|
||||||
_3 = UbChecks();
|
|
||||||
assume(_3);
|
|
||||||
_4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb3: {
|
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user