Skip to content

Commit f16bfd7

Browse files
author
David Rajchenbach-Teller
committed
Issue #5977 - RawNullablePointer can now optimize Option<char> and Option<bool> r?luqmana
1 parent 71817da commit f16bfd7

File tree

3 files changed

+89
-50
lines changed

3 files changed

+89
-50
lines changed

src/liblibc

src/librustc_trans/trans/adt.rs

+71-44
Original file line numberDiff line numberDiff line change
@@ -349,25 +349,28 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
349349

350350
// For the moment, we can only apply these
351351
// optimizations to safe pointers.
352-
match cases[discr].find_ptr(cx) {
353-
Some(ref df) if df.len() == 1 && st.fields.len() == 1 => {
352+
match cases[discr].find_forbidden_value(cx) {
353+
Some((ref df, forbidden_value))
354+
if df.len() == 1 && st.fields.len() == 1 => {
354355
let payload_ty = st.fields[0];
355356
return RawForbiddenValue {
356357
payload_discr: Disr::from(discr),
357358
payload_ty: payload_ty,
358-
forbidden_value: C_null(type_of::sizing_type_of(cx, payload_ty)),
359+
forbidden_value: forbidden_value,
359360
unit_fields: cases[1 - discr].tys.clone()
360361
};
361362
}
362-
Some(mut discrfield) => {
363-
discrfield.push(0);
364-
discrfield.reverse();
365-
return StructWrappedNullablePointer {
366-
nndiscr: Disr::from(discr),
367-
nonnull: st,
368-
discrfield: discrfield,
369-
nullfields: cases[1 - discr].tys.clone()
370-
};
363+
Some((mut discrfield, forbidden)) => {
364+
if is_null(forbidden) {
365+
discrfield.push(0);
366+
discrfield.reverse();
367+
return StructWrappedNullablePointer {
368+
nndiscr: Disr::from(discr),
369+
nonnull: st,
370+
discrfield: discrfield,
371+
nullfields: cases[1 - discr].tys.clone()
372+
};
373+
}
371374
}
372375
None => {}
373376
}
@@ -466,60 +469,73 @@ struct Case<'tcx> {
466469
/// This represents the (GEP) indices to follow to get to the discriminant field
467470
pub type DiscrField = Vec<usize>;
468471

469-
fn find_discr_field_candidate<'tcx>(tcx: &ty::ctxt<'tcx>,
472+
fn find_discr_field_candidate<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
470473
ty: Ty<'tcx>,
471-
mut path: DiscrField) -> Option<DiscrField> {
474+
mut path: DiscrField) -> Option<(DiscrField, ValueRef)> {
475+
let ref tcx = cx.tcx();
472476
match ty.sty {
473-
// Fat &T/&mut T/Box<T> i.e. T is [T], str, or Trait
474-
ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) if !type_is_sized(tcx, ty) => {
475-
path.push(FAT_PTR_ADDR);
476-
Some(path)
477-
},
478-
479-
// Regular thin pointer: &T/&mut T/Box<T>
480-
ty::TyRef(..) | ty::TyBox(..) => Some(path),
481-
482-
// Functions are just pointers
483-
ty::TyBareFn(..) => Some(path),
477+
// Fat &T/&mut T/Box<T> i.e. T is [T], str, or Trait: null is forbidden
478+
ty::TyRef(_, ty::TypeAndMut { ty: ty2, .. }) | ty::TyBox(ty2)
479+
if !type_is_sized(tcx, ty2) => {
480+
path.push(FAT_PTR_ADDR);
481+
Some((path, C_null(type_of::sizing_type_of(cx, ty))))
482+
},
483+
484+
// Regular thin pointer: &T/&mut T/Box<T>: null is forbidden
485+
ty::TyRef(..) | ty::TyBox(..) => Some((path, C_null(type_of::sizing_type_of(cx, ty)))),
486+
487+
// Functions are just pointers: null is forbidden
488+
ty::TyBareFn(..) => Some((path, C_null(type_of::sizing_type_of(cx, ty)))),
489+
490+
// Is this a char or a bool? If so, std::uXX:MAX is forbidden.
491+
ty::TyChar => Some((path,
492+
C_integral(type_of::sizing_type_of(cx, ty),
493+
std::u32::MAX as u64, false))),
494+
ty::TyBool => Some((path,
495+
C_integral(type_of::sizing_type_of(cx, ty),
496+
std::u8::MAX as u64, false))),
484497

485498
// Is this the NonZero lang item wrapping a pointer or integer type?
499+
// If so, null is forbidden.
486500
ty::TyStruct(def, substs) if Some(def.did) == tcx.lang_items.non_zero() => {
487501
let nonzero_fields = &def.struct_variant().fields;
488502
assert_eq!(nonzero_fields.len(), 1);
489503
let field_ty = monomorphize::field_ty(tcx, substs, &nonzero_fields[0]);
490504
match field_ty.sty {
491505
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) if !type_is_sized(tcx, ty) => {
492506
path.extend_from_slice(&[0, FAT_PTR_ADDR]);
493-
Some(path)
507+
Some((path, C_null(Type::i8p(cx))))
494508
},
495509
ty::TyRawPtr(..) | ty::TyInt(..) | ty::TyUint(..) => {
496510
path.push(0);
497-
Some(path)
511+
Some((path, C_null(type_of::sizing_type_of(cx, field_ty))))
498512
},
499513
_ => None
500514
}
501515
},
502516

503-
// Perhaps one of the fields of this struct is non-zero
517+
// Perhaps one of the fields of this struct has a forbidden value.
504518
// let's recurse and find out
505519
ty::TyStruct(def, substs) => {
506520
for (j, field) in def.struct_variant().fields.iter().enumerate() {
507521
let field_ty = monomorphize::field_ty(tcx, substs, field);
508-
if let Some(mut fpath) = find_discr_field_candidate(tcx, field_ty, path.clone()) {
522+
if let Some((mut fpath, forbidden)) =
523+
find_discr_field_candidate(cx, field_ty, path.clone()) {
509524
fpath.push(j);
510-
return Some(fpath);
525+
return Some((fpath, forbidden));
511526
}
512527
}
513528
None
514529
},
515530

516-
// Perhaps one of the upvars of this struct is non-zero
531+
// Perhaps one of the upvars of this struct has a forbidden value.
517532
// Let's recurse and find out!
518533
ty::TyClosure(_, ref substs) => {
519534
for (j, &ty) in substs.upvar_tys.iter().enumerate() {
520-
if let Some(mut fpath) = find_discr_field_candidate(tcx, ty, path.clone()) {
535+
if let Some((mut fpath, forbidden)) =
536+
find_discr_field_candidate(cx, ty, path.clone()) {
521537
fpath.push(j);
522-
return Some(fpath);
538+
return Some((fpath, forbidden));
523539
}
524540
}
525541
None
@@ -528,26 +544,27 @@ fn find_discr_field_candidate<'tcx>(tcx: &ty::ctxt<'tcx>,
528544
// Can we use one of the fields in this tuple?
529545
ty::TyTuple(ref tys) => {
530546
for (j, &ty) in tys.iter().enumerate() {
531-
if let Some(mut fpath) = find_discr_field_candidate(tcx, ty, path.clone()) {
547+
if let Some((mut fpath, forbidden)) =
548+
find_discr_field_candidate(cx, ty, path.clone()) {
532549
fpath.push(j);
533-
return Some(fpath);
550+
return Some((fpath, forbidden));
534551
}
535552
}
536553
None
537554
},
538555

539-
// Is this a fixed-size array of something non-zero
556+
// Is this a fixed-size array of something with a forbidden value
540557
// with at least one element?
541558
ty::TyArray(ety, d) if d > 0 => {
542-
if let Some(mut vpath) = find_discr_field_candidate(tcx, ety, path) {
559+
if let Some((mut vpath, forbidden)) = find_discr_field_candidate(cx, ety, path) {
543560
vpath.push(0);
544-
Some(vpath)
561+
Some((vpath, forbidden))
545562
} else {
546563
None
547564
}
548565
},
549566

550-
// Anything else is not a pointer
567+
// Anything else doesn't have a known-to-be-safe forbidden value.
551568
_ => None
552569
}
553570
}
@@ -557,13 +574,22 @@ impl<'tcx> Case<'tcx> {
557574
mk_struct(cx, &self.tys, false, scapegoat).size == 0
558575
}
559576

560-
/// Find a safe pointer that may be used to discriminate in a
577+
/// Find a forbidden value that may be used to discriminate in a
561578
/// RawForbiddenValue or StructWrappedNullablePointer.
562-
fn find_ptr<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Option<DiscrField> {
579+
///
580+
/// Example: In `Option<&T>`, since `&T` has a forbidden value 0,
581+
/// this method will return the path to `&T`, with a value of 0.
582+
///
583+
/// Example: In `Option<(u64, char)>`, since `char` has a
584+
/// forbidden value 2^32 - 1, this method will return the path to
585+
/// the `char` field in the tuple, with a value of 2^32 - 1.
586+
fn find_forbidden_value<'a>(&self, cx: &CrateContext<'a, 'tcx>) ->
587+
Option<(DiscrField, ValueRef)> {
563588
for (i, &ty) in self.tys.iter().enumerate() {
564-
if let Some(mut path) = find_discr_field_candidate(cx.tcx(), ty, vec![]) {
589+
if let Some((mut path, forbidden)) =
590+
find_discr_field_candidate(cx, ty, vec![]) {
565591
path.push(i);
566-
return Some(path);
592+
return Some((path, forbidden));
567593
}
568594
}
569595
None
@@ -1129,7 +1155,8 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
11291155
PointerCast(bcx, val.value, ty.ptr_to())
11301156
}
11311157
RawForbiddenValue { payload_discr, payload_ty, .. } => {
1132-
assert_eq!(ix, 0); // By definition, the payload of RawForbiddenValue has a single field.
1158+
// By definition, the payload of RawForbiddenValue has a single field.
1159+
assert_eq!(ix, 0);
11331160
assert_eq!(discr, payload_discr);
11341161
let ty = type_of::type_of(bcx.ccx(), payload_ty);
11351162
PointerCast(bcx, val.value, ty.ptr_to())

src/test/run-pass/enum-null-pointer-opt.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,33 @@ fn main() {
3838
assert_eq!(size_of::<&Trait>(), size_of::<Option<&Trait>>());
3939
assert_eq!(size_of::<&mut Trait>(), size_of::<Option<&mut Trait>>());
4040

41+
// Chars
42+
assert_eq!(size_of::<char>(), size_of::<Option<char>>());
43+
assert_eq!(size_of::<char>(), size_of::<Result<char, ()>>());
44+
45+
// Bools
46+
assert_eq!(size_of::<bool>(), size_of::<Option<bool>>());
47+
assert_eq!(size_of::<bool>(), size_of::<Result<bool, ()>>());
48+
4149
// Pointers - Box<T>
4250
assert_eq!(size_of::<Box<isize>>(), size_of::<Option<Box<isize>>>());
4351

4452
// The optimization can't apply to raw pointers
4553
assert!(size_of::<Option<*const isize>>() != size_of::<*const isize>());
4654
assert!(Some(0 as *const isize).is_some()); // Can't collapse None to null
4755

48-
struct Foo {
49-
_a: Box<isize>
56+
// The optimization can't apply to raw u32
57+
assert!(size_of::<Option<u32>>() != size_of::<u32>());
58+
59+
struct Foo<T> {
60+
_a: T
5061
}
51-
struct Bar(Box<isize>);
62+
struct Bar<T>(T);
5263

5364
// Should apply through structs
54-
assert_eq!(size_of::<Foo>(), size_of::<Option<Foo>>());
55-
assert_eq!(size_of::<Bar>(), size_of::<Option<Bar>>());
65+
assert_eq!(size_of::<Foo<Box<isize>>>(), size_of::<Option<Foo<Box<isize>>>>());
66+
assert_eq!(size_of::<Bar<Box<isize>>>(), size_of::<Option<Bar<Box<isize>>>>());
67+
5668
// and tuples
5769
assert_eq!(size_of::<(u8, Box<isize>)>(), size_of::<Option<(u8, Box<isize>)>>());
5870
// and fixed-size arrays

0 commit comments

Comments
 (0)