Skip to content

Commit 7e22fca

Browse files
committed
Find the set of template parameters used for any given IR node
Rather than determining whether any given template parameter is used at all globally, determine the set of template parameters used by any given IR node.
1 parent cf3b459 commit 7e22fca

File tree

1 file changed

+181
-53
lines changed

1 file changed

+181
-53
lines changed

src/ir/named.rs

Lines changed: 181 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,50 @@
7676
//! fixed-point.
7777
//!
7878
//! We use the "monotone framework" for this fix-point analysis where our
79-
//! lattice is the powerset of the template parameters that appear in the input
80-
//! C++ header, our join function is set union, and we use the
81-
//! `ir::traversal::Trace` trait to implement the work-list optimization so we
82-
//! don't have to revisit every node in the graph when for every iteration
83-
//! towards the fix-point.
79+
//! lattice is the mapping from each IR item to the powerset of the template
80+
//! parameters that appear in the input C++ header, our join function is set
81+
//! union, and we use the `ir::traversal::Trace` trait to implement the
82+
//! work-list optimization so we don't have to revisit every node in the graph
83+
//! when for every iteration towards the fix-point.
84+
//!
85+
//! A lattice is a set with a partial ordering between elements, where there is
86+
//! a single least upper bound and a single greatest least bound for every
87+
//! subset. We are dealing with finite lattices, which means that it has a
88+
//! finite number of elements, and it follows that there exists a single top and
89+
//! a single bottom member of the lattice. For example, the power set of a
90+
//! finite set forms a finite lattice where partial ordering is defined by set
91+
//! inclusion, that is `a <= b` if `a` is a subset of `b`. Here is the finite
92+
//! lattice constructed from the set {0,1,2}:
93+
//!
94+
//! ```text
95+
//! .----- Top = {0,1,2} -----.
96+
//! / | \
97+
//! / | \
98+
//! / | \
99+
//! {0,1} -------. {0,2} .--------- {1,2}
100+
//! | \ / \ / |
101+
//! | / \ |
102+
//! | / \ / \ |
103+
//! {0} --------' {1} `---------- {2}
104+
//! \ | /
105+
//! \ | /
106+
//! \ | /
107+
//! `------ Bottom = {} ------'
108+
//! ```
109+
//!
110+
//! A monotone function `f` is a function where if `x <= y`, then it holds that
111+
//! `f(x) <= f(y)`. It should be clear that running a monotone function to a
112+
//! fix-point on a finite lattice will always terminate: `f` can only "move"
113+
//! along the lattice in a single direction, and therefore can only either find
114+
//! a fix-point in the middle of the lattice or continue to the top or bottom
115+
//! depending if it is ascending or descending the lattice respectively.
116+
//!
117+
//! For our analysis, we are collecting the set of template parameters used by
118+
//! any given IR node. The set of template parameters appearing in the program
119+
//! is finite. Our lattice is their powerset. We start at the bottom element,
120+
//! the empty set. Our analysis only adds members to the set of used template
121+
//! parameters, never removes them, so it is monotone, and therefore iteration
122+
//! to a fix-point will terminate.
84123
//!
85124
//! For a deeper introduction to the general form of this kind of analysis, see
86125
//! [Static Program Analysis by Anders Møller and Michael I. Schwartzbach][spa].
@@ -173,38 +212,119 @@ pub fn analyze<Analysis>(extra: Analysis::Extra) -> Analysis::Output
173212
analysis.into()
174213
}
175214

176-
/// An analysis that finds the set of template parameters that actually end up
177-
/// used in our generated bindings.
215+
/// An analysis that finds for each IR item its set of template parameters that
216+
/// it uses.
217+
///
218+
/// We use the following monotone constraint function:
219+
///
220+
/// ```ignore
221+
/// template_param_usage(v) =
222+
/// self_template_param_usage(v) union
223+
/// template_param_usage(w_0) union
224+
/// template_param_usage(w_1) union
225+
/// ...
226+
/// template_param_usage(w_n)
227+
/// ```
228+
///
229+
/// Where `v` has direct edges in the IR graph to each of `w_0`, `w_1`,
230+
/// ..., `w_n` (for example, if `v` were a struct type and `x` and `y`
231+
/// were the types of two of `v`'s fields). We ignore certain edges, such
232+
/// as edges from a template declaration to its template parameters'
233+
/// definitions for this analysis. If we didn't, then we would mistakenly
234+
/// determine that ever template parameter is always used.
235+
///
236+
/// Finally, `self_template_param_usage` is defined with the following cases:
237+
///
238+
/// * If `T` is a template parameter:
239+
///
240+
/// ```ignore
241+
/// self_template_param_usage(T) = { T }
242+
/// ```
243+
///
244+
/// * If `inst` is a template instantiation, `inst.args` are the template
245+
/// instantiation's template arguments, and `inst.decl` is the template
246+
/// declaration being instantiated:
247+
///
248+
/// ```ignore
249+
/// self_template_param_usage(inst) =
250+
/// { T: for T in inst.args, if T in template_param_usage(inst.decl) }
251+
/// ```
252+
///
253+
/// * And for all other IR items, the result is the empty set:
254+
///
255+
/// ```ignore
256+
/// self_template_param_usage(_) = { }
257+
/// ```
178258
#[derive(Debug, Clone)]
179259
pub struct UsedTemplateParameters<'a> {
180260
ctx: &'a BindgenContext<'a>,
181-
used: ItemSet,
261+
used: HashMap<ItemId, ItemSet>,
182262
dependencies: HashMap<ItemId, Vec<ItemId>>,
183263
}
184264

265+
impl<'a> UsedTemplateParameters<'a> {
266+
fn consider_edge(kind: EdgeKind) -> bool {
267+
match kind {
268+
// For each of these kinds of edges, if the referent uses a template
269+
// parameter, then it should be considered that the origin of the
270+
// edge also uses the template parameter.
271+
EdgeKind::TemplateArgument |
272+
EdgeKind::BaseMember |
273+
EdgeKind::Field |
274+
EdgeKind::InnerType |
275+
EdgeKind::InnerVar |
276+
EdgeKind::Constructor |
277+
EdgeKind::VarType |
278+
EdgeKind::TypeReference => true,
279+
280+
// We can't emit machine code for new instantiations of function
281+
// templates and class templates' methods (and don't detect explicit
282+
// instantiations) so we must ignore template parameters that are
283+
// only used by functions.
284+
EdgeKind::Method |
285+
EdgeKind::FunctionReturn |
286+
EdgeKind::FunctionParameter => false,
287+
288+
// If we considered these edges, we would end up mistakenly claiming
289+
// that every template parameter always used.
290+
EdgeKind::TemplateDeclaration |
291+
EdgeKind::TemplateParameterDefinition => false,
292+
293+
// Since we have to be careful about which edges we consider for
294+
// this analysis to be correct, we ignore generic edges. We also
295+
// avoid a `_` wild card to force authors of new edge kinds to
296+
// determine whether they need to be considered by this analysis.
297+
EdgeKind::Generic => false,
298+
}
299+
}
300+
}
301+
185302
impl<'a> MonotoneFramework for UsedTemplateParameters<'a> {
186303
type Node = ItemId;
187304
type Extra = &'a BindgenContext<'a>;
188-
type Output = ItemSet;
305+
type Output = HashMap<ItemId, ItemSet>;
189306

190307
fn new(ctx: &'a BindgenContext<'a>) -> UsedTemplateParameters<'a> {
308+
let mut used = HashMap::new();
191309
let mut dependencies = HashMap::new();
192310

193311
for item in ctx.whitelisted_items() {
312+
dependencies.entry(item).or_insert(vec![]);
313+
used.insert(item, ItemSet::new());
314+
194315
{
195316
// We reverse our natural IR graph edges to find dependencies
196317
// between nodes.
197-
let mut add_reverse_edge = |sub_item, _| {
318+
item.trace(ctx, &mut |sub_item, _| {
198319
dependencies.entry(sub_item).or_insert(vec![]).push(item);
199-
};
200-
item.trace(ctx, &mut add_reverse_edge, &());
320+
}, &());
201321
}
202322

203323
// Additionally, whether a template instantiation's template
204324
// arguments are used depends on whether the template declaration's
205325
// generic template parameters are used.
206-
ctx.resolve_item_fallible(item)
207-
.and_then(|item| item.as_type())
326+
ctx.resolve_item(item)
327+
.as_type()
208328
.map(|ty| match ty.kind() {
209329
&TypeKind::TemplateInstantiation(decl, ref args) => {
210330
let decl = ctx.resolve_type(decl);
@@ -222,57 +342,65 @@ impl<'a> MonotoneFramework for UsedTemplateParameters<'a> {
222342

223343
UsedTemplateParameters {
224344
ctx: ctx,
225-
used: ItemSet::new(),
345+
used: used,
226346
dependencies: dependencies,
227347
}
228348
}
229349

230-
fn initial_worklist(&self) -> Vec<Self::Node> {
350+
fn initial_worklist(&self) -> Vec<ItemId> {
231351
self.ctx.whitelisted_items().collect()
232352
}
233353

234-
fn constrain(&mut self, item: ItemId) -> bool {
235-
let original_size = self.used.len();
354+
fn constrain(&mut self, id: ItemId) -> bool {
355+
let original_len = self.used[&id].len();
236356

237-
item.trace(self.ctx, &mut |item, edge_kind| {
238-
if edge_kind == EdgeKind::TemplateParameterDefinition {
239-
// The definition of a template parameter is not considered a
240-
// use of said template parameter. Ignore this edge.
241-
return;
357+
// First, add this item's self template parameter usage.
358+
let item = self.ctx.resolve_item(id);
359+
let ty_kind = item.as_type().map(|ty| ty.kind());
360+
match ty_kind {
361+
Some(&TypeKind::Named) => {
362+
// This is a trivial use of the template type parameter.
363+
self.used.get_mut(&id).unwrap().insert(id);
242364
}
243-
244-
let ty_kind = self.ctx.resolve_item(item)
245-
.as_type()
246-
.map(|ty| ty.kind());
247-
248-
match ty_kind {
249-
Some(&TypeKind::Named) => {
250-
// This is a "trivial" use of the template type parameter.
251-
self.used.insert(item);
252-
},
253-
Some(&TypeKind::TemplateInstantiation(decl, ref args)) => {
254-
// A template instantiation's concrete template argument is
255-
// only used if the template declaration uses the
256-
// corresponding template parameter.
257-
let decl = self.ctx.resolve_type(decl);
258-
let params = decl.self_template_params(self.ctx)
259-
.expect("a template instantiation's referenced \
260-
template declaration should have template \
261-
parameters");
262-
for (arg, param) in args.iter().zip(params.iter()) {
263-
if self.used.contains(param) {
264-
if self.ctx.resolve_item(*arg).is_named() {
265-
self.used.insert(*arg);
266-
}
365+
Some(&TypeKind::TemplateInstantiation(decl, ref args)) => {
366+
// A template instantiation's concrete template argument is
367+
// only used if the template declaration uses the
368+
// corresponding template parameter.
369+
let params = decl.self_template_params(self.ctx)
370+
.expect("a template instantiation's referenced \
371+
template declaration should have template \
372+
parameters");
373+
for (arg, param) in args.iter().zip(params.iter()) {
374+
if self.used[&decl].contains(param) {
375+
if self.ctx.resolve_item(*arg).is_named() {
376+
self.used.get_mut(&id).unwrap().insert(*arg);
267377
}
268378
}
269-
},
270-
_ => return,
379+
}
271380
}
381+
_ => {}
382+
}
383+
384+
// Second, add the union of each of its referent item's template
385+
// parameter usage.
386+
item.trace(self.ctx, &mut |sub_id, edge_kind| {
387+
if sub_id == id || !Self::consider_edge(edge_kind) {
388+
return;
389+
}
390+
391+
// This clone is unfortunate because we are potentially thrashing
392+
// malloc. We could investigate replacing the ItemSet values with
393+
// Rc<RefCell<ItemSet>> to make the borrow checker happy, but it
394+
// isn't clear that the added indirection wouldn't outweigh the cost
395+
// of malloc'ing a new ItemSet here. Ideally, `HashMap` would have a
396+
// `split_entries` method analogous to `slice::split_at_mut`...
397+
let to_add = self.used[&sub_id].clone();
398+
self.used.get_mut(&id).unwrap().extend(to_add);
272399
}, &());
273400

274-
let new_size = self.used.len();
275-
new_size != original_size
401+
let new_len = self.used[&id].len();
402+
assert!(new_len >= original_len);
403+
new_len != original_len
276404
}
277405

278406
fn each_depending_on<F>(&self, item: ItemId, mut f: F)
@@ -286,8 +414,8 @@ impl<'a> MonotoneFramework for UsedTemplateParameters<'a> {
286414
}
287415
}
288416

289-
impl<'a> From<UsedTemplateParameters<'a>> for ItemSet {
290-
fn from(used_templ_params: UsedTemplateParameters) -> ItemSet {
417+
impl<'a> From<UsedTemplateParameters<'a>> for HashMap<ItemId, ItemSet> {
418+
fn from(used_templ_params: UsedTemplateParameters) -> Self {
291419
used_templ_params.used
292420
}
293421
}

0 commit comments

Comments
 (0)