Skip to content

Commit 4c19cf5

Browse files
David Rajchenbach-Tellermrhota
David Rajchenbach-Teller
authored andcommitted
Issue rust-lang#5977 - RawNullablePointer can now optimize Option<char> and Option<bool> r?luqmana
1 parent 1d7881d commit 4c19cf5

File tree

2 files changed

+82
-44
lines changed

2 files changed

+82
-44
lines changed

src/librustc_trans/adt.rs

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -241,25 +241,28 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
241241

242242
// For the moment, we can only apply these
243243
// optimizations to safe pointers.
244-
match cases[discr].find_ptr(cx) {
245-
Some(ref df) if df.len() == 1 && st.fields.len() == 1 => {
244+
match cases[discr].find_forbidden_value(cx) {
245+
Some((ref df, forbidden_value))
246+
if df.len() == 1 && st.fields.len() == 1 => {
246247
let payload_ty = st.fields[0];
247248
return RawForbiddenValue {
248249
payload_discr: Disr::from(discr),
249250
payload_ty: payload_ty,
250-
forbidden_value: C_null(type_of::sizing_type_of(cx, payload_ty)),
251+
forbidden_value: forbidden_value,
251252
unit_fields: cases[1 - discr].tys.clone()
252253
};
253254
}
254-
Some(mut discrfield) => {
255-
discrfield.push(0);
256-
discrfield.reverse();
257-
return StructWrappedNullablePointer {
258-
nndiscr: Disr::from(discr),
259-
nonnull: st,
260-
discrfield: discrfield,
261-
nullfields: cases[1 - discr].tys.clone()
262-
};
255+
Some((mut discrfield, forbidden)) => {
256+
if is_null(forbidden) {
257+
discrfield.push(0);
258+
discrfield.reverse();
259+
return StructWrappedNullablePointer {
260+
nndiscr: Disr::from(discr),
261+
nonnull: st,
262+
discrfield: discrfield,
263+
nullfields: cases[1 - discr].tys.clone()
264+
};
265+
}
263266
}
264267
None => {}
265268
}
@@ -365,61 +368,73 @@ struct Case<'tcx> {
365368
/// This represents the (GEP) indices to follow to get to the discriminant field
366369
pub type DiscrField = Vec<usize>;
367370

368-
fn find_discr_field_candidate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
371+
fn find_discr_field_candidate<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
372+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
369373
ty: Ty<'tcx>,
370374
mut path: DiscrField)
371-
-> Option<DiscrField> {
375+
-> Option<(DiscrField, ValueRef)> {
372376
match ty.sty {
373-
// Fat &T/&mut T/Box<T> i.e. T is [T], str, or Trait
377+
// Fat &T/&mut T/Box<T> i.e. T is [T], str, or Trait; null is forbidden
374378
ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) if !type_is_sized(tcx, ty) => {
375379
path.push(FAT_PTR_ADDR);
376-
Some(path)
380+
Some((path, C_null(type_of::sizing_type_of(cx, ty))))
377381
},
378382

379-
// Regular thin pointer: &T/&mut T/Box<T>
380-
ty::TyRef(..) | ty::TyBox(..) => Some(path),
383+
// Regular thin pointer: &T/&mut T/Box<T>; null is forbidden
384+
ty::TyRef(..) | ty::TyBox(..) => Some((path, C_null(type_of::sizing_type_of(cx, ty)))),
385+
386+
// Function pointer: `fn() -> i32`; null is forbidden
387+
ty::TyFnPtr(_) => Some((path, C_null(type_of::sizing_type_of(cx, ty)))),
381388

382-
// Function pointer: `fn() -> i32`
383-
ty::TyFnPtr(_) => Some(path),
389+
// Is this a char or a bool? If so, std::uXX:MAX is forbidden.
390+
ty::TyChar => Some((path,
391+
C_integral(type_of::sizing_type_of(cx, ty),
392+
std::u32::MAX as u64, false))),
393+
ty::TyBool => Some((path,
394+
C_integral(type_of::sizing_type_of(cx, ty),
395+
std::u8::MAX as u64, false))),
384396

385397
// Is this the NonZero lang item wrapping a pointer or integer type?
398+
// If so, null is forbidden.
386399
ty::TyStruct(def, substs) if Some(def.did) == tcx.lang_items.non_zero() => {
387400
let nonzero_fields = &def.struct_variant().fields;
388401
assert_eq!(nonzero_fields.len(), 1);
389402
let field_ty = monomorphize::field_ty(tcx, substs, &nonzero_fields[0]);
390403
match field_ty.sty {
391404
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) if !type_is_sized(tcx, ty) => {
392405
path.extend_from_slice(&[0, FAT_PTR_ADDR]);
393-
Some(path)
406+
Some((path, C_null(Type::i8p(cx))))
394407
},
395408
ty::TyRawPtr(..) | ty::TyInt(..) | ty::TyUint(..) => {
396409
path.push(0);
397-
Some(path)
410+
Some((path, C_null(type_of::sizing_type_of(cx, field_ty))))
398411
},
399412
_ => None
400413
}
401414
},
402415

403-
// Perhaps one of the fields of this struct is non-zero
416+
// Perhaps one of the fields of this struct has a forbidden value.
404417
// let's recurse and find out
405418
ty::TyStruct(def, substs) => {
406419
for (j, field) in def.struct_variant().fields.iter().enumerate() {
407420
let field_ty = monomorphize::field_ty(tcx, substs, field);
408-
if let Some(mut fpath) = find_discr_field_candidate(tcx, field_ty, path.clone()) {
421+
if let Some((mut fpath, forbidden)) =
422+
find_discr_field_candidate(cx, field_ty, path.clone()) {
409423
fpath.push(j);
410-
return Some(fpath);
424+
return Some((fpath, forbidden));
411425
}
412426
}
413427
None
414428
},
415429

416-
// Perhaps one of the upvars of this struct is non-zero
430+
// Perhaps one of the upvars of this struct has a forbidden value.
417431
// Let's recurse and find out!
418432
ty::TyClosure(_, ref substs) => {
419433
for (j, &ty) in substs.upvar_tys.iter().enumerate() {
420-
if let Some(mut fpath) = find_discr_field_candidate(tcx, ty, path.clone()) {
434+
if let Some((mut fpath, forbidden)) =
435+
find_discr_field_candidate(cx, ty, path.clone()) {
421436
fpath.push(j);
422-
return Some(fpath);
437+
return Some((fpath, forbidden));
423438
}
424439
}
425440
None
@@ -428,26 +443,27 @@ fn find_discr_field_candidate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
428443
// Can we use one of the fields in this tuple?
429444
ty::TyTuple(ref tys) => {
430445
for (j, &ty) in tys.iter().enumerate() {
431-
if let Some(mut fpath) = find_discr_field_candidate(tcx, ty, path.clone()) {
446+
if let Some((mut fpath, forbidden)) =
447+
find_discr_field_candidate(cx, ty, path.clone()) {
432448
fpath.push(j);
433-
return Some(fpath);
449+
return Some((fpath, forbidden));
434450
}
435451
}
436452
None
437453
},
438454

439-
// Is this a fixed-size array of something non-zero
455+
// Is this a fixed-size array of something with a forbidden value
440456
// with at least one element?
441457
ty::TyArray(ety, d) if d > 0 => {
442-
if let Some(mut vpath) = find_discr_field_candidate(tcx, ety, path) {
458+
if let Some((mut vpath, forbidden)) = find_discr_field_candidate(cx, ety, path) {
443459
vpath.push(0);
444-
Some(vpath)
460+
Some((vpath, forbidden))
445461
} else {
446462
None
447463
}
448464
},
449465

450-
// Anything else is not a pointer
466+
// Anything else doesn't have a known-to-be-safe forbidden value.
451467
_ => None
452468
}
453469
}
@@ -457,13 +473,22 @@ impl<'tcx> Case<'tcx> {
457473
mk_struct(cx, &self.tys, false, scapegoat).size == 0
458474
}
459475

460-
/// Find a safe pointer that may be used to discriminate in a
476+
/// Find a forbidden value that may be used to discriminate in a
461477
/// RawForbiddenValue or StructWrappedNullablePointer.
462-
fn find_ptr<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Option<DiscrField> {
478+
///
479+
/// Example: In `Option<&T>`, since `&T` has a forbidden value 0,
480+
/// this method will return the path to `&T`, with a value of 0.
481+
///
482+
/// Example: In `Option<(u64, char)>`, since `char` has a
483+
/// forbidden value 2^32 - 1, this method will return the path to
484+
/// the `char` field in the tuple, with a value of 2^32 - 1.
485+
fn find_forbidden_value<'a>(&self, cx: &CrateContext<'a, 'tcx>) ->
486+
Option<(DiscrField, ValueRef)> {
463487
for (i, &ty) in self.tys.iter().enumerate() {
464-
if let Some(mut path) = find_discr_field_candidate(cx.tcx(), ty, vec![]) {
488+
if let Some((mut path, forbidden)) =
489+
find_discr_field_candidate(cx, ty, vec![]) {
465490
path.push(i);
466-
return Some(path);
491+
return Some((path, forbidden));
467492
}
468493
}
469494
None
@@ -973,7 +998,8 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
973998
bcx.pointercast(val.value, ty.ptr_to())
974999
}
9751000
RawForbiddenValue { payload_discr, payload_ty, .. } => {
976-
assert_eq!(ix, 0); // By definition, the payload of RawForbiddenValue has a single field.
1001+
// By definition, the payload of RawForbiddenValue has a single field.
1002+
assert_eq!(ix, 0);
9771003
assert_eq!(discr, payload_discr);
9781004
let ty = type_of::type_of(bcx.ccx(), payload_ty);
9791005
if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }

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

Lines changed: 17 additions & 5 deletions
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)