about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMoulins <arthur.heuillard@orange.fr>2023-06-27 00:47:27 +0200
committerMoulins <arthur.heuillard@orange.fr>2023-07-21 03:31:46 +0200
commit403f34b599fb3ed7abd71f3388da3c5061a2d84f (patch)
tree2a1db4f1a57cc5c374a1888e92540c7c22ce3783
parent4fb039ed6c069e0ba9ac6215422d4f61a705dc1c (diff)
downloadrust-403f34b599fb3ed7abd71f3388da3c5061a2d84f.tar.gz
rust-403f34b599fb3ed7abd71f3388da3c5061a2d84f.zip
Don't treat ref. fields with non-null niches as `dereferenceable_or_null`
-rw-r--r--compiler/rustc_codegen_gcc/src/type_of.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs4
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs49
-rw-r--r--compiler/rustc_target/src/abi/mod.rs6
4 files changed, 37 insertions, 25 deletions
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
index 84d57838512..a30bce0a313 100644
--- a/compiler/rustc_codegen_gcc/src/type_of.rs
+++ b/compiler/rustc_codegen_gcc/src/type_of.rs
@@ -339,7 +339,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
             return pointee;
         }
 
-        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
+        let assume_valid_ptr = true;
+        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
 
         cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
         result
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index 2dbd467cc84..29dd53ff763 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -411,8 +411,8 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
         if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) {
             return pointee;
         }
-
-        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
+        let assume_valid_ptr = true;
+        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
 
         cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
         result
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 328200d40ba..0aa10922542 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1036,6 +1036,9 @@ where
         this: TyAndLayout<'tcx>,
         cx: &C,
         offset: Size,
+        // If true, assume that pointers are either null or valid (according to their type),
+        // enabling extra optimizations.
+        mut assume_valid_ptr: bool,
     ) -> Option<PointeeInfo> {
         let tcx = cx.tcx();
         let param_env = cx.param_env();
@@ -1058,19 +1061,19 @@ where
                 // Freeze/Unpin queries, and can save time in the codegen backend (noalias
                 // attributes in LLVM have compile-time cost even in unoptimized builds).
                 let optimize = tcx.sess.opts.optimize != OptLevel::No;
-                let kind = match mt {
-                    hir::Mutability::Not => PointerKind::SharedRef {
+                let safe = match (assume_valid_ptr, mt) {
+                    (true, hir::Mutability::Not) => Some(PointerKind::SharedRef {
                         frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
-                    },
-                    hir::Mutability::Mut => PointerKind::MutableRef {
+                    }),
+                    (true, hir::Mutability::Mut) => Some(PointerKind::MutableRef {
                         unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
-                    },
+                    }),
+                    (false, _) => None,
                 };
-
                 tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
                     size: layout.size,
                     align: layout.align.abi,
-                    safe: Some(kind),
+                    safe,
                 })
             }
 
@@ -1079,20 +1082,21 @@ where
                     // Within the discriminant field, only the niche itself is
                     // always initialized, so we only check for a pointer at its
                     // offset.
-                    //
-                    // If the niche is a pointer, it's either valid (according
-                    // to its type), or null (which the niche field's scalar
-                    // validity range encodes). This allows using
-                    // `dereferenceable_or_null` for e.g., `Option<&T>`, and
-                    // this will continue to work as long as we don't start
-                    // using more niches than just null (e.g., the first page of
-                    // the address space, or unaligned pointers).
-                    // FIXME(reference_niches): well, the day has come...
                     Variants::Multiple {
-                        tag_encoding: TagEncoding::Niche { untagged_variant, .. },
+                        tag_encoding:
+                            TagEncoding::Niche {
+                                untagged_variant,
+                                niche_variants: ref variants,
+                                niche_start,
+                            },
                         tag_field,
                         ..
                     } if this.fields.offset(tag_field) == offset => {
+                        // We can only continue assuming pointer validity if the only possible
+                        // discriminant value is null. The null special-case is permitted by LLVM's
+                        // `dereferenceable_or_null`, and allow types like `Option<&T>` to benefit
+                        // from optimizations.
+                        assume_valid_ptr &= niche_start == 0 && variants.start() == variants.end();
                         Some(this.for_variant(cx, untagged_variant))
                     }
                     _ => Some(this),
@@ -1118,9 +1122,12 @@ where
                             result = field.to_result().ok().and_then(|field| {
                                 if ptr_end <= field_start + field.size {
                                     // We found the right field, look inside it.
-                                    let field_info =
-                                        field.pointee_info_at(cx, offset - field_start);
-                                    field_info
+                                    Self::ty_and_layout_pointee_info_at(
+                                        field,
+                                        cx,
+                                        offset - field_start,
+                                        assume_valid_ptr,
+                                    )
                                 } else {
                                     None
                                 }
@@ -1135,7 +1142,7 @@ where
                 // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
                 if let Some(ref mut pointee) = result {
                     if let ty::Adt(def, _) = this.ty.kind() {
-                        if def.is_box() && offset.bytes() == 0 {
+                        if assume_valid_ptr && def.is_box() && offset.bytes() == 0 {
                             let optimize = tcx.sess.opts.optimize != OptLevel::No;
                             pointee.safe = Some(PointerKind::Box {
                                 unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index 589cd3cf96b..752f10a74ac 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -50,6 +50,9 @@ pub trait TyAbiInterface<'a, C>: Sized {
         this: TyAndLayout<'a, Self>,
         cx: &C,
         offset: Size,
+        // If true, assume that pointers are either null or valid (according to their type),
+        // enabling extra optimizations.
+        assume_valid_ptr: bool,
     ) -> Option<PointeeInfo>;
     fn is_adt(this: TyAndLayout<'a, Self>) -> bool;
     fn is_never(this: TyAndLayout<'a, Self>) -> bool;
@@ -76,7 +79,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
     where
         Ty: TyAbiInterface<'a, C>,
     {
-        Ty::ty_and_layout_pointee_info_at(self, cx, offset)
+        let assume_valid_ptr = true;
+        Ty::ty_and_layout_pointee_info_at(self, cx, offset, assume_valid_ptr)
     }
 
     pub fn is_single_fp_element<C>(self, cx: &C) -> bool