Skip to content

Commit

Permalink
auto merge of #4980 : pcwalton/rust/sizing-type-of, r=pcwalton
Browse files Browse the repository at this point in the history
...locking-servo

`simplify_type` was bogus, as there was no way for it to handle enums
properly. It was also slow, because it created many Rust types at runtime. In
general creating Rust types during trans is a source of slowness, and I'd like
to avoid doing it as much as possible. (It is probably not possible to
eliminate it entirely, due to `subst`, but we should get rid of as much of it
as we can.) So this patch replaces `simplify_type` with `sizing_type_of`,
which creates a size-equivalent LLVM type directly without going through a
Rust type first.

Because this is causing an ICE in Servo, I'm rubber stamping it.
  • Loading branch information
bors committed Feb 16, 2013
2 parents 65aa259 + 548c098 commit 8d8c25b
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 87 deletions.
1 change: 1 addition & 0 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3030,6 +3030,7 @@ pub fn trans_crate(sess: session::Session,
const_values: HashMap(),
module_data: HashMap(),
lltypes: ty::new_ty_hash(),
llsizingtypes: ty::new_ty_hash(),
names: new_namegen(sess.parse_sess.interner),
next_addrspace: new_addrspace_gen(),
symbol_hasher: symbol_hasher,
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/trans/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ pub struct crate_ctxt {
const_values: HashMap<ast::node_id, ValueRef>,
module_data: HashMap<~str, ValueRef>,
lltypes: HashMap<ty::t, TypeRef>,
llsizingtypes: HashMap<ty::t, TypeRef>,
names: namegen,
next_addrspace: addrspace_gen,
symbol_hasher: @hash::State,
Expand Down
105 changes: 33 additions & 72 deletions src/librustc/middle/trans/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,60 +13,8 @@

use middle::trans::common::*;
use middle::trans::type_of;
use middle::ty::field;
use middle::ty;

use syntax::parse::token::special_idents;

// Creates a simpler, size-equivalent type. The resulting type is guaranteed
// to have (a) the same size as the type that was passed in; (b) to be non-
// recursive. This is done by replacing all boxes in a type with boxed unit
// types.
// This should reduce all pointers to some simple pointer type, to
// ensure that we don't recurse endlessly when computing the size of a
// nominal type that has pointers to itself in it.
pub fn simplify_type(tcx: ty::ctxt, typ: ty::t) -> ty::t {
fn nilptr(tcx: ty::ctxt) -> ty::t {
ty::mk_ptr(tcx, ty::mt {ty: ty::mk_nil(tcx), mutbl: ast::m_imm})
}
fn simplifier(tcx: ty::ctxt, typ: ty::t) -> ty::t {
match ty::get(typ).sty {
ty::ty_box(_) | ty::ty_opaque_box | ty::ty_uniq(_) |
ty::ty_evec(_, ty::vstore_uniq) | ty::ty_evec(_, ty::vstore_box) |
ty::ty_estr(ty::vstore_uniq) | ty::ty_estr(ty::vstore_box) |
ty::ty_ptr(_) | ty::ty_rptr(*) => nilptr(tcx),

ty::ty_bare_fn(*) | // FIXME(#4804) Bare fn repr
ty::ty_closure(*) => ty::mk_tup(tcx, ~[nilptr(tcx), nilptr(tcx)]),

ty::ty_evec(_, ty::vstore_slice(_)) |
ty::ty_estr(ty::vstore_slice(_)) => {
ty::mk_tup(tcx, ~[nilptr(tcx), ty::mk_int(tcx)])
}
// Reduce a class type to a record type in which all the fields are
// simplified
ty::ty_struct(did, ref substs) => {
let simpl_fields = (if ty::ty_dtor(tcx, did).is_present() {
// remember the drop flag
~[field {
ident: special_idents::dtor,
mt: ty::mt {ty: ty::mk_u8(tcx), mutbl: ast::m_mutbl}
}] }
else { ~[] }) +
do ty::lookup_struct_fields(tcx, did).map |f| {
let t = ty::lookup_field_type(tcx, did, f.id, substs);
field {
ident: f.ident,
mt: ty::mt {ty: simplify_type(tcx, t), mutbl: ast::m_const
}}
};
ty::mk_rec(tcx, simpl_fields)
}
_ => typ
}
}
ty::fold_ty(tcx, typ, |t| simplifier(tcx, t))
}
use util::ppaux::ty_to_str;

// ______________________________________________________________________
// compute sizeof / alignof
Expand Down Expand Up @@ -180,27 +128,40 @@ pub fn llalign_of(cx: @crate_ctxt, t: TypeRef) -> ValueRef {

// Computes the size of the data part of an enum.
pub fn static_size_of_enum(cx: @crate_ctxt, t: ty::t) -> uint {
if cx.enum_sizes.contains_key(&t) { return cx.enum_sizes.get(&t); }
if cx.enum_sizes.contains_key(&t) {
return cx.enum_sizes.get(&t);
}

debug!("static_size_of_enum %s", ty_to_str(cx.tcx, t));

match ty::get(t).sty {
ty::ty_enum(tid, ref substs) => {
// Compute max(variant sizes).
let mut max_size = 0u;
let variants = ty::enum_variants(cx.tcx, tid);
for vec::each(*variants) |variant| {
let tup_ty = simplify_type(
cx.tcx,
ty::mk_tup(cx.tcx, /*bad*/copy variant.args));
// Perform any type parameter substitutions.
let tup_ty = ty::subst(cx.tcx, substs, tup_ty);
// Here we possibly do a recursive call.
let this_size =
llsize_of_real(cx, type_of::type_of(cx, tup_ty));
if max_size < this_size { max_size = this_size; }
ty::ty_enum(tid, ref substs) => {
// Compute max(variant sizes).
let mut max_size = 0;
let variants = ty::enum_variants(cx.tcx, tid);
for variants.each |variant| {
if variant.args.len() == 0 {
loop;
}

let lltypes = variant.args.map(|&variant_arg| {
let substituted = ty::subst(cx.tcx, substs, variant_arg);
type_of::sizing_type_of(cx, substituted)
});

debug!("static_size_of_enum: variant %s type %s",
cx.tcx.sess.str_of(variant.name),
ty_str(cx.tn, T_struct(lltypes)));

let this_size = llsize_of_real(cx, T_struct(lltypes));
if max_size < this_size {
max_size = this_size;
}
}
cx.enum_sizes.insert(t, max_size);
return max_size;
}
cx.enum_sizes.insert(t, max_size);
return max_size;
}
_ => cx.sess.bug(~"static_size_of_enum called on non-enum")
_ => cx.sess.bug(~"static_size_of_enum called on non-enum")
}
}

119 changes: 104 additions & 15 deletions src/librustc/middle/trans/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,95 @@ pub fn type_of_non_gc_box(cx: @crate_ctxt, t: ty::t) -> TypeRef {
}
}

// A "sizing type" is an LLVM type, the size and alignment of which are
// guaranteed to be equivalent to what you would get out of `type_of()`. It's
// useful because:
//
// (1) It may be cheaper to compute the sizing type than the full type if all
// you're interested in is the size and/or alignment;
//
// (2) It won't make any recursive calls to determine the structure of the
// type behind pointers. This can help prevent infinite loops for
// recursive types. For example, `static_size_of_enum()` relies on this
// behavior.

pub fn sizing_type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef {
if cx.llsizingtypes.contains_key(&t) {
return cx.llsizingtypes.get(&t);
}

let llsizingty = match ty::get(t).sty {
ty::ty_nil | ty::ty_bot => T_nil(),
ty::ty_bool => T_bool(),
ty::ty_int(t) => T_int_ty(cx, t),
ty::ty_uint(t) => T_uint_ty(cx, t),
ty::ty_float(t) => T_float_ty(cx, t),

ty::ty_estr(ty::vstore_uniq) |
ty::ty_estr(ty::vstore_box) |
ty::ty_evec(_, ty::vstore_uniq) |
ty::ty_evec(_, ty::vstore_box) |
ty::ty_box(*) |
ty::ty_opaque_box |
ty::ty_uniq(*) |
ty::ty_ptr(*) |
ty::ty_rptr(*) |
ty::ty_type |
ty::ty_opaque_closure_ptr(*) => T_ptr(T_i8()),

ty::ty_estr(ty::vstore_slice(*)) |
ty::ty_evec(_, ty::vstore_slice(*)) => {
T_struct(~[T_ptr(T_i8()), T_ptr(T_i8())])
}

// FIXME(#4804) Bare fn repr
ty::ty_bare_fn(*) => T_struct(~[T_ptr(T_i8()), T_ptr(T_i8())]),
ty::ty_closure(*) => T_struct(~[T_ptr(T_i8()), T_ptr(T_i8())]),
ty::ty_trait(_, _, vstore) => T_opaque_trait(cx, vstore),

ty::ty_estr(ty::vstore_fixed(size)) => T_array(T_i8(), size),
ty::ty_evec(mt, ty::vstore_fixed(size)) => {
T_array(sizing_type_of(cx, mt.ty), size)
}

ty::ty_unboxed_vec(mt) => T_vec(cx, sizing_type_of(cx, mt.ty)),

ty::ty_tup(ref elems) => {
T_struct(elems.map(|&t| sizing_type_of(cx, t)))
}

ty::ty_rec(ref fields) => {
T_struct(fields.map(|f| sizing_type_of(cx, f.mt.ty)))
}

ty::ty_struct(def_id, ref substs) => {
let fields = ty::lookup_struct_fields(cx.tcx, def_id);
let lltype = T_struct(fields.map(|field| {
let field_type = ty::lookup_field_type(cx.tcx,
def_id,
field.id,
substs);
sizing_type_of(cx, field_type)
}));
if ty::ty_dtor(cx.tcx, def_id).is_present() {
T_struct(~[lltype, T_i8()])
} else {
lltype
}
}

ty::ty_enum(def_id, _) => T_struct(enum_body_types(cx, def_id, t)),

ty::ty_self | ty::ty_infer(*) | ty::ty_param(*) | ty::ty_err(*) => {
cx.tcx.sess.bug(~"fictitious type in sizing_type_of()")
}
};

cx.llsizingtypes.insert(t, llsizingty);
llsizingty
}

// NB: If you update this, be sure to update `sizing_type_of()` as well.
pub fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef {
debug!("type_of %?: %?", t, ty::get(t));

Expand Down Expand Up @@ -236,23 +325,23 @@ pub fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef {
return llty;
}

pub fn fill_type_of_enum(cx: @crate_ctxt, did: ast::def_id, t: ty::t,
llty: TypeRef) {
pub fn enum_body_types(cx: @crate_ctxt, did: ast::def_id, t: ty::t)
-> ~[TypeRef] {
let univar = ty::enum_is_univariant(cx.tcx, did);
let size = machine::static_size_of_enum(cx, t);
if !univar {
~[T_enum_discrim(cx), T_array(T_i8(), size)]
} else {
~[T_array(T_i8(), size)]
}
}

pub fn fill_type_of_enum(cx: @crate_ctxt,
did: ast::def_id,
t: ty::t,
llty: TypeRef) {
debug!("type_of_enum %?: %?", t, ty::get(t));

let lltys = {
let univar = ty::enum_is_univariant(cx.tcx, did);
let size = machine::static_size_of_enum(cx, t);
if !univar {
~[T_enum_discrim(cx), T_array(T_i8(), size)]
}
else {
~[T_array(T_i8(), size)]
}
};

common::set_struct_body(llty, lltys);
common::set_struct_body(llty, enum_body_types(cx, did, t));
}

// Want refinements! (Or case classes, I guess
Expand Down

0 comments on commit 8d8c25b

Please sign in to comment.