Skip to content

Commit 17adb13

Browse files
author
bors-servo
authored
Auto merge of #1084 - fitzgen:explicit-vtable-pointer-refactor, r=fitzgen
Explicit vtable pointer refactor r? @emilio This should make it easier to move padding into its own pass, rather than inside codegen, which should in turn let us start handling (or at least intelligently determining when we *can't* handle) `#pragma pack(..)` and other things that affect layout in exotic ways that we can only indirectly observe. See each commit for details. The reason for the first commit is this: when we compare, we rustfmt both expected and actual, so the expectations don't get updated to be formatted nicely until some patch that changes what gets generated. This is annoying, however, when debugging some minor difference, and not being able to see what it is easily. Best to just bite the bullet and format all the expectations the once and make the problem go away.
2 parents 8582a90 + 6f87f0b commit 17adb13

File tree

243 files changed

+10707
-4926
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

243 files changed

+10707
-4926
lines changed

src/codegen/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1424,7 +1424,7 @@ impl CodeGenerator for CompInfo {
14241424
//
14251425
// FIXME: Once we generate proper vtables, we need to codegen the
14261426
// vtable, but *not* generate a field for it in the case that
1427-
// needs_explicit_vtable is false but has_vtable is true.
1427+
// HasVtable::has_vtable_ptr is false but HasVtable::has_vtable is true.
14281428
//
14291429
// Also, we need to generate the vtable in such a way it "inherits" from
14301430
// the parent too.
@@ -1434,7 +1434,7 @@ impl CodeGenerator for CompInfo {
14341434
StructLayoutTracker::new(ctx, self, &canonical_name);
14351435

14361436
if !is_opaque {
1437-
if self.needs_explicit_vtable(ctx, item) {
1437+
if item.has_vtable_ptr(ctx) {
14381438
let vtable =
14391439
Vtable::new(item.id(), self.methods(), self.base_members());
14401440
vtable.codegen(ctx, result, item);

src/ir/analysis/has_vtable.rs

+120-34
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,7 @@ 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-
}
166+
trace!("constrain {:?}", id);
89167

90168
let item = self.ctx.resolve_item(id);
91169
let ty = match item.as_type() {
@@ -99,33 +177,32 @@ impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
99177
TypeKind::Alias(t) |
100178
TypeKind::ResolvedTypeRef(t) |
101179
TypeKind::Reference(t) => {
102-
if self.have_vtable.contains(&t.into()) {
103-
self.insert(id)
104-
} else {
105-
ConstrainResult::Same
106-
}
180+
trace!(" aliases and references forward to their inner type");
181+
self.forward(t, id)
107182
}
108183

109184
TypeKind::Comp(ref info) => {
185+
trace!(" comp considers its own methods and bases");
186+
let mut result = HasVtableResult::No;
187+
110188
if info.has_own_virtual_method() {
111-
return self.insert(id);
189+
trace!(" comp has its own virtual method");
190+
result |= HasVtableResult::SelfHasVtable;
112191
}
192+
113193
let bases_has_vtable = info.base_members().iter().any(|base| {
114-
self.have_vtable.contains(&base.ty.into())
194+
trace!(" comp has a base with a vtable: {:?}", base);
195+
self.have_vtable.contains_key(&base.ty.into())
115196
});
116197
if bases_has_vtable {
117-
self.insert(id)
118-
} else {
119-
ConstrainResult::Same
198+
result |= HasVtableResult::BaseHasVtable;
120199
}
200+
201+
self.insert(id, result)
121202
}
122203

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

131208
_ => ConstrainResult::Same,
@@ -145,8 +222,13 @@ impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
145222
}
146223
}
147224

148-
impl<'ctx> From<HasVtableAnalysis<'ctx>> for HashSet<ItemId> {
225+
impl<'ctx> From<HasVtableAnalysis<'ctx>> for HashMap<ItemId, HasVtableResult> {
149226
fn from(analysis: HasVtableAnalysis<'ctx>) -> Self {
227+
// We let the lack of an entry mean "No" to save space.
228+
extra_assert!(analysis.have_vtable.values().all(|v| {
229+
*v != HasVtableResult::No
230+
}));
231+
150232
analysis.have_vtable
151233
}
152234
}
@@ -160,4 +242,8 @@ impl<'ctx> From<HasVtableAnalysis<'ctx>> for HashSet<ItemId> {
160242
pub trait HasVtable {
161243
/// Return `true` if this thing has vtable, `false` otherwise.
162244
fn has_vtable(&self, ctx: &BindgenContext) -> bool;
245+
246+
/// Return `true` if this thing has an actual vtable pointer in itself, as
247+
/// opposed to transitively in a base member.
248+
fn has_vtable_ptr(&self, ctx: &BindgenContext) -> bool;
163249
}

src/ir/analysis/mod.rs

+27-2
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/comp.rs

+1-22
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,7 @@ impl CompInfo {
10121012

10131013
/// Is this compound type unsized?
10141014
pub fn is_unsized(&self, ctx: &BindgenContext, id: TypeId) -> bool {
1015-
!ctx.lookup_has_vtable(id) && self.fields().is_empty() &&
1015+
!id.has_vtable(ctx) && self.fields().is_empty() &&
10161016
self.base_members.iter().all(|base| {
10171017
ctx.resolve_type(base.ty).canonical_type(ctx).is_unsized(
10181018
ctx,
@@ -1451,27 +1451,6 @@ impl CompInfo {
14511451
self.packed
14521452
}
14531453

1454-
/// Returns whether this type needs an explicit vtable because it has
1455-
/// virtual methods and none of its base classes has already a vtable.
1456-
pub fn needs_explicit_vtable(
1457-
&self,
1458-
ctx: &BindgenContext,
1459-
item: &Item,
1460-
) -> bool {
1461-
item.has_vtable(ctx) && !self.base_members.iter().any(|base| {
1462-
// NB: Ideally, we could rely in all these types being `comp`, and
1463-
// life would be beautiful.
1464-
//
1465-
// Unfortunately, given the way we implement --match-pat, and also
1466-
// that you can inherit from templated types, we need to handle
1467-
// other cases here too.
1468-
ctx.resolve_type(base.ty)
1469-
.canonical_type(ctx)
1470-
.as_comp()
1471-
.map_or(false, |_| base.ty.has_vtable(ctx))
1472-
})
1473-
}
1474-
14751454
/// Returns true if compound type has been forward declared
14761455
pub fn is_forward_declaration(&self) -> bool {
14771456
self.is_forward_declaration

src/ir/context.rs

+10-5
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
///
@@ -1267,15 +1267,20 @@ impl BindgenContext {
12671267
}
12681268

12691269
/// Look up whether the item with `id` has vtable or not.
1270-
pub fn lookup_has_vtable(&self, id: TypeId) -> bool {
1270+
pub fn lookup_has_vtable(&self, id: TypeId) -> HasVtableResult {
12711271
assert!(
12721272
self.in_codegen_phase(),
12731273
"We only compute vtables when we enter codegen"
12741274
);
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
1279+
.as_ref()
1280+
.unwrap()
1281+
.get(&id.into())
1282+
.cloned()
1283+
.unwrap_or(HasVtableResult::No)
12791284
}
12801285

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

0 commit comments

Comments
 (0)