Skip to content

Commit

Permalink
Record whether the vtable pointer is in self or a base member in `Has…
Browse files Browse the repository at this point in the history
…VtableAnalysis`
  • Loading branch information
fitzgen committed Oct 23, 2017
1 parent 02c46bb commit d3e39dc
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 40 deletions.
145 changes: 111 additions & 34 deletions src/ir/analysis/has_vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,66 @@ use super::{ConstrainResult, MonotoneFramework, generate_dependencies};
use ir::context::{BindgenContext, ItemId};
use ir::traversal::EdgeKind;
use ir::ty::TypeKind;
use std::cmp;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::hash_map::Entry;
use std::ops;

/// The result of the `HasVtableAnalysis` for an individual item.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord)]
pub enum HasVtableResult {
/// The item has a vtable, but the actual vtable pointer is in a base
/// member.
BaseHasVtable,

/// The item has a vtable and the actual vtable pointer is within this item.
SelfHasVtable,

/// The item does not have a vtable pointer.
No
}

impl Default for HasVtableResult {
fn default() -> Self {
HasVtableResult::No
}
}

impl cmp::PartialOrd for HasVtableResult {
fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
use self::HasVtableResult::*;

match (*self, *rhs) {
(x, y) if x == y => Some(cmp::Ordering::Equal),
(BaseHasVtable, _) => Some(cmp::Ordering::Greater),
(_, BaseHasVtable) => Some(cmp::Ordering::Less),
(SelfHasVtable, _) => Some(cmp::Ordering::Greater),
(_, SelfHasVtable) => Some(cmp::Ordering::Less),
_ => unreachable!(),
}
}
}

impl HasVtableResult {
/// Take the least upper bound of `self` and `rhs`.
pub fn join(self, rhs: Self) -> Self {
cmp::max(self, rhs)
}
}

impl ops::BitOr for HasVtableResult {
type Output = Self;

fn bitor(self, rhs: HasVtableResult) -> Self::Output {
self.join(rhs)
}
}

impl ops::BitOrAssign for HasVtableResult {
fn bitor_assign(&mut self, rhs: HasVtableResult) {
*self = self.join(rhs)
}
}

/// An analysis that finds for each IR item whether it has vtable or not
///
Expand All @@ -23,7 +81,7 @@ pub struct HasVtableAnalysis<'ctx> {

// The incremental result of this analysis's computation. Everything in this
// set definitely has a vtable.
have_vtable: HashSet<ItemId>,
have_vtable: HashMap<ItemId, HasVtableResult>,

// 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
Expand All @@ -47,26 +105,50 @@ impl<'ctx> HasVtableAnalysis<'ctx> {
}
}

fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult {
fn insert<Id: Into<ItemId>>(&mut self, id: Id, result: HasVtableResult) -> ConstrainResult {
if let HasVtableResult::No = result {
return ConstrainResult::Same;
}

let id = id.into();
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
match self.have_vtable.entry(id) {
Entry::Occupied(mut entry) => {
if *entry.get() < result {
entry.insert(result);
ConstrainResult::Changed
} else {
ConstrainResult::Same
}
}
Entry::Vacant(entry) => {
entry.insert(result);
ConstrainResult::Changed
}
}
}

fn forward<Id1, Id2>(&mut self, from: Id1, to: Id2) -> ConstrainResult
where
Id1: Into<ItemId>,
Id2: Into<ItemId>,
{
let from = from.into();
let to = to.into();

match self.have_vtable.get(&from).cloned() {
None => ConstrainResult::Same,
Some(r) => self.insert(to, r),
}
}
}

impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
type Node = ItemId;
type Extra = &'ctx BindgenContext;
type Output = HashSet<ItemId>;
type Output = HashMap<ItemId, HasVtableResult>;

fn new(ctx: &'ctx BindgenContext) -> HasVtableAnalysis<'ctx> {
let have_vtable = HashSet::new();
let have_vtable = HashMap::new();
let dependencies = generate_dependencies(ctx, Self::consider_edge);

HasVtableAnalysis {
Expand All @@ -81,11 +163,6 @@ impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
}

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() {
Expand All @@ -99,33 +176,28 @@ impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
TypeKind::Alias(t) |
TypeKind::ResolvedTypeRef(t) |
TypeKind::Reference(t) => {
if self.have_vtable.contains(&t.into()) {
self.insert(id)
} else {
ConstrainResult::Same
}
self.forward(t, id)
}

TypeKind::Comp(ref info) => {
let mut result = HasVtableResult::No;

if info.has_own_virtual_method() {
return self.insert(id);
result |= HasVtableResult::SelfHasVtable;
}

let bases_has_vtable = info.base_members().iter().any(|base| {
self.have_vtable.contains(&base.ty.into())
self.have_vtable.contains_key(&base.ty.into())
});
if bases_has_vtable {
self.insert(id)
} else {
ConstrainResult::Same
result |= HasVtableResult::BaseHasVtable;
}

self.insert(id, result)
}

TypeKind::TemplateInstantiation(ref inst) => {
if self.have_vtable.contains(&inst.template_definition().into()) {
self.insert(id)
} else {
ConstrainResult::Same
}
self.forward(inst.template_definition(), id)
}

_ => ConstrainResult::Same,
Expand All @@ -145,8 +217,13 @@ impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
}
}

impl<'ctx> From<HasVtableAnalysis<'ctx>> for HashSet<ItemId> {
impl<'ctx> From<HasVtableAnalysis<'ctx>> for HashMap<ItemId, HasVtableResult> {
fn from(analysis: HasVtableAnalysis<'ctx>) -> Self {
// We let the lack of an entry mean "No" to save space.
extra_assert!(analysis.have_vtable.values().all(|v| {
*v != HasVtableResult::No
}));

analysis.have_vtable
}
}
Expand Down
29 changes: 27 additions & 2 deletions src/ir/analysis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ pub use self::template_params::UsedTemplateParameters;
mod derive_debug;
pub use self::derive_debug::CannotDeriveDebug;
mod has_vtable;
pub use self::has_vtable::HasVtable;
pub use self::has_vtable::HasVtableAnalysis;
pub use self::has_vtable::{HasVtable, HasVtableAnalysis, HasVtableResult};
mod has_destructor;
pub use self::has_destructor::HasDestructorAnalysis;
mod derive_default;
Expand All @@ -64,6 +63,7 @@ use ir::context::{BindgenContext, ItemId};
use ir::traversal::{EdgeKind, Trace};
use std::collections::HashMap;
use std::fmt;
use std::ops;

/// An analysis in the monotone framework.
///
Expand Down Expand Up @@ -125,6 +125,7 @@ pub trait MonotoneFramework: Sized + fmt::Debug {

/// Whether an analysis's `constrain` function modified the incremental results
/// or not.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ConstrainResult {
/// The incremental results were updated, and the fix-point computation
/// should continue.
Expand All @@ -134,6 +135,30 @@ pub enum ConstrainResult {
Same,
}

impl Default for ConstrainResult {
fn default() -> Self {
ConstrainResult::Same
}
}

impl ops::BitOr for ConstrainResult {
type Output = Self;

fn bitor(self, rhs: ConstrainResult) -> Self::Output {
if self == ConstrainResult::Changed || rhs == ConstrainResult::Changed {
ConstrainResult::Changed
} else {
ConstrainResult::Same
}
}
}

impl ops::BitOrAssign for ConstrainResult {
fn bitor_assign(&mut self, rhs: ConstrainResult) {
*self = *self | rhs;
}
}

/// Run an analysis in the monotone framework.
pub fn analyze<Analysis>(extra: Analysis::Extra) -> Analysis::Output
where
Expand Down
8 changes: 4 additions & 4 deletions src/ir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
use super::analysis::{CannotDeriveCopy, CannotDeriveDebug,
CannotDeriveDefault, CannotDeriveHash,
CannotDerivePartialEqOrPartialOrd, HasTypeParameterInArray,
HasVtableAnalysis, HasDestructorAnalysis, UsedTemplateParameters,
HasFloat, analyze};
HasVtableAnalysis, HasVtableResult, HasDestructorAnalysis,
UsedTemplateParameters, HasFloat, analyze};
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault,
CanDeriveHash, CanDerivePartialOrd, CanDeriveOrd,
CanDerivePartialEq, CanDeriveEq, CannotDeriveReason};
Expand Down Expand Up @@ -432,7 +432,7 @@ pub struct BindgenContext {
///
/// Populated when we enter codegen by `compute_has_vtable`; always `None`
/// before that and `Some` after.
have_vtable: Option<HashSet<ItemId>>,
have_vtable: Option<HashMap<ItemId, HasVtableResult>>,

/// The set of (`ItemId's of`) types that has destructor.
///
Expand Down Expand Up @@ -1275,7 +1275,7 @@ impl BindgenContext {

// Look up the computed value for whether the item with `id` has a
// vtable or not.
self.have_vtable.as_ref().unwrap().contains(&id.into())
self.have_vtable.as_ref().unwrap().contains_key(&id.into())
}

/// Compute whether the type has a destructor.
Expand Down

0 comments on commit d3e39dc

Please sign in to comment.