Skip to content

Commit a325780

Browse files
committed
auto merge of #15406 : luqmana/rust/nop, r=pcwalton
Extend the null ptr optimization to work with slices, closures, procs, & trait objects by using the internal pointers as the discriminant. This decreases the size of `Option<&[int]>` (and similar) by one word.
2 parents 00cdd63 + fa8da9d commit a325780

File tree

7 files changed

+185
-77
lines changed

7 files changed

+185
-77
lines changed

src/libcollections/vec.rs

+12-17
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ use {Collection, Mutable};
2727
use slice::{MutableOrdVector, MutableVectorAllocating, CloneableVector};
2828
use slice::{Items, MutItems};
2929

30+
31+
#[doc(hidden)]
32+
pub static PTR_MARKER: u8 = 0;
33+
3034
/// An owned, growable vector.
3135
///
3236
/// # Examples
@@ -71,7 +75,11 @@ impl<T> Vec<T> {
7175
/// ```
7276
#[inline]
7377
pub fn new() -> Vec<T> {
74-
Vec { len: 0, cap: 0, ptr: 0 as *mut T }
78+
// We want ptr to never be NULL so instead we set it to some arbitrary
79+
// non-null value which is fine since we never call deallocate on the ptr
80+
// if cap is 0. The reason for this is because the pointer of a slice
81+
// being NULL would break the null pointer optimization for enums.
82+
Vec { len: 0, cap: 0, ptr: &PTR_MARKER as *const _ as *mut T }
7583
}
7684

7785
/// Constructs a new, empty `Vec` with the specified capacity.
@@ -88,7 +96,7 @@ impl<T> Vec<T> {
8896
#[inline]
8997
pub fn with_capacity(capacity: uint) -> Vec<T> {
9098
if mem::size_of::<T>() == 0 {
91-
Vec { len: 0, cap: uint::MAX, ptr: 0 as *mut T }
99+
Vec { len: 0, cap: uint::MAX, ptr: &PTR_MARKER as *const _ as *mut T }
92100
} else if capacity == 0 {
93101
Vec::new()
94102
} else {
@@ -1206,15 +1214,7 @@ impl<T> Vec<T> {
12061214
/// would also make any pointers to it invalid.
12071215
#[inline]
12081216
pub fn as_ptr(&self) -> *const T {
1209-
// If we have a 0-sized vector, then the base pointer should not be NULL
1210-
// because an iterator over the slice will attempt to yield the base
1211-
// pointer as the first element in the vector, but this will end up
1212-
// being Some(NULL) which is optimized to None.
1213-
if mem::size_of::<T>() == 0 {
1214-
1 as *const T
1215-
} else {
1216-
self.ptr as *const T
1217-
}
1217+
self.ptr as *const T
12181218
}
12191219

12201220
/// Returns a mutable unsafe pointer to the vector's buffer.
@@ -1226,12 +1226,7 @@ impl<T> Vec<T> {
12261226
/// would also make any pointers to it invalid.
12271227
#[inline]
12281228
pub fn as_mut_ptr(&mut self) -> *mut T {
1229-
// see above for the 0-size check
1230-
if mem::size_of::<T>() == 0 {
1231-
1 as *mut T
1232-
} else {
1233-
self.ptr
1234-
}
1229+
self.ptr
12351230
}
12361231

12371232
/// Retains only the elements specified by the predicate.

src/libcore/slice.rs

+24-12
Original file line numberDiff line numberDiff line change
@@ -884,17 +884,20 @@ macro_rules! iterator {
884884
if self.ptr == self.end {
885885
None
886886
} else {
887-
let old = self.ptr;
888-
self.ptr = if mem::size_of::<T>() == 0 {
887+
if mem::size_of::<T>() == 0 {
889888
// purposefully don't use 'ptr.offset' because for
890889
// vectors with 0-size elements this would return the
891890
// same pointer.
892-
transmute(self.ptr as uint + 1)
891+
self.ptr = transmute(self.ptr as uint + 1);
892+
893+
// Use a non-null pointer value
894+
Some(transmute(1u))
893895
} else {
894-
self.ptr.offset(1)
895-
};
896+
let old = self.ptr;
897+
self.ptr = self.ptr.offset(1);
896898

897-
Some(transmute(old))
899+
Some(transmute(old))
900+
}
898901
}
899902
}
900903
}
@@ -916,13 +919,17 @@ macro_rules! iterator {
916919
if self.end == self.ptr {
917920
None
918921
} else {
919-
self.end = if mem::size_of::<T>() == 0 {
922+
if mem::size_of::<T>() == 0 {
920923
// See above for why 'ptr.offset' isn't used
921-
transmute(self.end as uint - 1)
924+
self.end = transmute(self.end as uint - 1);
925+
926+
// Use a non-null pointer value
927+
Some(transmute(1u))
922928
} else {
923-
self.end.offset(-1)
924-
};
925-
Some(transmute(self.end))
929+
self.end = self.end.offset(-1);
930+
931+
Some(transmute(self.end))
932+
}
926933
}
927934
}
928935
}
@@ -956,7 +963,12 @@ impl<'a, T> RandomAccessIterator<&'a T> for Items<'a, T> {
956963
fn idx(&mut self, index: uint) -> Option<&'a T> {
957964
unsafe {
958965
if index < self.indexable() {
959-
transmute(self.ptr.offset(index as int))
966+
if mem::size_of::<T>() == 0 {
967+
// Use a non-null pointer value
968+
Some(transmute(1u))
969+
} else {
970+
Some(transmute(self.ptr.offset(index as int)))
971+
}
960972
} else {
961973
None
962974
}

src/librustc/middle/trans/adt.rs

+99-42
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub enum Repr {
111111
StructWrappedNullablePointer {
112112
pub nonnull: Struct,
113113
pub nndiscr: Disr,
114-
pub ptrfield: uint,
114+
pub ptrfield: PointerField,
115115
pub nullfields: Vec<ty::t>,
116116
}
117117
}
@@ -211,24 +211,21 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
211211
let mut discr = 0;
212212
while discr < 2 {
213213
if cases.get(1 - discr).is_zerolen(cx) {
214+
let st = mk_struct(cx, cases.get(discr).tys.as_slice(), false);
214215
match cases.get(discr).find_ptr() {
216+
Some(ThinPointer(_)) if st.fields.len() == 1 => {
217+
return RawNullablePointer {
218+
nndiscr: discr as Disr,
219+
nnty: *st.fields.get(0),
220+
nullfields: cases.get(1 - discr).tys.clone()
221+
};
222+
}
215223
Some(ptrfield) => {
216-
let st = mk_struct(cx, cases.get(discr).tys.as_slice(),
217-
false);
218-
219-
return if st.fields.len() == 1 {
220-
RawNullablePointer {
221-
nndiscr: discr as Disr,
222-
nnty: *st.fields.get(0),
223-
nullfields: cases.get(1 - discr).tys.clone()
224-
}
225-
} else {
226-
StructWrappedNullablePointer {
227-
nndiscr: discr as Disr,
228-
nonnull: st,
229-
ptrfield: ptrfield,
230-
nullfields: cases.get(1 - discr).tys.clone()
231-
}
224+
return StructWrappedNullablePointer {
225+
nndiscr: discr as Disr,
226+
nonnull: st,
227+
ptrfield: ptrfield,
228+
nullfields: cases.get(1 - discr).tys.clone()
232229
};
233230
}
234231
None => { }
@@ -283,23 +280,67 @@ pub fn is_ffi_safe(tcx: &ty::ctxt, def_id: ast::DefId) -> bool {
283280
}
284281

285282
// this should probably all be in ty
286-
struct Case { discr: Disr, tys: Vec<ty::t> }
283+
struct Case {
284+
discr: Disr,
285+
tys: Vec<ty::t>
286+
}
287+
288+
289+
#[deriving(Show)]
290+
pub enum PointerField {
291+
ThinPointer(uint),
292+
FatPointer(uint, uint)
293+
}
294+
287295
impl Case {
288296
fn is_zerolen(&self, cx: &CrateContext) -> bool {
289297
mk_struct(cx, self.tys.as_slice(), false).size == 0
290298
}
291-
fn find_ptr(&self) -> Option<uint> {
292-
self.tys.iter().position(|&ty| {
299+
fn find_ptr(&self) -> Option<PointerField> {
300+
use back::abi::{fn_field_code, slice_elt_base, trt_field_box};
301+
302+
for (i, &ty) in self.tys.iter().enumerate() {
293303
match ty::get(ty).sty {
294-
ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => match ty::get(ty).sty {
295-
ty::ty_vec(_, None) | ty::ty_str| ty::ty_trait(..) => false,
296-
_ => true,
304+
// &T/&mut T could either be a thin or fat pointer depending on T
305+
ty::ty_rptr(_, ty::mt { ty, .. }) => match ty::get(ty).sty {
306+
// &[T] and &str are a pointer and length pair
307+
ty::ty_vec(_, None) | ty::ty_str => return Some(FatPointer(i, slice_elt_base)),
308+
309+
// &Trait/&mut Trait are a pair of pointers: the actual object and a vtable
310+
ty::ty_trait(..) => return Some(FatPointer(i, trt_field_box)),
311+
312+
// Any other &T/&mut T is just a pointer
313+
_ => return Some(ThinPointer(i))
314+
},
315+
316+
// Box<T> could either be a thin or fat pointer depending on T
317+
ty::ty_uniq(t) => match ty::get(t).sty {
318+
// Box<[T]>/Box<str> might be FatPointer in a post DST world
319+
ty::ty_vec(_, None) | ty::ty_str => continue,
320+
321+
// Box<Trait> is a pair of pointers: the actual object and a vtable
322+
ty::ty_trait(..) => return Some(FatPointer(i, trt_field_box)),
323+
324+
// Any other Box<T> is just a pointer
325+
_ => return Some(ThinPointer(i))
297326
},
298-
ty::ty_box(..) | ty::ty_bare_fn(..) => true,
299-
// Is that everything? Would closures or slices qualify?
300-
_ => false
327+
328+
// Gc<T> is just a pointer
329+
ty::ty_box(..) => return Some(ThinPointer(i)),
330+
331+
// Functions are just pointers
332+
ty::ty_bare_fn(..) => return Some(ThinPointer(i)),
333+
334+
// Closures are a pair of pointers: the code and environment
335+
ty::ty_closure(..) => return Some(FatPointer(i, fn_field_code)),
336+
337+
// Anything else is not a pointer
338+
_ => continue
339+
301340
}
302-
})
341+
}
342+
343+
None
303344
}
304345
}
305346

@@ -552,8 +593,8 @@ pub fn trans_get_discr(bcx: &Block, r: &Repr, scrutinee: ValueRef, cast_to: Opti
552593
val = ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty));
553594
signed = false;
554595
}
555-
StructWrappedNullablePointer { nonnull: ref nonnull, nndiscr, ptrfield, .. } => {
556-
val = struct_wrapped_nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee);
596+
StructWrappedNullablePointer { nndiscr, ptrfield, .. } => {
597+
val = struct_wrapped_nullable_bitdiscr(bcx, nndiscr, ptrfield, scrutinee);
557598
signed = false;
558599
}
559600
}
@@ -563,12 +604,15 @@ pub fn trans_get_discr(bcx: &Block, r: &Repr, scrutinee: ValueRef, cast_to: Opti
563604
}
564605
}
565606

566-
fn struct_wrapped_nullable_bitdiscr(bcx: &Block, nonnull: &Struct, nndiscr: Disr, ptrfield: uint,
607+
fn struct_wrapped_nullable_bitdiscr(bcx: &Block, nndiscr: Disr, ptrfield: PointerField,
567608
scrutinee: ValueRef) -> ValueRef {
568-
let llptr = Load(bcx, GEPi(bcx, scrutinee, [0, ptrfield]));
609+
let llptrptr = match ptrfield {
610+
ThinPointer(field) => GEPi(bcx, scrutinee, [0, field]),
611+
FatPointer(field, pair) => GEPi(bcx, scrutinee, [0, field, pair])
612+
};
613+
let llptr = Load(bcx, llptrptr);
569614
let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
570-
let llptrty = type_of::type_of(bcx.ccx(), *nonnull.fields.get(ptrfield));
571-
ICmp(bcx, cmp, llptr, C_null(llptrty))
615+
ICmp(bcx, cmp, llptr, C_null(val_ty(llptr)))
572616
}
573617

574618
/// Helper for cases where the discriminant is simply loaded.
@@ -655,9 +699,15 @@ pub fn trans_start_init(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr) {
655699
}
656700
StructWrappedNullablePointer { nonnull: ref nonnull, nndiscr, ptrfield, .. } => {
657701
if discr != nndiscr {
658-
let llptrptr = GEPi(bcx, val, [0, ptrfield]);
659-
let llptrty = type_of::type_of(bcx.ccx(),
660-
*nonnull.fields.get(ptrfield));
702+
let (llptrptr, llptrty) = match ptrfield {
703+
ThinPointer(field) =>
704+
(GEPi(bcx, val, [0, field]),
705+
type_of::type_of(bcx.ccx(), *nonnull.fields.get(field))),
706+
FatPointer(field, pair) => {
707+
let v = GEPi(bcx, val, [0, field, pair]);
708+
(v, val_ty(v).element_type())
709+
}
710+
};
661711
Store(bcx, C_null(llptrty), llptrptr)
662712
}
663713
}
@@ -925,7 +975,11 @@ pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef)
925975
}
926976
}
927977
StructWrappedNullablePointer { nndiscr, ptrfield, .. } => {
928-
if is_null(const_struct_field(ccx, val, ptrfield)) {
978+
let (idx, sub_idx) = match ptrfield {
979+
ThinPointer(field) => (field, None),
980+
FatPointer(field, pair) => (field, Some(pair))
981+
};
982+
if is_null(const_struct_field(ccx, val, idx, sub_idx)) {
929983
/* subtraction as uint is ok because nndiscr is either 0 or 1 */
930984
(1 - nndiscr) as Disr
931985
} else {
@@ -946,26 +1000,29 @@ pub fn const_get_field(ccx: &CrateContext, r: &Repr, val: ValueRef,
9461000
_discr: Disr, ix: uint) -> ValueRef {
9471001
match *r {
9481002
CEnum(..) => ccx.sess().bug("element access in C-like enum const"),
949-
Univariant(..) => const_struct_field(ccx, val, ix),
950-
General(..) => const_struct_field(ccx, val, ix + 1),
1003+
Univariant(..) => const_struct_field(ccx, val, ix, None),
1004+
General(..) => const_struct_field(ccx, val, ix + 1, None),
9511005
RawNullablePointer { .. } => {
9521006
assert_eq!(ix, 0);
9531007
val
9541008
}
955-
StructWrappedNullablePointer{ .. } => const_struct_field(ccx, val, ix)
1009+
StructWrappedNullablePointer{ .. } => const_struct_field(ccx, val, ix, None)
9561010
}
9571011
}
9581012

9591013
/// Extract field of struct-like const, skipping our alignment padding.
960-
fn const_struct_field(ccx: &CrateContext, val: ValueRef, ix: uint)
1014+
fn const_struct_field(ccx: &CrateContext, val: ValueRef, ix: uint, sub_idx: Option<uint>)
9611015
-> ValueRef {
9621016
// Get the ix-th non-undef element of the struct.
9631017
let mut real_ix = 0; // actual position in the struct
9641018
let mut ix = ix; // logical index relative to real_ix
9651019
let mut field;
9661020
loop {
9671021
loop {
968-
field = const_get_elt(ccx, val, [real_ix]);
1022+
field = match sub_idx {
1023+
Some(si) => const_get_elt(ccx, val, [real_ix, si as u32]),
1024+
None => const_get_elt(ccx, val, [real_ix])
1025+
};
9691026
if !is_undef(field) {
9701027
break;
9711028
}

src/librustc/middle/trans/consts.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -484,8 +484,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
484484
if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty.to_ref()) }
485485
else { llvm::LLVMConstFPToUI(v, llty.to_ref()) }
486486
}
487-
(expr::cast_enum, expr::cast_integral) |
488-
(expr::cast_enum, expr::cast_float) => {
487+
(expr::cast_enum, expr::cast_integral) => {
489488
let repr = adt::represent_type(cx, basety);
490489
let discr = adt::const_get_discrim(cx, &*repr, v);
491490
let iv = C_integral(cx.int_type, discr, false);

src/librustc/middle/trans/debuginfo.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -2152,8 +2152,12 @@ impl EnumMemberDescriptionFactory {
21522152
let null_variant_index = (1 - nndiscr) as uint;
21532153
let null_variant_ident = self.variants.get(null_variant_index).name;
21542154
let null_variant_name = token::get_ident(null_variant_ident);
2155+
let discrfield = match ptrfield {
2156+
adt::ThinPointer(field) => format!("{}", field),
2157+
adt::FatPointer(field, pair) => format!("{}${}", field, pair)
2158+
};
21552159
let union_member_name = format!("RUST$ENCODED$ENUM${}${}",
2156-
ptrfield,
2160+
discrfield,
21572161
null_variant_name);
21582162

21592163
// Create the (singleton) list of descriptions of union members.
@@ -2196,7 +2200,7 @@ impl VariantMemberDescriptionFactory {
21962200

21972201
enum EnumDiscriminantInfo {
21982202
RegularDiscriminant(DIType),
2199-
OptimizedDiscriminant(uint),
2203+
OptimizedDiscriminant(adt::PointerField),
22002204
NoDiscriminant
22012205
}
22022206

src/librustc/middle/trans/tvec.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,9 @@ pub fn trans_slice_vstore<'a>(
155155
let llcount = C_uint(ccx, count);
156156
let llfixed;
157157
if count == 0 {
158-
// Zero-length array: just use NULL as the data pointer
159-
llfixed = C_null(vt.llunit_ty.ptr_to());
158+
// Just create a zero-sized alloca to preserve
159+
// the non-null invariant of the inner slice ptr
160+
llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount);
160161
} else {
161162
// Make a fixed-length backing array and allocate it on the stack.
162163
llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount);

0 commit comments

Comments
 (0)