Skip to content

Commit

Permalink
librustc: Traverse arbitrarily deep for nullable enum opt.
Browse files Browse the repository at this point in the history
  • Loading branch information
luqmana committed Dec 29, 2014
1 parent 3e6b29f commit e954fc4
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 97 deletions.
166 changes: 76 additions & 90 deletions src/librustc_trans/trans/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,13 @@
#![allow(unsigned_negation)]

pub use self::PointerField::*;
pub use self::Repr::*;

use std::num::Int;
use std::rc::Rc;

use llvm::{ValueRef, True, IntEQ, IntNE};
use back::abi;
use back::abi::FAT_PTR_ADDR;
use middle::subst;
use middle::subst::Subst;
use trans::_match;
Expand All @@ -71,7 +70,6 @@ use util::ppaux::ty_to_string;

type Hint = attr::ReprAttr;


/// Representations.
#[deriving(Eq, PartialEq, Show)]
pub enum Repr<'tcx> {
Expand Down Expand Up @@ -101,7 +99,7 @@ pub enum Repr<'tcx> {
nullfields: Vec<Ty<'tcx>>
},
/// Two cases distinguished by a nullable pointer: the case with discriminant
/// `nndiscr` is represented by the struct `nonnull`, where the `ptrfield`th
/// `nndiscr` is represented by the struct `nonnull`, where the `discrfield`th
/// field is known to be nonnull due to its type; if that field is null, then
/// it represents the other case, which is inhabited by at most one value
/// (and all other fields are undefined/unused).
Expand All @@ -112,7 +110,7 @@ pub enum Repr<'tcx> {
StructWrappedNullablePointer {
nonnull: Struct<'tcx>,
nndiscr: Disr,
ptrfield: PointerField,
discrfield: DiscrField,
nullfields: Vec<Ty<'tcx>>,
}
}
Expand Down Expand Up @@ -230,18 +228,20 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let st = mk_struct(cx, cases[discr].tys[],
false, t);
match cases[discr].find_ptr(cx) {
Some(ThinPointer(_)) if st.fields.len() == 1 => {
Some(ref pf) if pf.len() == 1 && st.fields.len() == 1 => {
return RawNullablePointer {
nndiscr: discr as Disr,
nnty: st.fields[0],
nullfields: cases[1 - discr].tys.clone()
};
}
Some(ptrfield) => {
Some(pf) => {
let mut discrfield = vec![0];
discrfield.extend(pf.into_iter());
return StructWrappedNullablePointer {
nndiscr: discr as Disr,
nonnull: st,
ptrfield: ptrfield,
discrfield: discrfield,
nullfields: cases[1 - discr].tys.clone()
};
}
Expand Down Expand Up @@ -335,49 +335,67 @@ struct Case<'tcx> {
tys: Vec<Ty<'tcx>>
}

/// This represents the (GEP) indices to follow to get to the discriminant field
pub type DiscrField = Vec<uint>;

#[deriving(Copy, Eq, PartialEq, Show)]
pub enum PointerField {
ThinPointer(uint),
FatPointer(uint)
}
fn find_discr_field_candidate<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> Option<DiscrField> {
match ty.sty {
// &T/&mut T/Box<T> could either be a thin or fat pointer depending on T
ty::ty_rptr(_, ty::mt { ty, .. }) | ty::ty_uniq(ty) => match ty.sty {
// &[T] and &str are a pointer and length pair
ty::ty_vec(_, None) | ty::ty_str => Some(vec![FAT_PTR_ADDR]),

impl<'tcx> Case<'tcx> {
fn is_zerolen<'a>(&self, cx: &CrateContext<'a, 'tcx>, scapegoat: Ty<'tcx>)
-> bool {
mk_struct(cx, self.tys[], false, scapegoat).size == 0
}
ty::ty_struct(..) if !ty::type_is_sized(tcx, ty) => Some(vec![FAT_PTR_ADDR]),

fn find_ptr<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Option<PointerField> {
for (i, &ty) in self.tys.iter().enumerate() {
match ty.sty {
// &T/&mut T/Box<T> could either be a thin or fat pointer depending on T
ty::ty_rptr(_, ty::mt { ty, .. }) | ty::ty_uniq(ty) => match ty.sty {
// &[T] and &str are a pointer and length pair
ty::ty_vec(_, None) | ty::ty_str => return Some(FatPointer(i)),
// Any other &T is just a pointer
_ => Some(vec![])
},

// &Trait is a pair of pointers: the actual object and a vtable
ty::ty_trait(..) => return Some(FatPointer(i)),
// Functions are just pointers
ty::ty_bare_fn(..) => Some(vec![]),

ty::ty_struct(..) if !ty::type_is_sized(cx.tcx(), ty) => {
return Some(FatPointer(i))
}
// Closures are a pair of pointers: the code and environment
ty::ty_closure(..) => Some(vec![FAT_PTR_ADDR]),

// Any other &T is just a pointer
_ => return Some(ThinPointer(i))
},
// Perhaps one of the fields of this struct is non-null
// let's recurse and find out
ty::ty_struct(def_id, ref substs) => {
let fields = ty::lookup_struct_fields(tcx, def_id);
for (j, field) in fields.iter().enumerate() {
let field_ty = ty::lookup_field_type(tcx, def_id, field.id, substs);
match find_discr_field_candidate(tcx, field_ty) {
Some(v) => {
let mut discrfield = vec![j];
discrfield.extend(v.into_iter());
return Some(discrfield);
}
None => continue
}
}
None
},

// Functions are just pointers
ty::ty_bare_fn(..) => return Some(ThinPointer(i)),
// Anything else is not a pointer
_ => None
}
}

// Closures are a pair of pointers: the code and environment
ty::ty_closure(..) => return Some(FatPointer(i)),
impl<'tcx> Case<'tcx> {
fn is_zerolen<'a>(&self, cx: &CrateContext<'a, 'tcx>, scapegoat: Ty<'tcx>) -> bool {
mk_struct(cx, self.tys[], false, scapegoat).size == 0
}

// Anything else is not a pointer
_ => continue
fn find_ptr<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Option<DiscrField> {
for (i, &ty) in self.tys.iter().enumerate() {
match find_discr_field_candidate(cx.tcx(), ty) {
Some(v) => {
let mut discrfield = vec![i];
discrfield.extend(v.into_iter());
return Some(discrfield);
}
None => continue
}
}

None
}
}
Expand Down Expand Up @@ -709,8 +727,8 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
val = ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty));
signed = false;
}
StructWrappedNullablePointer { nndiscr, ptrfield, .. } => {
val = struct_wrapped_nullable_bitdiscr(bcx, nndiscr, ptrfield, scrutinee);
StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
val = struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee);
signed = false;
}
}
Expand All @@ -720,12 +738,9 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
}
}

fn struct_wrapped_nullable_bitdiscr(bcx: Block, nndiscr: Disr, ptrfield: PointerField,
fn struct_wrapped_nullable_bitdiscr(bcx: Block, nndiscr: Disr, discrfield: &DiscrField,
scrutinee: ValueRef) -> ValueRef {
let llptrptr = match ptrfield {
ThinPointer(field) => GEPi(bcx, scrutinee, &[0, field]),
FatPointer(field) => GEPi(bcx, scrutinee, &[0, field, abi::FAT_PTR_ADDR])
};
let llptrptr = GEPi(bcx, scrutinee, discrfield[]);
let llptr = Load(bcx, llptrptr);
let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
ICmp(bcx, cmp, llptr, C_null(val_ty(llptr)))
Expand Down Expand Up @@ -811,17 +826,10 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
Store(bcx, C_null(llptrty), val)
}
}
StructWrappedNullablePointer { ref nonnull, nndiscr, ptrfield, .. } => {
StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
if discr != nndiscr {
let (llptrptr, llptrty) = match ptrfield {
ThinPointer(field) =>
(GEPi(bcx, val, &[0, field]),
type_of::type_of(bcx.ccx(), nonnull.fields[field])),
FatPointer(field) => {
let v = GEPi(bcx, val, &[0, field, abi::FAT_PTR_ADDR]);
(v, val_ty(v).element_type())
}
};
let llptrptr = GEPi(bcx, val, discrfield[]);
let llptrty = val_ty(llptrptr).element_type();
Store(bcx, C_null(llptrty), llptrptr)
}
}
Expand Down Expand Up @@ -1041,7 +1049,7 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
false)
} else {
let vals = nonnull.fields.iter().map(|&ty| {
// Always use null even if it's not the `ptrfield`th
// Always use null even if it's not the `discrfield`th
// field; see #8506.
C_null(type_of::sizing_type_of(ccx, ty))
}).collect::<Vec<ValueRef>>();
Expand Down Expand Up @@ -1121,9 +1129,8 @@ fn padding(ccx: &CrateContext, size: u64) -> ValueRef {
#[inline]
fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a }

/// Get the discriminant of a constant value. (Not currently used.)
pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef)
-> Disr {
/// Get the discriminant of a constant value.
pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) -> Disr {
match *r {
CEnum(ity, _, _) => {
match ity {
Expand All @@ -1138,25 +1145,8 @@ pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef)
}
}
Univariant(..) => 0,
RawNullablePointer { nndiscr, .. } => {
if is_null(val) {
/* subtraction as uint is ok because nndiscr is either 0 or 1 */
(1 - nndiscr) as Disr
} else {
nndiscr
}
}
StructWrappedNullablePointer { nndiscr, ptrfield, .. } => {
let (idx, sub_idx) = match ptrfield {
ThinPointer(field) => (field, None),
FatPointer(field) => (field, Some(abi::FAT_PTR_ADDR))
};
if is_null(const_struct_field(ccx, val, idx, sub_idx)) {
/* subtraction as uint is ok because nndiscr is either 0 or 1 */
(1 - nndiscr) as Disr
} else {
nndiscr
}
RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
ccx.sess().bug("const discrim access of non c-like enum")
}
}
}
Expand All @@ -1170,29 +1160,25 @@ pub fn const_get_field(ccx: &CrateContext, r: &Repr, val: ValueRef,
_discr: Disr, ix: uint) -> ValueRef {
match *r {
CEnum(..) => ccx.sess().bug("element access in C-like enum const"),
Univariant(..) => const_struct_field(ccx, val, ix, None),
General(..) => const_struct_field(ccx, val, ix + 1, None),
Univariant(..) => const_struct_field(ccx, val, ix),
General(..) => const_struct_field(ccx, val, ix + 1),
RawNullablePointer { .. } => {
assert_eq!(ix, 0);
val
}
StructWrappedNullablePointer{ .. } => const_struct_field(ccx, val, ix, None)
},
StructWrappedNullablePointer{ .. } => const_struct_field(ccx, val, ix)
}
}

/// Extract field of struct-like const, skipping our alignment padding.
fn const_struct_field(ccx: &CrateContext, val: ValueRef, ix: uint, sub_idx: Option<uint>)
-> ValueRef {
fn const_struct_field(ccx: &CrateContext, val: ValueRef, ix: uint) -> ValueRef {
// Get the ix-th non-undef element of the struct.
let mut real_ix = 0; // actual position in the struct
let mut ix = ix; // logical index relative to real_ix
let mut field;
loop {
loop {
field = match sub_idx {
Some(si) => const_get_elt(ccx, val, &[real_ix, si as u32]),
None => const_get_elt(ccx, val, &[real_ix])
};
field = const_get_elt(ccx, val, &[real_ix]);
if !is_undef(field) {
break;
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_trans/trans/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ pub fn is_undef(val: ValueRef) -> bool {
}
}

#[allow(dead_code)] // potentially useful
pub fn is_null(val: ValueRef) -> bool {
unsafe {
llvm::LLVMIsNull(val) != False
Expand Down
14 changes: 7 additions & 7 deletions src/librustc_trans/trans/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2292,14 +2292,14 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
},
adt::StructWrappedNullablePointer { nonnull: ref struct_def,
nndiscr,
ptrfield, ..} => {
ref discrfield, ..} => {
// Create a description of the non-null variant
let (variant_type_metadata, variant_llvm_type, member_description_factory) =
describe_enum_variant(cx,
self.enum_type,
struct_def,
&*(*self.variants)[nndiscr as uint],
OptimizedDiscriminant(ptrfield),
OptimizedDiscriminant,
self.containing_scope,
self.span);

Expand All @@ -2315,10 +2315,10 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
// member's name.
let null_variant_index = (1 - nndiscr) as uint;
let null_variant_name = token::get_name((*self.variants)[null_variant_index].name);
let discrfield = match ptrfield {
adt::ThinPointer(field) => format!("{}", field),
adt::FatPointer(field) => format!("{}", field)
};
let discrfield = discrfield.iter()
.skip(1)
.map(|x| x.to_string())
.collect::<Vec<_>>().connect("$");
let union_member_name = format!("RUST$ENCODED$ENUM${}${}",
discrfield,
null_variant_name);
Expand Down Expand Up @@ -2367,7 +2367,7 @@ impl<'tcx> VariantMemberDescriptionFactory<'tcx> {
#[deriving(Copy)]
enum EnumDiscriminantInfo {
RegularDiscriminant(DIType),
OptimizedDiscriminant(adt::PointerField),
OptimizedDiscriminant,
NoDiscriminant
}

Expand Down

0 comments on commit e954fc4

Please sign in to comment.