about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRémy Rakic <remy.rakic+github@gmail.com>2022-05-31 12:36:48 +0200
committerRémy Rakic <remy.rakic+github@gmail.com>2022-06-28 22:09:28 +0200
commit97a0b2e2d0816bcf935b7d925f8f1d4225f7def7 (patch)
treea208e171052803518458d407ea351febf779c697
parent94e93749ab00539a11e90426ea87382c433530a8 (diff)
downloadrust-97a0b2e2d0816bcf935b7d925f8f1d4225f7def7.tar.gz
rust-97a0b2e2d0816bcf935b7d925f8f1d4225f7def7.zip
ctfe interning: don't walk allocations that don't need it
The interning of const allocations visits the mplace looking for references
to intern. Walking big aggregates like big static arrays can be costly,
so we only do it if the allocation we're interning contains references
or interior mutability.

Walking ZSTs was avoided before, and this optimization is now applied
to cases where there are no references/relocations either.
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs23
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs5
2 files changed, 25 insertions, 3 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index 1fda60c021e..808e7db5ae3 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -168,10 +168,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
         mplace: &MPlaceTy<'tcx>,
         fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>,
     ) -> InterpResult<'tcx> {
-        // ZSTs cannot contain pointers, so we can skip them.
-        if mplace.layout.is_zst() {
+        // We want to walk the aggregate to look for reference types to intern. While doing that we
+        // also need to take special care of interior mutability.
+        //
+        // As an optimization, however, if the allocation does not contain any pointers: we don't
+        // need to do the walk. It can be costly for big arrays for example (e.g. issue #93215).
+
+        let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else {
+            // We could be dealing with an extern type here in the future, so we do the regular
+            // walk.
+            return self.walk_aggregate(mplace, fields);
+        };
+
+        let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? else {
+            // ZSTs cannot contain pointers, so we can skip them.
             return Ok(());
-        }
+        };
 
         if let Some(def) = mplace.layout.ty.ty_adt_def() {
             if Some(def.did()) == self.ecx.tcx.lang_items().unsafe_cell_type() {
@@ -186,6 +198,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
             }
         }
 
+        if !alloc.has_relocations() {
+            // There are no refs or relocations in this allocation, we can skip the interning walk.
+            return Ok(());
+        }
+
         self.walk_aggregate(mplace, fields)
     }
 
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index d5e68dbd5b7..fb82406ce82 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -942,6 +942,11 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> {
             .check_bytes(&self.tcx, self.range.subrange(range), allow_uninit, allow_ptr)
             .map_err(|e| e.to_interp_error(self.alloc_id))?)
     }
+
+    /// Returns whether the allocation has relocations for the entire range of the `AllocRef`.
+    pub(crate) fn has_relocations(&self) -> bool {
+        !self.alloc.get_relocations(&self.tcx, self.range).is_empty()
+    }
 }
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {