From 548c0982cabe12fba5ee106e542f615f750af6e6 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sat, 16 Feb 2013 09:43:16 -0800 Subject: [PATCH] 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. --- src/librustc/middle/trans/base.rs | 1 + src/librustc/middle/trans/common.rs | 1 + src/librustc/middle/trans/machine.rs | 105 ++++++++--------------- src/librustc/middle/trans/type_of.rs | 119 +++++++++++++++++++++++---- 4 files changed, 139 insertions(+), 87 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 7110365034093..13f29724ba80e 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -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, diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 13902be776be5..b0ec0e1b90738 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -202,6 +202,7 @@ pub struct crate_ctxt { const_values: HashMap, module_data: HashMap<~str, ValueRef>, lltypes: HashMap, + llsizingtypes: HashMap, names: namegen, next_addrspace: addrspace_gen, symbol_hasher: @hash::State, diff --git a/src/librustc/middle/trans/machine.rs b/src/librustc/middle/trans/machine.rs index 75e494f7bfcd9..58a53cd878a70 100644 --- a/src/librustc/middle/trans/machine.rs +++ b/src/librustc/middle/trans/machine.rs @@ -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") } } diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 972f702c18a81..d87674f6b6e84 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -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, - 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