From 6c2d875261e10105dcfcbd9be84d05a730edd235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 2 May 2018 08:02:57 +0200 Subject: [PATCH 1/3] Make &Slice a thin pointer --- src/libarena/lib.rs | 59 ++++++++++-------- src/librustc/lib.rs | 1 + src/librustc/ty/context.rs | 27 ++++---- src/librustc/ty/mod.rs | 93 +++++++++++++++++++++++++--- src/test/mir-opt/basic_assignment.rs | 2 +- 5 files changed, 134 insertions(+), 48 deletions(-) diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index f7143a4f98155..b6a81596d06cc 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -314,8 +314,7 @@ impl DroplessArena { false } - fn align_for(&self) { - let align = mem::align_of::(); + fn align(&self, align: usize) { let final_address = ((self.ptr.get() as usize) + align - 1) & !(align - 1); self.ptr.set(final_address as *mut u8); assert!(self.ptr <= self.end); @@ -323,8 +322,7 @@ impl DroplessArena { #[inline(never)] #[cold] - fn grow(&self, n: usize) { - let needed_bytes = n * mem::size_of::(); + fn grow(&self, needed_bytes: usize) { unsafe { let mut chunks = self.chunks.borrow_mut(); let (chunk, mut new_capacity); @@ -356,25 +354,38 @@ impl DroplessArena { } #[inline] - pub fn alloc(&self, object: T) -> &mut T { + pub fn alloc_raw(&self, bytes: usize, align: usize) -> &mut [u8] { unsafe { - assert!(!mem::needs_drop::()); - assert!(mem::size_of::() != 0); + assert!(bytes != 0); + + self.align(align); - self.align_for::(); - let future_end = intrinsics::arith_offset(self.ptr.get(), mem::size_of::() as isize); + let future_end = intrinsics::arith_offset(self.ptr.get(), bytes as isize); if (future_end as *mut u8) >= self.end.get() { - self.grow::(1) + self.grow(bytes); } let ptr = self.ptr.get(); // Set the pointer past ourselves self.ptr.set( - intrinsics::arith_offset(self.ptr.get(), mem::size_of::() as isize) as *mut u8, + intrinsics::arith_offset(self.ptr.get(), bytes as isize) as *mut u8, ); + slice::from_raw_parts_mut(ptr, bytes) + } + } + + #[inline] + pub fn alloc(&self, object: T) -> &mut T { + assert!(!mem::needs_drop::()); + + let mem = self.alloc_raw( + mem::size_of::(), + mem::align_of::()) as *mut _ as *mut T; + + unsafe { // Write into uninitialized memory. - ptr::write(ptr as *mut T, object); - &mut *(ptr as *mut T) + ptr::write(mem, object); + &mut *mem } } @@ -393,21 +404,13 @@ impl DroplessArena { assert!(!mem::needs_drop::()); assert!(mem::size_of::() != 0); assert!(slice.len() != 0); - self.align_for::(); - let future_end = unsafe { - intrinsics::arith_offset(self.ptr.get(), (slice.len() * mem::size_of::()) as isize) - }; - if (future_end as *mut u8) >= self.end.get() { - self.grow::(slice.len()); - } + let mem = self.alloc_raw( + slice.len() * mem::size_of::(), + mem::align_of::()) as *mut _ as *mut T; unsafe { - let arena_slice = slice::from_raw_parts_mut(self.ptr.get() as *mut T, slice.len()); - self.ptr.set(intrinsics::arith_offset( - self.ptr.get(), - (slice.len() * mem::size_of::()) as isize, - ) as *mut u8); + let arena_slice = slice::from_raw_parts_mut(mem, slice.len()); arena_slice.copy_from_slice(slice); arena_slice } @@ -464,6 +467,12 @@ impl SyncDroplessArena { self.lock.lock().in_arena(ptr) } + #[inline(always)] + pub fn alloc_raw(&self, bytes: usize, align: usize) -> &mut [u8] { + // Extend the lifetime of the result since it's limited to the lock guard + unsafe { &mut *(self.lock.lock().alloc_raw(bytes, align) as *mut [u8]) } + } + #[inline(always)] pub fn alloc(&self, object: T) -> &mut T { // Extend the lifetime of the result since it's limited to the lock guard diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 486ea93588cc6..e8402487c7dd8 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -54,6 +54,7 @@ #![feature(macro_vis_matcher)] #![feature(never_type)] #![feature(exhaustive_patterns)] +#![feature(extern_types)] #![feature(non_exhaustive)] #![feature(proc_macro_internals)] #![feature(quote)] diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 8a73219cf7091..3580926d8ad23 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2056,9 +2056,8 @@ for Interned<'tcx, Slice>> { macro_rules! intern_method { ($lt_tcx:tt, $name:ident: $method:ident($alloc:ty, - $alloc_method:ident, + $alloc_method:expr, $alloc_to_key:expr, - $alloc_to_ret:expr, $keep_in_local_tcx:expr) -> $ty:ty) => { impl<'a, 'gcx, $lt_tcx> TyCtxt<'a, 'gcx, $lt_tcx> { pub fn $method(self, v: $alloc) -> &$lt_tcx $ty { @@ -2081,7 +2080,7 @@ macro_rules! intern_method { v); } - let i = ($alloc_to_ret)(self.interners.arena.$alloc_method(v)); + let i = $alloc_method(&self.interners.arena, v); interner.insert(Interned(i)); i } else { @@ -2094,7 +2093,9 @@ macro_rules! intern_method { let v = unsafe { mem::transmute(v) }; - let i = ($alloc_to_ret)(self.global_interners.arena.$alloc_method(v)); + let i: &$lt_tcx $ty = $alloc_method(&self.global_interners.arena, v); + // Cast to 'gcx + let i = unsafe { mem::transmute(i) }; interner.insert(Interned(i)); i } @@ -2121,8 +2122,10 @@ macro_rules! direct_interners { intern_method!( $lt_tcx, - $name: $method($ty, alloc, |x| x, |x| x, $keep_in_local_tcx) -> $ty - );)+ + $name: $method($ty, + |a: &$lt_tcx SyncDroplessArena, v| -> &$lt_tcx $ty { a.alloc(v) }, + |x| x, + $keep_in_local_tcx) -> $ty);)+ } } @@ -2137,10 +2140,11 @@ direct_interners!('tcx, macro_rules! slice_interners { ($($field:ident: $method:ident($ty:ident)),+) => ( - $(intern_method!('tcx, $field: $method(&[$ty<'tcx>], alloc_slice, Deref::deref, - |xs: &[$ty]| -> &Slice<$ty> { - unsafe { mem::transmute(xs) } - }, |xs: &[$ty]| xs.iter().any(keep_local)) -> Slice<$ty<'tcx>>);)+ + $(intern_method!( 'tcx, $field: $method( + &[$ty<'tcx>], + |a, v| Slice::from_arena(a, v), + Deref::deref, + |xs: &[$ty]| xs.iter().any(keep_local)) -> Slice<$ty<'tcx>>);)+ ) } @@ -2162,9 +2166,8 @@ intern_method! { 'tcx, canonical_var_infos: _intern_canonical_var_infos( &[CanonicalVarInfo], - alloc_slice, + |a, v| Slice::from_arena(a, v), Deref::deref, - |xs: &[CanonicalVarInfo]| -> &Slice { unsafe { mem::transmute(xs) } }, |_xs: &[CanonicalVarInfo]| -> bool { false } ) -> Slice } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 115c6442db57d..a4ca0c4170151 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -36,12 +36,14 @@ use ty::util::{IntTypeExt, Discr}; use ty::walk::TypeWalker; use util::captures::Captures; use util::nodemap::{NodeSet, DefIdMap, FxHashMap}; +use arena::SyncDroplessArena; use serialize::{self, Encodable, Encoder}; use std::cell::RefCell; use std::cmp::{self, Ordering}; use std::fmt; use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; use std::ops::Deref; use rustc_data_structures::sync::Lrc; use std::slice; @@ -582,18 +584,76 @@ impl <'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for Ty<'tcx> { } } +extern { + /// A dummy type used to force Slice to by unsized without requiring fat pointers + type OpaqueSliceContents; +} + /// A wrapper for slices with the additional invariant /// that the slice is interned and no other slice with /// the same contents can exist in the same context. /// This means we can use pointer + length for both /// equality comparisons and hashing. -#[derive(Debug, RustcEncodable)] -pub struct Slice([T]); +pub struct Slice(PhantomData, OpaqueSliceContents); + +impl Slice { + /// Returns the offset of the array + #[inline(always)] + fn offset() -> usize { + // Align up the size of the len (usize) field + let align = mem::align_of::(); + let align_mask = align - 1; + let offset = mem::size_of::(); + (offset + align_mask) & !align_mask + } +} + +impl Slice { + #[inline] + fn from_arena<'tcx>(arena: &'tcx SyncDroplessArena, slice: &[T]) -> &'tcx Slice { + assert!(!mem::needs_drop::()); + assert!(mem::size_of::() != 0); + assert!(slice.len() != 0); + + let offset = Slice::::offset(); + let size = offset + slice.len() * mem::size_of::(); + + let mem: *mut u8 = arena.alloc_raw( + size, + cmp::max(mem::align_of::(), mem::align_of::())).as_mut_ptr(); + + unsafe { + // Write the length + *(mem as *mut usize) = slice.len(); + + // Write the elements + let arena_slice = slice::from_raw_parts_mut( + mem.offset(offset as isize) as *mut T, + slice.len()); + arena_slice.copy_from_slice(slice); + + &*(mem as *const Slice) + } + } +} + +impl fmt::Debug for Slice { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (**self).fmt(f) + } +} + +impl Encodable for Slice { + #[inline] + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} impl Ord for Slice where T: Ord { fn cmp(&self, other: &Slice) -> Ordering { if self == other { Ordering::Equal } else { - <[T] as Ord>::cmp(&self.0, &other.0) + <[T] as Ord>::cmp(&**self, &**other) } } } @@ -601,35 +661,43 @@ impl Ord for Slice where T: Ord { impl PartialOrd for Slice where T: PartialOrd { fn partial_cmp(&self, other: &Slice) -> Option { if self == other { Some(Ordering::Equal) } else { - <[T] as PartialOrd>::partial_cmp(&self.0, &other.0) + <[T] as PartialOrd>::partial_cmp(&**self, &**other) } } } -impl PartialEq for Slice { +impl PartialEq for Slice { #[inline] fn eq(&self, other: &Slice) -> bool { - (&self.0 as *const [T]) == (&other.0 as *const [T]) + (self as *const _) == (other as *const _) } } -impl Eq for Slice {} +impl Eq for Slice {} impl Hash for Slice { + #[inline] fn hash(&self, s: &mut H) { - (self.as_ptr(), self.len()).hash(s) + (self as *const Slice).hash(s) } } impl Deref for Slice { type Target = [T]; + #[inline(always)] fn deref(&self) -> &[T] { - &self.0 + unsafe { + let raw = self as *const _ as *const u8; + let len = *(raw as *const usize); + let slice = raw.offset(Slice::::offset() as isize); + slice::from_raw_parts(slice as *const T, len) + } } } impl<'a, T> IntoIterator for &'a Slice { type Item = &'a T; type IntoIter = <&'a [T] as IntoIterator>::IntoIter; + #[inline(always)] fn into_iter(self) -> Self::IntoIter { self[..].iter() } @@ -638,9 +706,14 @@ impl<'a, T> IntoIterator for &'a Slice { impl<'tcx> serialize::UseSpecializedDecodable for &'tcx Slice> {} impl Slice { + #[inline(always)] pub fn empty<'a>() -> &'a Slice { + #[repr(align(64), C)] + struct EmptySlice([u8; 64]); + static EMPTY_SLICE: EmptySlice = EmptySlice([0; 64]); + assert!(mem::align_of::() <= 64); unsafe { - mem::transmute(slice::from_raw_parts(0x1 as *const T, 0)) + &*(&EMPTY_SLICE as *const _ as *const Slice) } } } diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs index 3c236ddcf0409..54b7a3821caad 100644 --- a/src/test/mir-opt/basic_assignment.rs +++ b/src/test/mir-opt/basic_assignment.rs @@ -48,7 +48,7 @@ fn main() { // _2 = move _3; // StorageDead(_3); // StorageLive(_4); -// UserAssertTy(Canonical { variables: Slice([]), value: std::option::Option> }, _4); +// UserAssertTy(Canonical { variables: [], value: std::option::Option> }, _4); // _4 = std::option::Option>::None; // StorageLive(_5); // StorageLive(_6); From 0b6586b989053b98f098928f79e95f3e30b3899c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sun, 27 May 2018 17:13:15 +0200 Subject: [PATCH 2/3] Add assertions for TyS and TypeVariants sizes --- src/librustc/ty/context.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 3580926d8ad23..2b2da6f842bb6 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -794,6 +794,12 @@ impl<'a, 'gcx> HashStable> for TypeckTables<'gcx> { impl<'tcx> CommonTypes<'tcx> { fn new(interners: &CtxtInterners<'tcx>) -> CommonTypes<'tcx> { + // Ensure our type representation does not grow + #[cfg(target_pointer_width = "64")] + assert!(mem::size_of::() <= 24); + #[cfg(target_pointer_width = "64")] + assert!(mem::size_of::() <= 32); + let mk = |sty| CtxtInterners::intern_ty(interners, interners, sty); let mk_region = |r| { if let Some(r) = interners.region.borrow().get(&r) { From fb4e3b62e05555815b7e2b55383b853c472c3712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 24 May 2018 13:07:27 +0200 Subject: [PATCH 3/3] Add fields to Slice --- src/librustc/ty/mod.rs | 46 +++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index a4ca0c4170151..775c7c234fda1 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -43,7 +43,6 @@ use std::cell::RefCell; use std::cmp::{self, Ordering}; use std::fmt; use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; use std::ops::Deref; use rustc_data_structures::sync::Lrc; use std::slice; @@ -592,20 +591,13 @@ extern { /// A wrapper for slices with the additional invariant /// that the slice is interned and no other slice with /// the same contents can exist in the same context. -/// This means we can use pointer + length for both +/// This means we can use pointer for both /// equality comparisons and hashing. -pub struct Slice(PhantomData, OpaqueSliceContents); - -impl Slice { - /// Returns the offset of the array - #[inline(always)] - fn offset() -> usize { - // Align up the size of the len (usize) field - let align = mem::align_of::(); - let align_mask = align - 1; - let offset = mem::size_of::(); - (offset + align_mask) & !align_mask - } +#[repr(C)] +pub struct Slice { + len: usize, + data: [T; 0], + opaque: OpaqueSliceContents, } impl Slice { @@ -615,24 +607,27 @@ impl Slice { assert!(mem::size_of::() != 0); assert!(slice.len() != 0); - let offset = Slice::::offset(); + // Align up the size of the len (usize) field + let align = mem::align_of::(); + let align_mask = align - 1; + let offset = mem::size_of::(); + let offset = (offset + align_mask) & !align_mask; + let size = offset + slice.len() * mem::size_of::(); - let mem: *mut u8 = arena.alloc_raw( + let mem = arena.alloc_raw( size, - cmp::max(mem::align_of::(), mem::align_of::())).as_mut_ptr(); - + cmp::max(mem::align_of::(), mem::align_of::())); unsafe { + let result = &mut *(mem.as_mut_ptr() as *mut Slice); // Write the length - *(mem as *mut usize) = slice.len(); + result.len = slice.len(); // Write the elements - let arena_slice = slice::from_raw_parts_mut( - mem.offset(offset as isize) as *mut T, - slice.len()); + let arena_slice = slice::from_raw_parts_mut(result.data.as_mut_ptr(), result.len); arena_slice.copy_from_slice(slice); - &*(mem as *const Slice) + result } } } @@ -686,10 +681,7 @@ impl Deref for Slice { #[inline(always)] fn deref(&self) -> &[T] { unsafe { - let raw = self as *const _ as *const u8; - let len = *(raw as *const usize); - let slice = raw.offset(Slice::::offset() as isize); - slice::from_raw_parts(slice as *const T, len) + slice::from_raw_parts(self.data.as_ptr(), self.len) } } }