Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
@@ -8,8 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub use self::code_stats::{CodeStats, DataTypeKind, FieldInfo};
pub use self::code_stats::{SizeKind, TypeSizeInfo, VariantInfo};
pub use self::print_type_info::{CodeStats};
// pub use self::code_stats::{CodeStats, DataTypeKind, FieldInfo};
// pub use self::code_stats::{SizeKind, TypeSizeInfo, VariantInfo};

use dep_graph::DepGraph;
use hir::def_id::{CrateNum, DefIndex};
@@ -51,7 +52,8 @@ use std::rc::Rc;
use std::sync::{Once, ONCE_INIT};
use std::time::Duration;

mod code_stats;
// mod code_stats;
pub mod print_type_info;
pub mod config;
pub mod filesearch;
pub mod search_paths;
154 changes: 154 additions & 0 deletions src/librustc/session/print_type_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.


use rustc_data_structures::fx::FxHashSet;
use std::mem;
use std::hash::{Hash, Hasher};
use std::collections::BTreeMap;
use rustc_serialize::json::Json;

pub struct CodeStats {
types: FxHashSet<Type>,
}

/// A Type name, with certain modifiers applied.
///
/// While a Type can have a name like rust::u32, a ComplexTypeName
/// can include nested pointer/array modifiers:
///
/// * `*const ComplexTypeName`
/// * `[ComplexTypeName; N]`
///
/// This avoids metadata blowup.
///
/// For example: `*const [*mut [rust::u32, 12], 32]`
pub type ComplexTypeName = String;

pub struct Type {
pub name: String,
pub size: u64,
pub align: u64,
pub public: bool,
pub kind: TypeKind,
}

pub enum TypeKind {
PrimitiveInt,
PrimitiveFloat,
Opaque,
Struct { fields: Vec<Field> },
Union { fields: Vec<Field> },
Enum { base_type: ComplexTypeName, cases: Vec<Case> },
}

pub struct Field {
pub name: String,
pub type_name: ComplexTypeName,
pub offset: u64,
pub public: bool,
}

pub struct Case {
pub name: String,
pub value: i64, // TODO: u64/u128/i128? (serialize doesn't support)
}

impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool { self.name == other.name }
}
impl Eq for Type {}
impl Hash for Type {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}


impl TypeKind {
fn as_str(&self) -> &'static str {
match *self {
TypeKind::PrimitiveInt => "primitive_int",
TypeKind::PrimitiveFloat => "primitive_float",
TypeKind::Opaque => "opaque",
TypeKind::Struct { .. } => "struct",
TypeKind::Union { .. } => "union",
TypeKind::Enum { .. } => "enum",
}
}
}

impl CodeStats {
pub fn new() -> Self {
CodeStats { types: FxHashSet::default() }
}

pub fn insert(&mut self, ty: Type) {
self.types.insert(ty);
}

pub fn print_type_sizes(&mut self) {
let types = mem::replace(&mut self.types, FxHashSet::default());

let mut output = Vec::with_capacity(types.len());

for ty in types {
let mut json = BTreeMap::new();

json.insert("name".to_string(), Json::String(ty.name));
json.insert("size".to_string(), Json::U64(ty.size));
json.insert("align".to_string(), Json::U64(ty.align));
json.insert("public".to_string(), Json::Boolean(ty.public));
json.insert("kind".to_string(), Json::String(ty.kind.as_str().to_string()));

match ty.kind {
TypeKind::Struct { fields } | TypeKind::Union { fields } => {
let fields_json = fields.into_iter().map(field_to_json).collect();
json.insert("fields".to_string(), Json::Array(fields_json));
}
TypeKind::Enum { base_type, cases } => {
json.insert("base_type".to_string(), Json::String(base_type));
let cases_json = cases.into_iter().map(case_to_json).collect();
json.insert("cases".to_string(), Json::Array(cases_json));
}
_ => { /* nothing */ }
}

output.push(Json::Object(json));
}

println!("WARNING: these values are platform-specific, implementation-specific, \
and compilation-specific. They can and will change for absolutely no reason. \
To use this properly, you must recompute and evaluate them on each compilation \
of your crate. Yes we broke your JSON parsing just to say this. We're not \
kidding here.");
println!("{}", Json::Array(output));
}
}

fn case_to_json(case: Case) -> Json {
let mut json = BTreeMap::new();

json.insert("name".to_string(), Json::String(case.name));
json.insert("value".to_string(), Json::I64(case.value));

Json::Object(json)
}

fn field_to_json(field: Field) -> Json {
let mut json = BTreeMap::new();

json.insert("name".to_string(), Json::String(field.name));
json.insert("type".to_string(), Json::String(field.type_name));
json.insert("offset".to_string(), Json::U64(field.offset));
json.insert("public".to_string(), Json::Boolean(field.public));

Json::Object(json)
}
381 changes: 205 additions & 176 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
@@ -12,10 +12,10 @@ pub use self::Integer::*;
pub use self::Layout::*;
pub use self::Primitive::*;

use session::{self, DataTypeKind, Session};
use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags};
use session::Session;
use ty::{self, item_path, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags, Visibility};

use syntax::ast::{self, FloatTy, IntTy, UintTy};
use syntax::ast::{FloatTy, IntTy, UintTy};
use syntax::attr;
use syntax_pos::DUMMY_SP;

@@ -1719,15 +1719,14 @@ impl<'a, 'tcx> Layout {
pub fn record_layout_for_printing(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
layout: &Layout) {
layout: &'tcx Layout) {
// If we are running with `-Zprint-type-sizes`, record layouts for
// dumping later. Ignore layouts that are done with non-empty
// environments or non-monomorphic layouts, as the user only wants
// to see the stuff resulting from the final trans session.
if
!tcx.sess.opts.debugging_opts.print_type_sizes ||
ty.has_param_types() ||
ty.has_self_ty() ||
!ty.is_normalized_for_trans() ||
!param_env.caller_bounds.is_empty()
{
return;
@@ -1739,197 +1738,142 @@ impl<'a, 'tcx> Layout {
fn record_layout_for_printing_outlined(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
layout: &Layout) {
// (delay format until we actually need it)
let record = |kind, opt_discr_size, variants| {
let type_desc = format!("{:?}", ty);
let overall_size = layout.size(tcx);
let align = layout.align(tcx);
tcx.sess.code_stats.borrow_mut().record_type_size(kind,
type_desc,
align,
overall_size,
opt_discr_size,
variants);
};
layout: &'tcx Layout) {

let (adt_def, substs) = match ty.sty {
ty::TyAdt(ref adt_def, substs) => {
debug!("print-type-size t: `{:?}` process adt", ty);
(adt_def, substs)
}
use session::print_type_info::{Type, TypeKind, Field, Case};

ty::TyClosure(..) => {
debug!("print-type-size t: `{:?}` record closure", ty);
record(DataTypeKind::Closure, None, vec![]);
return;
}
// Caller should have filtered out all non-monomorphic types, so we
// we don't need to worry about finding a generic Vec<T> or Vec<U::Item>
match ty.sty {
// Concrete types we should consider processing
ty::TyBool |
ty::TyChar |
ty::TyInt(..) |
ty::TyUint(..) |
ty::TyFloat(..) |
ty::TyAdt(..) |
ty::TyRawPtr(..) |
ty::TyRef(..) |
ty::TyNever |
ty::TyTuple(..) => { }

// Anonymous or generic types we should avoid
_ => {
// TODO: `-> impl Trait` means closures can technically end up
// in public signatures, so we might want to actually record them?
debug!("print-type-size t: `{:?}` skip non-nominal", ty);
return;
}
};

let adt_kind = adt_def.adt_kind();

let build_field_info = |(field_name, field_ty): (ast::Name, Ty<'tcx>), offset: &Size| {
let layout = field_ty.layout(tcx, param_env);
match layout {
Err(_) => bug!("no layout found for field {} type: `{:?}`", field_name, field_ty),
Ok(field_layout) => {
session::FieldInfo {
name: field_name.to_string(),
offset: offset.bytes(),
size: field_layout.size(tcx).bytes(),
align: field_layout.align(tcx).abi(),
}
}
}
let cx = LayoutCx::new(tcx, param_env);

let layout = TyLayout {
ty,
layout: layout,
variant_index: None
};

let build_primitive_info = |name: ast::Name, value: &Primitive| {
session::VariantInfo {
name: Some(name.to_string()),
kind: session::SizeKind::Exact,
align: value.align(tcx).abi(),
size: value.size(tcx).bytes(),
fields: vec![],
// Common code for structs and unions
let fields = || {
let field_count = layout.field_count();
let mut fields = Vec::with_capacity(field_count);

for i in 0..field_count {
let offset = layout.field_offset(cx, i).bytes();
let field_ty = layout.field_type(cx, i);
let type_name = item_path::with_forced_impl_filename_line(||{
field_ty.to_string()
});
let (name, public) = layout.field_info(cx, i);

let field = Field {
name,
offset,
type_name,
public,
};

fields.push(field);
}

fields
};

enum Fields<'a> {
WithDiscrim(&'a Struct),
NoDiscrim(&'a Struct),
}
let kind = match *layout {
// Basic primitive, nothing interesting to do
Layout::Scalar { value: Primitive::Int(..), .. } => {
TypeKind::PrimitiveInt
}
Layout::Scalar { value: Primitive::F32, .. } |
Layout::Scalar { value: Primitive::F64, .. } => {
TypeKind::PrimitiveFloat
}

let build_variant_info = |n: Option<ast::Name>,
flds: &[(ast::Name, Ty<'tcx>)],
layout: Fields| {
let (s, field_offsets) = match layout {
Fields::WithDiscrim(s) => (s, &s.offsets[1..]),
Fields::NoDiscrim(s) => (s, &s.offsets[0..]),
};
let field_info: Vec<_> =
flds.iter()
.zip(field_offsets.iter())
.map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset))
.collect();

session::VariantInfo {
name: n.map(|n|n.to_string()),
kind: if s.sized {
session::SizeKind::Exact
} else {
session::SizeKind::Min
},
align: s.align.abi(),
size: s.min_size.bytes(),
fields: field_info,
// Thin pointers; don't record these
Layout::Scalar { value: Primitive::Pointer, .. } |
Layout::RawNullablePointer { .. } => {
// TODO: do we maybe want to actually type RawNullablePointer?
// Option<Box>/Option<Rc> aren't quite the same as Option<&>...
return;
}
};

match *layout {
Layout::StructWrappedNullablePointer { nonnull: ref variant_layout,
nndiscr,
discrfield: _,
discrfield_source: _ } => {
debug!("print-type-size t: `{:?}` adt struct-wrapped nullable nndiscr {} is {:?}",
ty, nndiscr, variant_layout);
let variant_def = &adt_def.variants[nndiscr as usize];
let fields: Vec<_> =
variant_def.fields.iter()
.map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
.collect();
record(adt_kind.into(),
None,
vec![build_variant_info(Some(variant_def.name),
&fields,
Fields::NoDiscrim(variant_layout))]);
}
Layout::RawNullablePointer { nndiscr, value } => {
debug!("print-type-size t: `{:?}` adt raw nullable nndiscr {} is {:?}",
ty, nndiscr, value);
let variant_def = &adt_def.variants[nndiscr as usize];
record(adt_kind.into(), None,
vec![build_primitive_info(variant_def.name, &value)]);
}
Layout::Univariant { variant: ref variant_layout, non_zero: _ } => {
let variant_names = || {
adt_def.variants.iter().map(|v|format!("{}", v.name)).collect::<Vec<_>>()
};
debug!("print-type-size t: `{:?}` adt univariant {:?} variants: {:?}",
ty, variant_layout, variant_names());
assert!(adt_def.variants.len() <= 1,
"univariant with variants {:?}", variant_names());
if adt_def.variants.len() == 1 {
let variant_def = &adt_def.variants[0];
let fields: Vec<_> =
variant_def.fields.iter()
.map(|f| (f.name, f.ty(tcx, substs)))
.collect();
record(adt_kind.into(),
None,
vec![build_variant_info(Some(variant_def.name),
&fields,
Fields::NoDiscrim(variant_layout))]);
} else {
// (This case arises for *empty* enums; so give it
// zero variants.)
record(adt_kind.into(), None, vec![]);
// Structs
Layout::Vector { .. } |
Layout::FatPointer { .. } |
Layout::Univariant { .. } => {
TypeKind::Struct { fields: fields() }
}

// Union
Layout::UntaggedUnion { .. } => {
TypeKind::Union { fields: fields() }
}


// C-like Enums
Layout::CEnum { discr, signed, .. } => {
let base_type = item_path::with_forced_impl_filename_line(||{
discr.to_ty(&tcx, signed).to_string()
});

let def = if let ty::TyAdt(def, ..) = ty.sty { def } else { unreachable!() };

let mut cases = Vec::with_capacity(def.variants.len());
for (discr, variant) in def.discriminants(tcx).zip(def.variants.iter()) {
// TODO: i128?
let value = discr.to_u128_unchecked() as i64;
let name = variant.name.to_string();
let case = Case { name, value };
cases.push(case);
}

TypeKind::Enum { base_type, cases }
}

Layout::General { ref variants, discr, .. } => {
debug!("print-type-size t: `{:?}` adt general variants def {} layouts {} {:?}",
ty, adt_def.variants.len(), variants.len(), variants);
let variant_infos: Vec<_> =
adt_def.variants.iter()
.zip(variants.iter())
.map(|(variant_def, variant_layout)| {
let fields: Vec<_> =
variant_def.fields
.iter()
.map(|f| (f.name, f.ty(tcx, substs)))
.collect();
build_variant_info(Some(variant_def.name),
&fields,
Fields::WithDiscrim(variant_layout))
})
.collect();
record(adt_kind.into(), Some(discr.size()), variant_infos);
}

Layout::UntaggedUnion { ref variants } => {
debug!("print-type-size t: `{:?}` adt union variants {:?}",
ty, variants);
// layout does not currently store info about each
// variant...
record(adt_kind.into(), None, Vec::new());
}

Layout::CEnum { discr, .. } => {
debug!("print-type-size t: `{:?}` adt c-like enum", ty);
let variant_infos: Vec<_> =
adt_def.variants.iter()
.map(|variant_def| {
build_primitive_info(variant_def.name,
&Primitive::Int(discr))
})
.collect();
record(adt_kind.into(), Some(discr.size()), variant_infos);
}

// other cases provide little interesting (i.e. adjustable
// via representation tweaks) size info beyond total size.
Layout::Scalar { .. } |
Layout::Vector { .. } |
Layout::Array { .. } |
Layout::FatPointer { .. } => {
debug!("print-type-size t: `{:?}` adt other", ty);
record(adt_kind.into(), None, Vec::new())
// Don't expose details of tagged unions yet, as we
// reserve the right to do pretty extreme optimizations, and don't
// want to settle on an abstraction yet.
Layout::General { .. } |
StructWrappedNullablePointer { .. } => {
TypeKind::Opaque
}
}

Layout::Array { .. } => {
bug!("Arrays shouldn't ever make it here")
}
};

let size = layout.size(tcx).bytes();
let align = layout.align(tcx).abi();
let name = item_path::with_forced_impl_filename_line(||{
ty.to_string()
});
let public = true; // TODO

tcx.sess.code_stats.borrow_mut().insert(Type {
name, size, align, public, kind
});
}
}

@@ -2264,6 +2208,91 @@ impl<'a, 'tcx> TyLayout<'tcx> {
}
}

/// Gets the field name, and whether the field is `pub`, for use in print-type-info.
///
/// Fields of builtins have synthesized names that are valid C idents.
/// e.g. tuple.0 becomes tuple.item0
///
/// Note: some of these results aren't ever used by print-type-info, because these types
/// have a natural C ABI (i.e. TyArray).
fn field_info<C: LayoutTyper<'tcx>>(&self, cx: C, i: usize) -> (String, bool) {
let tcx = cx.tcx();

let ptr_field_info = |pointee: Ty<'tcx>| {
assert!(i < 2);
match tcx.struct_tail(pointee).sty {
ty::TySlice(..) |
ty::TyStr => {
if i == 0 {
("ptr".to_string(), false)
} else {
("len".to_string(), false)
}
}
ty::TyDynamic(..) => {
if i == 0 {
("data".to_string(), false)
} else {
("vtable".to_string(), false)
}
}
_ => bug!("TyLayout::field_info({:?}): not applicable", self)
}
};

match self.ty.sty {
ty::TyBool |
ty::TyChar |
ty::TyInt(_) |
ty::TyUint(_) |
ty::TyFloat(_) |
ty::TyFnPtr(_) |
ty::TyNever |
ty::TyFnDef(..) |
ty::TyDynamic(..) |
ty::TySlice(..) |
ty::TyStr => {
bug!("TyLayout::field_info({:?}): not applicable", self)
}

// Potentially-fat pointers.
ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
ptr_field_info(pointee)
}
ty::TyAdt(def, _) if def.is_box() => {
ptr_field_info(self.ty.boxed_ty())
}

// Arrays and slices.
ty::TyArray(..) => (format!("idx{}", i), false),

// Closures.
ty::TyClosure(..) => (format!("capture{}", i), false),

// Tuples
ty::TyTuple(..) => (format!("item{}", i), true),

// SIMD vectors
ty::TyAdt(def, ..) if def.repr.simd() => {
(format!("idx{}", i), true)
}

// ADTs.
ty::TyAdt(def, ..) => {
let field = &def.variants[self.variant_index.unwrap_or(0)].fields[i];
let public = if let Visibility::Public = field.vis { true } else { false };
let name = field.name.to_string();
(name, public)
}

ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) |
ty::TyInfer(_) | ty::TyError => {
bug!("TyLayout::field_type: unexpected type `{}`", self.ty)
}
}
}

pub fn field<C: LayoutTyper<'tcx>>(&self,
cx: C,
i: usize)
2 changes: 1 addition & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
@@ -226,7 +226,7 @@ pub fn compile_input(sess: &Session,
};

if sess.opts.debugging_opts.print_type_sizes {
sess.code_stats.borrow().print_type_sizes();
sess.code_stats.borrow_mut().print_type_sizes();
}

let (phase5_result, trans) = phase_5_run_llvm_passes(sess, trans);