librustc: Remove simplify_type and replace with sizing_type_of. rs=blocking-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.
This commit is contained in:
Patrick Walton 2013-02-16 09:43:16 -08:00
parent 9ad8a1f465
commit 548c0982ca
4 changed files with 139 additions and 87 deletions

View file

@ -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,

View file

@ -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,

View file

@ -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
@ -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")
}
}

View file

@ -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));
@ -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,
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