Skip to content

Commit d3e39dc

Browse files
committed
Record whether the vtable pointer is in self or a base member in HasVtableAnalysis
1 parent 02c46bb commit d3e39dc

File tree

3 files changed

+142
-40
lines changed

3 files changed

+142
-40
lines changed

src/ir/analysis/has_vtable.rs

Lines changed: 111 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,66 @@ use super::{ConstrainResult, MonotoneFramework, generate_dependencies};
44
use ir::context::{BindgenContext, ItemId};
55
use ir::traversal::EdgeKind;
66
use ir::ty::TypeKind;
7+
use std::cmp;
78
use std::collections::HashMap;
8-
use std::collections::HashSet;
9+
use std::collections::hash_map::Entry;
10+
use std::ops;
11+
12+
/// The result of the `HasVtableAnalysis` for an individual item.
13+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord)]
14+
pub enum HasVtableResult {
15+
/// The item has a vtable, but the actual vtable pointer is in a base
16+
/// member.
17+
BaseHasVtable,
18+
19+
/// The item has a vtable and the actual vtable pointer is within this item.
20+
SelfHasVtable,
21+
22+
/// The item does not have a vtable pointer.
23+
No
24+
}
25+
26+
impl Default for HasVtableResult {
27+
fn default() -> Self {
28+
HasVtableResult::No
29+
}
30+
}
31+
32+
impl cmp::PartialOrd for HasVtableResult {
33+
fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
34+
use self::HasVtableResult::*;
35+
36+
match (*self, *rhs) {
37+
(x, y) if x == y => Some(cmp::Ordering::Equal),
38+
(BaseHasVtable, _) => Some(cmp::Ordering::Greater),
39+
(_, BaseHasVtable) => Some(cmp::Ordering::Less),
40+
(SelfHasVtable, _) => Some(cmp::Ordering::Greater),
41+
(_, SelfHasVtable) => Some(cmp::Ordering::Less),
42+
_ => unreachable!(),
43+
}
44+
}
45+
}
46+
47+
impl HasVtableResult {
48+
/// Take the least upper bound of `self` and `rhs`.
49+
pub fn join(self, rhs: Self) -> Self {
50+
cmp::max(self, rhs)
51+
}
52+
}
53+
54+
impl ops::BitOr for HasVtableResult {
55+
type Output = Self;
56+
57+
fn bitor(self, rhs: HasVtableResult) -> Self::Output {
58+
self.join(rhs)
59+
}
60+
}
61+
62+
impl ops::BitOrAssign for HasVtableResult {
63+
fn bitor_assign(&mut self, rhs: HasVtableResult) {
64+
*self = self.join(rhs)
65+
}
66+
}
967

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

2482
// The incremental result of this analysis's computation. Everything in this
2583
// set definitely has a vtable.
26-
have_vtable: HashSet<ItemId>,
84+
have_vtable: HashMap<ItemId, HasVtableResult>,
2785

2886
// Dependencies saying that if a key ItemId has been inserted into the
2987
// `have_vtable` set, then each of the ids in Vec<ItemId> need to be
@@ -47,26 +105,50 @@ impl<'ctx> HasVtableAnalysis<'ctx> {
47105
}
48106
}
49107

50-
fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult {
108+
fn insert<Id: Into<ItemId>>(&mut self, id: Id, result: HasVtableResult) -> ConstrainResult {
109+
if let HasVtableResult::No = result {
110+
return ConstrainResult::Same;
111+
}
112+
51113
let id = id.into();
52-
let was_not_already_in_set = self.have_vtable.insert(id);
53-
assert!(
54-
was_not_already_in_set,
55-
"We shouldn't try and insert {:?} twice because if it was \
56-
already in the set, `constrain` should have exited early.",
57-
id
58-
);
59-
ConstrainResult::Changed
114+
match self.have_vtable.entry(id) {
115+
Entry::Occupied(mut entry) => {
116+
if *entry.get() < result {
117+
entry.insert(result);
118+
ConstrainResult::Changed
119+
} else {
120+
ConstrainResult::Same
121+
}
122+
}
123+
Entry::Vacant(entry) => {
124+
entry.insert(result);
125+
ConstrainResult::Changed
126+
}
127+
}
128+
}
129+
130+
fn forward<Id1, Id2>(&mut self, from: Id1, to: Id2) -> ConstrainResult
131+
where
132+
Id1: Into<ItemId>,
133+
Id2: Into<ItemId>,
134+
{
135+
let from = from.into();
136+
let to = to.into();
137+
138+
match self.have_vtable.get(&from).cloned() {
139+
None => ConstrainResult::Same,
140+
Some(r) => self.insert(to, r),
141+
}
60142
}
61143
}
62144

63145
impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
64146
type Node = ItemId;
65147
type Extra = &'ctx BindgenContext;
66-
type Output = HashSet<ItemId>;
148+
type Output = HashMap<ItemId, HasVtableResult>;
67149

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

72154
HasVtableAnalysis {
@@ -81,11 +163,6 @@ impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
81163
}
82164

83165
fn constrain(&mut self, id: ItemId) -> ConstrainResult {
84-
if self.have_vtable.contains(&id) {
85-
// We've already computed that this type has a vtable and that can't
86-
// change.
87-
return ConstrainResult::Same;
88-
}
89166

90167
let item = self.ctx.resolve_item(id);
91168
let ty = match item.as_type() {
@@ -99,33 +176,28 @@ impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
99176
TypeKind::Alias(t) |
100177
TypeKind::ResolvedTypeRef(t) |
101178
TypeKind::Reference(t) => {
102-
if self.have_vtable.contains(&t.into()) {
103-
self.insert(id)
104-
} else {
105-
ConstrainResult::Same
106-
}
179+
self.forward(t, id)
107180
}
108181

109182
TypeKind::Comp(ref info) => {
183+
let mut result = HasVtableResult::No;
184+
110185
if info.has_own_virtual_method() {
111-
return self.insert(id);
186+
result |= HasVtableResult::SelfHasVtable;
112187
}
188+
113189
let bases_has_vtable = info.base_members().iter().any(|base| {
114-
self.have_vtable.contains(&base.ty.into())
190+
self.have_vtable.contains_key(&base.ty.into())
115191
});
116192
if bases_has_vtable {
117-
self.insert(id)
118-
} else {
119-
ConstrainResult::Same
193+
result |= HasVtableResult::BaseHasVtable;
120194
}
195+
196+
self.insert(id, result)
121197
}
122198

123199
TypeKind::TemplateInstantiation(ref inst) => {
124-
if self.have_vtable.contains(&inst.template_definition().into()) {
125-
self.insert(id)
126-
} else {
127-
ConstrainResult::Same
128-
}
200+
self.forward(inst.template_definition(), id)
129201
}
130202

131203
_ => ConstrainResult::Same,
@@ -145,8 +217,13 @@ impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
145217
}
146218
}
147219

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

src/ir/analysis/mod.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ pub use self::template_params::UsedTemplateParameters;
4343
mod derive_debug;
4444
pub use self::derive_debug::CannotDeriveDebug;
4545
mod has_vtable;
46-
pub use self::has_vtable::HasVtable;
47-
pub use self::has_vtable::HasVtableAnalysis;
46+
pub use self::has_vtable::{HasVtable, HasVtableAnalysis, HasVtableResult};
4847
mod has_destructor;
4948
pub use self::has_destructor::HasDestructorAnalysis;
5049
mod derive_default;
@@ -64,6 +63,7 @@ use ir::context::{BindgenContext, ItemId};
6463
use ir::traversal::{EdgeKind, Trace};
6564
use std::collections::HashMap;
6665
use std::fmt;
66+
use std::ops;
6767

6868
/// An analysis in the monotone framework.
6969
///
@@ -125,6 +125,7 @@ pub trait MonotoneFramework: Sized + fmt::Debug {
125125

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

138+
impl Default for ConstrainResult {
139+
fn default() -> Self {
140+
ConstrainResult::Same
141+
}
142+
}
143+
144+
impl ops::BitOr for ConstrainResult {
145+
type Output = Self;
146+
147+
fn bitor(self, rhs: ConstrainResult) -> Self::Output {
148+
if self == ConstrainResult::Changed || rhs == ConstrainResult::Changed {
149+
ConstrainResult::Changed
150+
} else {
151+
ConstrainResult::Same
152+
}
153+
}
154+
}
155+
156+
impl ops::BitOrAssign for ConstrainResult {
157+
fn bitor_assign(&mut self, rhs: ConstrainResult) {
158+
*self = *self | rhs;
159+
}
160+
}
161+
137162
/// Run an analysis in the monotone framework.
138163
pub fn analyze<Analysis>(extra: Analysis::Extra) -> Analysis::Output
139164
where

src/ir/context.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
use super::analysis::{CannotDeriveCopy, CannotDeriveDebug,
44
CannotDeriveDefault, CannotDeriveHash,
55
CannotDerivePartialEqOrPartialOrd, HasTypeParameterInArray,
6-
HasVtableAnalysis, HasDestructorAnalysis, UsedTemplateParameters,
7-
HasFloat, analyze};
6+
HasVtableAnalysis, HasVtableResult, HasDestructorAnalysis,
7+
UsedTemplateParameters, HasFloat, analyze};
88
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault,
99
CanDeriveHash, CanDerivePartialOrd, CanDeriveOrd,
1010
CanDerivePartialEq, CanDeriveEq, CannotDeriveReason};
@@ -432,7 +432,7 @@ pub struct BindgenContext {
432432
///
433433
/// Populated when we enter codegen by `compute_has_vtable`; always `None`
434434
/// before that and `Some` after.
435-
have_vtable: Option<HashSet<ItemId>>,
435+
have_vtable: Option<HashMap<ItemId, HasVtableResult>>,
436436

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

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

12811281
/// Compute whether the type has a destructor.

0 commit comments

Comments
 (0)