diff options
author | Nick Fitzgerald <fitzgen@gmail.com> | 2017-07-25 16:47:35 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-25 16:47:35 -0700 |
commit | 69075f48c9b7c1ebf52ff4bec098dd15be162a78 (patch) | |
tree | e30acbf472320e713e5c5da224a46a09afebeff3 | |
parent | 23dbe487b8d2a142fb03d9aefc3b4dd29aa78009 (diff) | |
parent | 0969a8d6a226590fc293c3e4d79aafb3da05a34b (diff) |
Merge pull request #850 from photoszzt/has_vtable
has vtable analysis
-rw-r--r-- | src/codegen/mod.rs | 10 | ||||
-rw-r--r-- | src/ir/analysis/has_vtable.rs | 186 | ||||
-rw-r--r-- | src/ir/analysis/mod.rs | 3 | ||||
-rw-r--r-- | src/ir/comp.rs | 45 | ||||
-rw-r--r-- | src/ir/context.rs | 28 | ||||
-rw-r--r-- | src/ir/derive.rs | 2 | ||||
-rw-r--r-- | src/ir/item.rs | 21 | ||||
-rw-r--r-- | src/ir/layout.rs | 2 | ||||
-rw-r--r-- | src/ir/template.rs | 5 | ||||
-rw-r--r-- | src/ir/ty.rs | 32 |
10 files changed, 272 insertions, 62 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 78d73cd0..1c87d14f 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1473,7 +1473,7 @@ impl CodeGenerator for CompInfo { // the parent too. let mut fields = vec![]; let mut struct_layout = StructLayoutTracker::new(ctx, self, &canonical_name); - if self.needs_explicit_vtable(ctx) { + if self.needs_explicit_vtable(ctx, item) { let vtable = Vtable::new(item.id(), self.methods(), self.base_members()); vtable.codegen(ctx, result, item); @@ -1504,7 +1504,7 @@ impl CodeGenerator for CompInfo { // NB: We won't include unsized types in our base chain because they // would contribute to our size given the dummy field we insert for // unsized types. - if base_ty.is_unsized(ctx) { + if base_ty.is_unsized(ctx, &base.ty) { continue; } @@ -1583,7 +1583,7 @@ impl CodeGenerator for CompInfo { warn!("Opaque type without layout! Expect dragons!"); } } - } else if !is_union && !self.is_unsized(ctx) { + } else if !is_union && !self.is_unsized(ctx, &item.id()) { if let Some(padding_field) = layout.and_then(|layout| { struct_layout.pad_struct(layout) @@ -1607,7 +1607,7 @@ impl CodeGenerator for CompInfo { // // NOTE: This check is conveniently here to avoid the dummy fields we // may add for unused template parameters. - if self.is_unsized(ctx) { + if self.is_unsized(ctx, &item.id()) { let has_address = if is_opaque { // Generate the address field if it's an opaque type and // couldn't determine the layout of the blob. @@ -1707,7 +1707,7 @@ impl CodeGenerator for CompInfo { let too_many_base_vtables = self.base_members() .iter() .filter(|base| { - ctx.resolve_type(base.ty).has_vtable(ctx) + ctx.lookup_item_id_has_vtable(&base.ty) }) .count() > 1; diff --git a/src/ir/analysis/has_vtable.rs b/src/ir/analysis/has_vtable.rs new file mode 100644 index 00000000..d6287742 --- /dev/null +++ b/src/ir/analysis/has_vtable.rs @@ -0,0 +1,186 @@ +//! Determining which types has vtable +use super::{ConstrainResult, MonotoneFramework}; +use std::collections::HashSet; +use std::collections::HashMap; +use ir::context::{BindgenContext, ItemId}; +use ir::traversal::EdgeKind; +use ir::ty::TypeKind; +use ir::traversal::Trace; + +/// An analysis that finds for each IR item whether it has vtable or not +/// +/// We use the monotone function `has vtable`, defined as follows: +/// +/// * If T is a type alias, a templated alias, an indirection to another type, +/// or a reference of a type, T has vtable if the type T refers to has vtable. +/// * If T is a compound type, T has vtable if we saw a virtual function when +/// parsing it or any of its base member has vtable. +/// * If T is an instantiation of an abstract template definition, T has +/// vtable if template definition has vtable +#[derive(Debug, Clone)] +pub struct HasVtableAnalysis<'ctx, 'gen> + where 'gen: 'ctx +{ + ctx: &'ctx BindgenContext<'gen>, + + // The incremental result of this analysis's computation. Everything in this + // set definitely has a vtable. + have_vtable: HashSet<ItemId>, + + // Dependencies saying that if a key ItemId has been inserted into the + // `have_vtable` set, then each of the ids in Vec<ItemId> need to be + // considered again. + // + // This is a subset of the natural IR graph with reversed edges, where we + // only include the edges from the IR graph that can affect whether a type + // has a vtable or not. + dependencies: HashMap<ItemId, Vec<ItemId>>, +} + +impl<'ctx, 'gen> HasVtableAnalysis<'ctx, 'gen> { + fn consider_edge(kind: EdgeKind) -> bool { + match kind { + // These are the only edges that can affect whether a type has a + // vtable or not. + EdgeKind::TypeReference | + EdgeKind::BaseMember | + EdgeKind::TemplateDeclaration => true, + _ => false, + } + } + + fn insert(&mut self, id: ItemId) -> ConstrainResult { + let was_not_already_in_set = self.have_vtable.insert(id); + assert!( + was_not_already_in_set, + "We shouldn't try and insert {:?} twice because if it was \ + already in the set, `constrain` should have exited early.", + id + ); + ConstrainResult::Changed + } +} + +impl<'ctx, 'gen> MonotoneFramework for HasVtableAnalysis<'ctx, 'gen> { + type Node = ItemId; + type Extra = &'ctx BindgenContext<'gen>; + type Output = HashSet<ItemId>; + + fn new(ctx: &'ctx BindgenContext<'gen>) -> HasVtableAnalysis<'ctx, 'gen> { + let have_vtable = HashSet::new(); + let mut dependencies = HashMap::new(); + + for &item in ctx.whitelisted_items() { + dependencies.entry(item).or_insert(vec![]); + + { + // We reverse our natural IR graph edges to find dependencies + // between nodes. + item.trace(ctx, &mut |sub_item: ItemId, edge_kind| { + if ctx.whitelisted_items().contains(&sub_item) && + Self::consider_edge(edge_kind) { + dependencies.entry(sub_item) + .or_insert(vec![]) + .push(item); + } + }, &()); + } + } + + HasVtableAnalysis { + ctx, + have_vtable, + dependencies, + } + } + + fn initial_worklist(&self) -> Vec<ItemId> { + self.ctx.whitelisted_items().iter().cloned().collect() + } + + fn constrain(&mut self, id: ItemId) -> ConstrainResult { + if self.have_vtable.contains(&id) { + // We've already computed that this type has a vtable and that can't + // change. + return ConstrainResult::Same; + } + + let item = self.ctx.resolve_item(id); + let ty = match item.as_type() { + None => return ConstrainResult::Same, + Some(ty) => ty + }; + + // TODO #851: figure out a way to handle deriving from template type parameters. + match *ty.kind() { + TypeKind::TemplateAlias(t, _) | + TypeKind::Alias(t) | + TypeKind::ResolvedTypeRef(t) | + TypeKind::Reference(t) => { + if self.have_vtable.contains(&t) { + self.insert(id) + } else { + ConstrainResult::Same + } + }, + + TypeKind::Comp(ref info) => { + if info.has_own_virtual_method() { + return self.insert(id); + } + let bases_has_vtable = info.base_members().iter().any(|base| { + self.have_vtable.contains(&base.ty) + }); + if bases_has_vtable { + self.insert(id) + } else { + ConstrainResult::Same + } + }, + + TypeKind::TemplateInstantiation(ref inst) => { + if self.have_vtable.contains(&inst.template_definition()) { + self.insert(id) + } else { + ConstrainResult::Same + } + }, + + _ => ConstrainResult::Same, + } + } + + fn each_depending_on<F>(&self, id: ItemId, mut f: F) + where F: FnMut(ItemId), + { + if let Some(edges) = self.dependencies.get(&id) { + for item in edges { + trace!("enqueue {:?} into worklist", item); + f(*item); + } + } + } +} + +impl<'ctx, 'gen> From<HasVtableAnalysis<'ctx, 'gen>> for HashSet<ItemId> { + fn from(analysis: HasVtableAnalysis<'ctx, 'gen>) -> Self { + analysis.have_vtable + } +} + +/// A convenience trait for the things for which we might wonder if they have a +/// vtable during codegen. +/// +/// This is not for _computing_ whether the thing has a vtable, it is for +/// looking up the results of the HasVtableAnalysis's computations for a +/// specific thing. +pub trait HasVtable { + + /// Implementations can define this type to get access to any extra + /// information required to determine whether they have vtable. If + /// extra information is unneeded, then this should simply be the unit type. + type Extra; + + /// Return `true` if this thing has vtable, `false` otherwise. + fn has_vtable(&self, ctx: &BindgenContext, extra: &Self::Extra) -> bool; +} diff --git a/src/ir/analysis/mod.rs b/src/ir/analysis/mod.rs index 5fe6785b..11467438 100644 --- a/src/ir/analysis/mod.rs +++ b/src/ir/analysis/mod.rs @@ -42,6 +42,9 @@ mod template_params; pub use self::template_params::UsedTemplateParameters; mod derive_debug; pub use self::derive_debug::CannotDeriveDebug; +mod has_vtable; +pub use self::has_vtable::HasVtableAnalysis; +pub use self::has_vtable::HasVtable; use std::fmt; diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 0960ee11..327711fe 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -701,7 +701,7 @@ impl FieldMethods for FieldData { } } -impl CanDeriveDefault for Field { +impl<'a> CanDeriveDefault<'a> for Field { type Extra = (); fn can_derive_default(&self, ctx: &BindgenContext, _: ()) -> bool { @@ -819,7 +819,7 @@ pub struct CompInfo { /// Whether this type should generate an vtable (TODO: Should be able to /// look at the virtual methods and ditch this field). - has_vtable: bool, + has_own_virtual_method: bool, /// Whether this type has destructor. has_destructor: bool, @@ -870,7 +870,7 @@ impl CompInfo { base_members: vec![], inner_types: vec![], inner_vars: vec![], - has_vtable: false, + has_own_virtual_method: false, has_destructor: false, has_nonempty_base: false, has_non_type_template_params: false, @@ -883,10 +883,10 @@ impl CompInfo { } /// Is this compound type unsized? - pub fn is_unsized(&self, ctx: &BindgenContext) -> bool { - !self.has_vtable(ctx) && self.fields().is_empty() && + pub fn is_unsized(&self, ctx: &BindgenContext, itemid: &ItemId) -> bool { + !ctx.lookup_item_id_has_vtable(itemid) && self.fields().is_empty() && self.base_members.iter().all(|base| { - ctx.resolve_type(base.ty).canonical_type(ctx).is_unsized(ctx) + ctx.resolve_type(base.ty).canonical_type(ctx).is_unsized(ctx, &base.ty) }) } @@ -964,13 +964,10 @@ impl CompInfo { self.has_non_type_template_params } - /// Does this type have a virtual table? - pub fn has_vtable(&self, ctx: &BindgenContext) -> bool { - self.has_vtable || - self.base_members().iter().any(|base| { - ctx.resolve_type(base.ty) - .has_vtable(ctx) - }) + /// Do we see a virtual function during parsing? + /// Get the has_own_virtual_method boolean. + pub fn has_own_virtual_method(&self) -> bool { + return self.has_own_virtual_method; } /// Get this type's set of methods. @@ -1169,7 +1166,7 @@ impl CompInfo { } CXCursor_CXXBaseSpecifier => { let is_virtual_base = cur.is_virtual_base(); - ci.has_vtable |= is_virtual_base; + ci.has_own_virtual_method |= is_virtual_base; let kind = if is_virtual_base { BaseKind::Virtual @@ -1192,7 +1189,7 @@ impl CompInfo { debug_assert!(!(is_static && is_virtual), "How?"); ci.has_destructor |= cur.kind() == CXCursor_Destructor; - ci.has_vtable |= is_virtual; + ci.has_own_virtual_method |= is_virtual; // This used to not be here, but then I tried generating // stylo bindings with this (without path filters), and @@ -1335,8 +1332,8 @@ impl CompInfo { /// Returns whether this type needs an explicit vtable because it has /// virtual methods and none of its base classes has already a vtable. - pub fn needs_explicit_vtable(&self, ctx: &BindgenContext) -> bool { - self.has_vtable(ctx) && + pub fn needs_explicit_vtable(&self, ctx: &BindgenContext, item: &Item) -> bool { + ctx.lookup_item_id_has_vtable(&item.id()) && !self.base_members.iter().any(|base| { // NB: Ideally, we could rely in all these types being `comp`, and // life would be beautiful. @@ -1347,7 +1344,7 @@ impl CompInfo { ctx.resolve_type(base.ty) .canonical_type(ctx) .as_comp() - .map_or(false, |ci| ci.has_vtable(ctx)) + .map_or(false, |_| ctx.lookup_item_id_has_vtable(&base.ty)) }) } @@ -1368,7 +1365,7 @@ impl DotAttributes for CompInfo { { writeln!(out, "<tr><td>CompKind</td><td>{:?}</td></tr>", self.kind)?; - if self.has_vtable { + if self.has_own_virtual_method { writeln!(out, "<tr><td>has_vtable</td><td>true</td></tr>")?; } @@ -1424,12 +1421,12 @@ impl TemplateParameters for CompInfo { } } -impl CanDeriveDefault for CompInfo { - type Extra = Option<Layout>; +impl<'a> CanDeriveDefault<'a> for CompInfo { + type Extra = (&'a Item, Option<Layout>); fn can_derive_default(&self, ctx: &BindgenContext, - layout: Option<Layout>) + (item, layout): (&Item, Option<Layout>)) -> bool { // We can reach here recursively via template parameters of a member, // for example. @@ -1452,8 +1449,8 @@ impl CanDeriveDefault for CompInfo { self.detect_derive_default_cycle.set(true); - let can_derive_default = !self.has_vtable(ctx) && - !self.needs_explicit_vtable(ctx) && + let can_derive_default = !ctx.lookup_item_id_has_vtable(&item.id()) && + !self.needs_explicit_vtable(ctx, item) && self.base_members .iter() .all(|base| base.ty.can_derive_default(ctx, ())) && diff --git a/src/ir/context.rs b/src/ir/context.rs index 1f6580f0..3232e8e6 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -5,7 +5,7 @@ use super::int::IntKind; use super::item::{IsOpaque, Item, ItemAncestors, ItemCanonicalPath, ItemSet}; use super::item_kind::ItemKind; use super::module::{Module, ModuleKind}; -use super::analysis::{analyze, UsedTemplateParameters, CannotDeriveDebug}; +use super::analysis::{analyze, UsedTemplateParameters, CannotDeriveDebug, HasVtableAnalysis}; use super::template::{TemplateInstantiation, TemplateParameters}; use super::traversal::{self, Edge, ItemTraversal}; use super::ty::{FloatKind, Type, TypeKind}; @@ -47,7 +47,7 @@ impl CanDeriveDebug for ItemId { } } -impl CanDeriveDefault for ItemId { +impl<'a> CanDeriveDefault<'a> for ItemId { type Extra = (); fn can_derive_default(&self, ctx: &BindgenContext, _: ()) -> bool { @@ -184,6 +184,12 @@ pub struct BindgenContext<'ctx> { /// This is populated when we enter codegen by `compute_can_derive_debug` /// and is always `None` before that and `Some` after. cant_derive_debug: Option<HashSet<ItemId>>, + + /// The set of (`ItemId's of`) types that has vtable. + /// + /// Populated when we enter codegen by `compute_has_vtable`; always `None` + /// before that and `Some` after. + have_vtable: Option<HashSet<ItemId>>, } /// A traversal of whitelisted items. @@ -310,6 +316,7 @@ impl<'ctx> BindgenContext<'ctx> { need_bitfield_allocation: Default::default(), needs_mangling_hack: needs_mangling_hack, cant_derive_debug: None, + have_vtable: None, }; me.add_item(root_module, None, None); @@ -780,6 +787,7 @@ impl<'ctx> BindgenContext<'ctx> { // messes with them correctly. self.assert_every_item_in_a_module(); + self.compute_has_vtable(); self.find_used_template_parameters(); self.compute_cant_derive_debug(); @@ -847,6 +855,22 @@ impl<'ctx> BindgenContext<'ctx> { } } + /// Compute whether the type has vtable. + fn compute_has_vtable(&mut self) { + assert!(self.have_vtable.is_none()); + self.have_vtable = Some(analyze::<HasVtableAnalysis>(self)); + } + + /// Look up whether the item with `id` has vtable or not. + pub fn lookup_item_id_has_vtable(&self, id: &ItemId) -> bool { + assert!(self.in_codegen_phase(), + "We only compute vtables when we enter codegen"); + + // Look up the computed value for whether the item with `id` has a + // vtable or not. + self.have_vtable.as_ref().unwrap().contains(id) + } + fn find_used_template_parameters(&mut self) { if self.options.whitelist_recursively { let used_params = analyze::<UsedTemplateParameters>(self); diff --git a/src/ir/derive.rs b/src/ir/derive.rs index 59548163..446d2435 100644 --- a/src/ir/derive.rs +++ b/src/ir/derive.rs @@ -90,7 +90,7 @@ pub trait CanDeriveCopy<'a> { /// to be a recursive method that checks whether all the proper members can /// derive default or not, because of the limit rust has on 32 items as max in the /// array. -pub trait CanDeriveDefault { +pub trait CanDeriveDefault<'a> { /// Implementations can define this type to get access to any extra /// information required to determine whether they can derive `Default`. If /// extra information is unneeded, then this should simply be the unit type. diff --git a/src/ir/item.rs b/src/ir/item.rs index 7f3afefb..b3a26bb7 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -14,6 +14,7 @@ use super::module::Module; use super::template::{AsTemplateParam, TemplateParameters}; use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::{Type, TypeKind}; +use super::analysis::HasVtable; use clang; use clang_sys; use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; @@ -277,7 +278,7 @@ impl CanDeriveDebug for Item { } } -impl CanDeriveDefault for Item { +impl<'a> CanDeriveDefault<'a> for Item { type Extra = (); fn can_derive_default(&self, ctx: &BindgenContext, _: ()) -> bool { @@ -289,7 +290,7 @@ impl CanDeriveDefault for Item { .map_or(false, |l| l.opaque().can_derive_default(ctx, ())) } else { - ty.can_derive_default(ctx, ()) + ty.can_derive_default(ctx, self) } } _ => false, @@ -947,6 +948,22 @@ impl IsOpaque for Item { } } +impl HasVtable for ItemId { + type Extra = (); + + fn has_vtable(&self, ctx: &BindgenContext, _: &()) -> bool { + ctx.lookup_item_id_has_vtable(self) + } +} + +impl HasVtable for Item { + type Extra = (); + + fn has_vtable(&self, ctx: &BindgenContext, _: &()) -> bool { + ctx.lookup_item_id_has_vtable(&self.id()) + } +} + /// A set of items. pub type ItemSet = BTreeSet<ItemId>; diff --git a/src/ir/layout.rs b/src/ir/layout.rs index 91dd9d6c..03496ad0 100644 --- a/src/ir/layout.rs +++ b/src/ir/layout.rs @@ -111,7 +111,7 @@ impl CanTriviallyDeriveDebug for Opaque { } } -impl CanDeriveDefault for Opaque { +impl<'a> CanDeriveDefault<'a> for Opaque { type Extra = (); fn can_derive_default(&self, _: &BindgenContext, _: ()) -> bool { diff --git a/src/ir/template.rs b/src/ir/template.rs index 25d1d438..6147365e 100644 --- a/src/ir/template.rs +++ b/src/ir/template.rs @@ -292,11 +292,6 @@ impl TemplateInstantiation { Some(TemplateInstantiation::new(template_definition, template_args)) } - /// Does this instantiation have a vtable? - pub fn has_vtable(&self, ctx: &BindgenContext) -> bool { - ctx.resolve_type(self.definition).has_vtable(ctx) - } - /// Does this instantiation have a destructor? pub fn has_destructor(&self, ctx: &BindgenContext) -> bool { ctx.resolve_type(self.definition).has_destructor(ctx) || diff --git a/src/ir/ty.rs b/src/ir/ty.rs index 5cbc4cf6..b189ceac 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -237,19 +237,6 @@ impl Type { }) } - /// Whether this type has a vtable. - pub fn has_vtable(&self, ctx: &BindgenContext) -> bool { - // FIXME: Can we do something about template parameters? Huh... - match self.kind { - TypeKind::TemplateAlias(t, _) | - TypeKind::Alias(t) | - TypeKind::ResolvedTypeRef(t) => ctx.resolve_type(t).has_vtable(ctx), - TypeKind::Comp(ref info) => info.has_vtable(ctx), - TypeKind::TemplateInstantiation(ref inst) => inst.has_vtable(ctx), - _ => false, - } - } - /// Returns whether this type has a destructor. pub fn has_destructor(&self, ctx: &BindgenContext) -> bool { match self.kind { @@ -547,10 +534,10 @@ impl CanDeriveDebug for Type { } } -impl CanDeriveDefault for Type { - type Extra = (); +impl<'a> CanDeriveDefault<'a> for Type { + type Extra = &'a Item; - fn can_derive_default(&self, ctx: &BindgenContext, _: ()) -> bool { + fn can_derive_default(&self, ctx: &BindgenContext, item: &Item) -> bool { match self.kind { TypeKind::Array(t, len) => { len <= RUST_DERIVE_IN_ARRAY_LIMIT && @@ -560,7 +547,7 @@ impl CanDeriveDefault for Type { TypeKind::TemplateAlias(t, _) | TypeKind::Alias(t) => t.can_derive_default(ctx, ()), TypeKind::Comp(ref info) => { - info.can_derive_default(ctx, self.layout(ctx)) + info.can_derive_default(ctx, (&item, self.layout(ctx))) } TypeKind::Opaque => { self.layout @@ -745,23 +732,24 @@ impl Type { /// derive whether we should generate a dummy `_address` field for structs, /// to comply to the C and C++ layouts, that specify that every type needs /// to be addressable. - pub fn is_unsized(&self, ctx: &BindgenContext) -> bool { + pub fn is_unsized(&self, ctx: &BindgenContext, itemid: &ItemId) -> bool { debug_assert!(ctx.in_codegen_phase(), "Not yet"); match self.kind { TypeKind::Void => true, - TypeKind::Comp(ref ci) => ci.is_unsized(ctx), + TypeKind::Comp(ref ci) => ci.is_unsized(ctx, itemid), TypeKind::Opaque => self.layout.map_or(true, |l| l.size == 0), TypeKind::Array(inner, size) => { - size == 0 || ctx.resolve_type(inner).is_unsized(ctx) + size == 0 || ctx.resolve_type(inner).is_unsized(ctx, &inner) } TypeKind::ResolvedTypeRef(inner) | TypeKind::Alias(inner) | TypeKind::TemplateAlias(inner, _) => { - ctx.resolve_type(inner).is_unsized(ctx) + ctx.resolve_type(inner).is_unsized(ctx, &inner) } TypeKind::TemplateInstantiation(ref inst) => { - ctx.resolve_type(inst.template_definition()).is_unsized(ctx) + let definition = inst.template_definition(); + ctx.resolve_type(definition).is_unsized(ctx, &definition) } TypeKind::Named | TypeKind::Int(..) | |