-
Notifications
You must be signed in to change notification settings - Fork 745
has vtable analysis #850
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
has vtable analysis #850
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Want to do a follow up PR to pull the creation of the dependencies graph out and share its definition between all of our analyses? This is getting copied with slight changes a little too much now, and its clear we need a helper function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.