]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/interpret/memory.rs
Correct a typo in interpret/memory.rs
[rust.git] / src / librustc_mir / interpret / memory.rs
index 8af1a8ac608ac8f2a56f4bb61d0c4d455a2a037b..7dfa913fd08bd07b45daf49c22a829de9313f11d 100644 (file)
@@ -14,6 +14,7 @@
 
 use rustc_ast::ast::Mutability;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::def_id::DefId;
 use rustc_middle::ty::{self, Instance, ParamEnv, TyCtxt};
 use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
 
@@ -118,6 +119,17 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
     pub tcx: TyCtxt<'tcx>,
 }
 
+/// Return the `tcx` allocation containing the initial value of the given static
+pub fn get_static(tcx: TyCtxt<'tcx>, def_id: DefId) -> InterpResult<'tcx, &'tcx Allocation> {
+    trace!("get_static: Need to compute {:?}", def_id);
+    let instance = Instance::mono(tcx, def_id);
+    let gid = GlobalId { instance, promoted: None };
+    // Use the raw query here to break validation cycles. Later uses of the static
+    // will call the full query anyway.
+    let raw_const = tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid))?;
+    Ok(tcx.global_alloc(raw_const.alloc_id).unwrap_memory())
+}
+
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for Memory<'mir, 'tcx, M> {
     #[inline]
     fn data_layout(&self) -> &TargetDataLayout {
@@ -137,15 +149,36 @@ pub fn new(tcx: TyCtxt<'tcx>, extra: M::MemoryExtra) -> Self {
     }
 
     /// Call this to turn untagged "global" pointers (obtained via `tcx`) into
-    /// the *canonical* machine pointer to the allocation.  Must never be used
-    /// for any other pointers!
+    /// the machine pointer to the allocation.  Must never be used
+    /// for any other pointers, nor for TLS statics.
+    ///
+    /// Using the resulting pointer represents a *direct* access to that memory
+    /// (e.g. by directly using a `static`),
+    /// as opposed to access through a pointer that was created by the program.
     ///
-    /// This represents a *direct* access to that memory, as opposed to access
-    /// through a pointer that was created by the program.
+    /// This function can fail only if `ptr` points to an `extern static`.
     #[inline]
-    pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
-        let id = M::canonical_alloc_id(self, ptr.alloc_id);
-        ptr.with_tag(M::tag_global_base_pointer(&self.extra, id))
+    pub fn global_base_pointer(
+        &self,
+        mut ptr: Pointer,
+    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
+        // We need to handle `extern static`.
+        let ptr = match self.tcx.get_global_alloc(ptr.alloc_id) {
+            Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => {
+                bug!("global memory cannot point to thread-local static")
+            }
+            Some(GlobalAlloc::Static(def_id)) if self.tcx.is_foreign_item(def_id) => {
+                ptr.alloc_id = M::extern_static_alloc_id(self, def_id)?;
+                ptr
+            }
+            _ => {
+                // No need to change the `AllocId`.
+                ptr
+            }
+        };
+        // And we need to get the tag.
+        let tag = M::tag_global_base_pointer(&self.extra, ptr.alloc_id);
+        Ok(ptr.with_tag(tag))
     }
 
     pub fn create_fn_alloc(
@@ -162,7 +195,9 @@ pub fn create_fn_alloc(
                 id
             }
         };
-        self.tag_global_base_pointer(Pointer::from(id))
+        // Functions are global allocations, so make sure we get the right base pointer.
+        // We know this is not an `extern static` so this cannot fail.
+        self.global_base_pointer(Pointer::from(id)).unwrap()
     }
 
     pub fn allocate(
@@ -171,7 +206,7 @@ pub fn allocate(
         align: Align,
         kind: MemoryKind<M::MemoryKind>,
     ) -> Pointer<M::PointerTag> {
-        let alloc = Allocation::undef(size, align);
+        let alloc = Allocation::uninit(size, align);
         self.allocate_with(alloc, kind)
     }
 
@@ -195,6 +230,7 @@ pub fn allocate_with(
             M::GLOBAL_KIND.map(MemoryKind::Machine),
             "dynamically allocating global memory"
         );
+        // This is a new allocation, not a new global one, so no `global_base_ptr`.
         let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind));
         self.alloc_map.insert(id, (kind, alloc.into_owned()));
         Pointer::from(id).with_tag(tag)
@@ -437,6 +473,7 @@ fn get_global_alloc(
             Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
             None => throw_ub!(PointerUseAfterFree(id)),
             Some(GlobalAlloc::Static(def_id)) => {
+                assert!(tcx.is_static(def_id));
                 assert!(!tcx.is_thread_local_static(def_id));
                 // Notice that every static has two `AllocId` that will resolve to the same
                 // thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID,
@@ -448,29 +485,11 @@ fn get_global_alloc(
                 // The `GlobalAlloc::Memory` branch here is still reachable though; when a static
                 // contains a reference to memory that was created during its evaluation (i.e., not
                 // to another static), those inner references only exist in "resolved" form.
-                //
-                // Assumes `id` is already canonical.
                 if tcx.is_foreign_item(def_id) {
-                    trace!("get_global_alloc: foreign item {:?}", def_id);
-                    throw_unsup!(ReadForeignStatic(def_id))
+                    throw_unsup!(ReadExternStatic(def_id));
                 }
-                trace!("get_global_alloc: Need to compute {:?}", def_id);
-                let instance = Instance::mono(tcx, def_id);
-                let gid = GlobalId { instance, promoted: None };
-                // Use the raw query here to break validation cycles. Later uses of the static
-                // will call the full query anyway.
-                let raw_const =
-                    tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| {
-                        // no need to report anything, the const_eval call takes care of that
-                        // for statics
-                        assert!(tcx.is_static(def_id));
-                        err
-                    })?;
-                // Make sure we use the ID of the resolved memory, not the lazy one!
-                let id = raw_const.alloc_id;
-                let allocation = tcx.global_alloc(id).unwrap_memory();
-
-                (allocation, Some(def_id))
+
+                (get_static(tcx, def_id)?, Some(def_id))
             }
         };
         M::before_access_global(memory_extra, id, alloc, def_id, is_write)?;
@@ -482,6 +501,7 @@ fn get_global_alloc(
             alloc,
             M::GLOBAL_KIND.map(MemoryKind::Machine),
         );
+        // Sanity check that this is the same pointer we would have gotten via `global_base_pointer`.
         debug_assert_eq!(tag, M::tag_global_base_pointer(memory_extra, id));
         Ok(alloc)
     }
@@ -492,7 +512,6 @@ pub fn get_raw(
         &self,
         id: AllocId,
     ) -> InterpResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> {
-        let id = M::canonical_alloc_id(self, id);
         // The error type of the inner closure here is somewhat funny.  We have two
         // ways of "erroring": An actual error, or because we got a reference from
         // `get_global_alloc` that we can actually use directly without inserting anything anywhere.
@@ -529,7 +548,6 @@ pub fn get_raw_mut(
         &mut self,
         id: AllocId,
     ) -> InterpResult<'tcx, &mut Allocation<M::PointerTag, M::AllocExtra>> {
-        let id = M::canonical_alloc_id(self, id);
         let tcx = self.tcx;
         let memory_extra = &self.extra;
         let a = self.alloc_map.get_mut_or(id, || {
@@ -568,7 +586,6 @@ pub fn get_size_and_align(
         id: AllocId,
         liveness: AllocCheck,
     ) -> InterpResult<'static, (Size, Align)> {
-        let id = M::canonical_alloc_id(self, id);
         // # Regular allocations
         // Don't use `self.get_raw` here as that will
         // a) cause cycles in case `id` refers to a static
@@ -621,7 +638,6 @@ pub fn get_size_and_align(
         }
     }
 
-    /// Assumes `id` is already canonical.
     fn get_fn_alloc(&self, id: AllocId) -> Option<FnVal<'tcx, M::ExtraFnVal>> {
         trace!("reading fn ptr: {}", id);
         if let Some(extra) = self.extra_fn_ptr_map.get(&id) {
@@ -642,8 +658,8 @@ pub fn get_fn(
         if ptr.offset.bytes() != 0 {
             throw_ub!(InvalidFunctionPointer(ptr.erase_tag()))
         }
-        let id = M::canonical_alloc_id(self, ptr.alloc_id);
-        self.get_fn_alloc(id).ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into())
+        self.get_fn_alloc(ptr.alloc_id)
+            .ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into())
     }
 
     pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
@@ -651,32 +667,84 @@ pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
         Ok(())
     }
 
-    /// Print an allocation and all allocations it points to, recursively.
-    /// This prints directly to stderr, ignoring RUSTC_LOG! It is up to the caller to
-    /// control for this.
-    pub fn dump_alloc(&self, id: AllocId) {
-        self.dump_allocs(vec![id]);
+    /// Create a lazy debug printer that prints the given allocation and all allocations it points
+    /// to, recursively.
+    #[must_use]
+    pub fn dump_alloc<'a>(&'a self, id: AllocId) -> DumpAllocs<'a, 'mir, 'tcx, M> {
+        self.dump_allocs(vec![id])
+    }
+
+    /// Create a lazy debug printer for a list of allocations and all allocations they point to,
+    /// recursively.
+    #[must_use]
+    pub fn dump_allocs<'a>(&'a self, mut allocs: Vec<AllocId>) -> DumpAllocs<'a, 'mir, 'tcx, M> {
+        allocs.sort();
+        allocs.dedup();
+        DumpAllocs { mem: self, allocs }
+    }
+
+    /// Print leaked memory. Allocations reachable from `static_roots` or a `Global` allocation
+    /// are not considered leaked. Leaks whose kind `may_leak()` returns true are not reported.
+    pub fn leak_report(&self, static_roots: &[AllocId]) -> usize {
+        // Collect the set of allocations that are *reachable* from `Global` allocations.
+        let reachable = {
+            let mut reachable = FxHashSet::default();
+            let global_kind = M::GLOBAL_KIND.map(MemoryKind::Machine);
+            let mut todo: Vec<_> = self.alloc_map.filter_map_collect(move |&id, &(kind, _)| {
+                if Some(kind) == global_kind { Some(id) } else { None }
+            });
+            todo.extend(static_roots);
+            while let Some(id) = todo.pop() {
+                if reachable.insert(id) {
+                    // This is a new allocation, add its relocations to `todo`.
+                    if let Some((_, alloc)) = self.alloc_map.get(id) {
+                        todo.extend(alloc.relocations().values().map(|&(_, target_id)| target_id));
+                    }
+                }
+            }
+            reachable
+        };
+
+        // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking.
+        let leaks: Vec<_> = self.alloc_map.filter_map_collect(|&id, &(kind, _)| {
+            if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) }
+        });
+        let n = leaks.len();
+        if n > 0 {
+            eprintln!("The following memory was leaked: {:?}", self.dump_allocs(leaks));
+        }
+        n
+    }
+
+    /// This is used by [priroda](https://github.com/oli-obk/priroda)
+    pub fn alloc_map(&self) -> &M::MemoryMap {
+        &self.alloc_map
     }
+}
 
-    /// Print a list of allocations and all allocations they point to, recursively.
-    /// This prints directly to stderr, ignoring RUSTC_LOG! It is up to the caller to
-    /// control for this.
-    pub fn dump_allocs(&self, mut allocs: Vec<AllocId>) {
+#[doc(hidden)]
+/// There's no way to use this directly, it's just a helper struct for the `dump_alloc(s)` methods.
+pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
+    mem: &'a Memory<'mir, 'tcx, M>,
+    allocs: Vec<AllocId>,
+}
+
+impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         // Cannot be a closure because it is generic in `Tag`, `Extra`.
         fn write_allocation_track_relocs<'tcx, Tag: Copy + fmt::Debug, Extra>(
+            fmt: &mut std::fmt::Formatter<'_>,
             tcx: TyCtxt<'tcx>,
             allocs_to_print: &mut VecDeque<AllocId>,
             alloc: &Allocation<Tag, Extra>,
-        ) {
+        ) -> std::fmt::Result {
             for &(_, target_id) in alloc.relocations().values() {
                 allocs_to_print.push_back(target_id);
             }
-            pretty::write_allocation(tcx, alloc, &mut std::io::stderr()).unwrap();
+            write!(fmt, "{}", pretty::display_allocation(tcx, alloc))
         }
 
-        allocs.sort();
-        allocs.dedup();
-        let mut allocs_to_print = VecDeque::from(allocs);
+        let mut allocs_to_print: VecDeque<_> = self.allocs.iter().copied().collect();
         // `allocs_printed` contains all allocations that we have already printed.
         let mut allocs_printed = FxHashSet::default();
 
@@ -686,70 +754,45 @@ fn write_allocation_track_relocs<'tcx, Tag: Copy + fmt::Debug, Extra>(
                 continue;
             }
 
-            eprint!("{}", id);
-            match self.alloc_map.get(id) {
+            write!(fmt, "{}", id)?;
+            match self.mem.alloc_map.get(id) {
                 Some(&(kind, ref alloc)) => {
                     // normal alloc
-                    eprint!(" ({}, ", kind);
-                    write_allocation_track_relocs(self.tcx, &mut allocs_to_print, alloc);
+                    write!(fmt, " ({}, ", kind)?;
+                    write_allocation_track_relocs(
+                        &mut *fmt,
+                        self.mem.tcx,
+                        &mut allocs_to_print,
+                        alloc,
+                    )?;
                 }
                 None => {
                     // global alloc
-                    match self.tcx.get_global_alloc(id) {
+                    match self.mem.tcx.get_global_alloc(id) {
                         Some(GlobalAlloc::Memory(alloc)) => {
-                            eprint!(" (unchanged global, ");
-                            write_allocation_track_relocs(self.tcx, &mut allocs_to_print, alloc);
+                            write!(fmt, " (unchanged global, ")?;
+                            write_allocation_track_relocs(
+                                &mut *fmt,
+                                self.mem.tcx,
+                                &mut allocs_to_print,
+                                alloc,
+                            )?;
                         }
                         Some(GlobalAlloc::Function(func)) => {
-                            eprint!(" (fn: {})", func);
+                            write!(fmt, " (fn: {})", func)?;
                         }
                         Some(GlobalAlloc::Static(did)) => {
-                            eprint!(" (static: {})", self.tcx.def_path_str(did));
+                            write!(fmt, " (static: {})", self.mem.tcx.def_path_str(did))?;
                         }
                         None => {
-                            eprint!(" (deallocated)");
+                            write!(fmt, " (deallocated)")?;
                         }
                     }
                 }
             }
-            eprintln!();
-        }
-    }
-
-    pub fn leak_report(&self) -> usize {
-        // Collect the set of allocations that are *reachable* from `Global` allocations.
-        let reachable = {
-            let mut reachable = FxHashSet::default();
-            let global_kind = M::GLOBAL_KIND.map(MemoryKind::Machine);
-            let mut todo: Vec<_> = self.alloc_map.filter_map_collect(move |&id, &(kind, _)| {
-                if Some(kind) == global_kind { Some(id) } else { None }
-            });
-            while let Some(id) = todo.pop() {
-                if reachable.insert(id) {
-                    // This is a new allocation, add its relocations to `todo`.
-                    if let Some((_, alloc)) = self.alloc_map.get(id) {
-                        todo.extend(alloc.relocations().values().map(|&(_, target_id)| target_id));
-                    }
-                }
-            }
-            reachable
-        };
-
-        // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking.
-        let leaks: Vec<_> = self.alloc_map.filter_map_collect(|&id, &(kind, _)| {
-            if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) }
-        });
-        let n = leaks.len();
-        if n > 0 {
-            eprintln!("The following memory was leaked:");
-            self.dump_allocs(leaks);
+            writeln!(fmt)?;
         }
-        n
-    }
-
-    /// This is used by [priroda](https://github.com/oli-obk/priroda)
-    pub fn alloc_map(&self) -> &M::MemoryMap {
-        &self.alloc_map
+        Ok(())
     }
 }
 
@@ -904,18 +947,18 @@ pub fn copy_repeatedly(
 
         let dest_bytes = dest_bytes.as_mut_ptr();
 
-        // Prepare a copy of the undef mask.
+        // Prepare a copy of the initialization mask.
         let compressed = self.get_raw(src.alloc_id)?.compress_undef_range(src, size);
 
-        if compressed.all_bytes_undef() {
-            // Fast path: If all bytes are `undef` then there is nothing to copy. The target range
-            // is marked as undef but we otherwise omit changing the byte representation which may
-            // be arbitrary for undef bytes.
+        if compressed.no_bytes_init() {
+            // Fast path: If all bytes are `uninit` then there is nothing to copy. The target range
+            // is marked as uninitialized but we otherwise omit changing the byte representation which may
+            // be arbitrary for uninitialized bytes.
             // This also avoids writing to the target bytes so that the backing allocation is never
-            // touched if the bytes stay undef for the whole interpreter execution. On contemporary
+            // touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary
             // operating system this can avoid physically allocating the page.
             let dest_alloc = self.get_raw_mut(dest.alloc_id)?;
-            dest_alloc.mark_definedness(dest, size * length, false); // `Size` multiplication
+            dest_alloc.mark_init(dest, size * length, false); // `Size` multiplication
             dest_alloc.mark_relocation_range(relocations);
             return Ok(());
         }
@@ -955,7 +998,7 @@ pub fn copy_repeatedly(
         }
 
         // now fill in all the data
-        self.get_raw_mut(dest.alloc_id)?.mark_compressed_undef_range(
+        self.get_raw_mut(dest.alloc_id)?.mark_compressed_init_range(
             &compressed,
             dest,
             size,