Skip to content

Commit 6eb860d

Browse files
committed
Stop thrashing malloc when unioning ItemSets in UsedTemplateParameters
Previously, we were cloning an ItemSet, which requires a malloc for non-empty sets, when taking its union with our current id's set. Now, instead of doing that, we wrap each ItemSet in an Option, and take the set out of the hash map when modifying it. This allows us to side-step the borrow checker and HashMap's lack of an analog to `slice::split_at_mut` and mutate what is logically a value in the hash map while also using immutable references of values that are physically in the hash map.
1 parent 44ed6df commit 6eb860d

File tree

1 file changed

+40
-20
lines changed

1 file changed

+40
-20
lines changed

src/ir/named.rs

+40-20
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ pub struct UsedTemplateParameters<'ctx, 'gen>
257257
where 'gen: 'ctx
258258
{
259259
ctx: &'ctx BindgenContext<'gen>,
260-
used: HashMap<ItemId, ItemSet>,
260+
used: HashMap<ItemId, Option<ItemSet>>,
261261
dependencies: HashMap<ItemId, Vec<ItemId>>,
262262
}
263263

@@ -315,7 +315,7 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
315315

316316
for item in ctx.whitelisted_items() {
317317
dependencies.entry(item).or_insert(vec![]);
318-
used.insert(item, ItemSet::new());
318+
used.insert(item, Some(ItemSet::new()));
319319

320320
{
321321
// We reverse our natural IR graph edges to find dependencies
@@ -359,33 +359,47 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
359359
}
360360

361361
fn constrain(&mut self, id: ItemId) -> bool {
362-
let original_len = self.used[&id].len();
362+
// Invariant: all hash map entries' values are `Some` upon entering and
363+
// exiting this method.
364+
debug_assert!(self.used.values().all(|v| v.is_some()));
365+
366+
// Take the set for this id out of the hash map while we mutate it based
367+
// on other hash map entries. We *must* put it back into the hash map at
368+
// the end of this method. This allows us to side-step HashMap's lack of
369+
// an analog to slice::split_at_mut.
370+
let mut used_by_this_id = self.used.get_mut(&id).unwrap().take().unwrap();
371+
372+
let original_len = used_by_this_id.len();
363373

364-
// First, add this item's self template parameter usage.
365374
let item = self.ctx.resolve_item(id);
366375
let ty_kind = item.as_type().map(|ty| ty.kind());
367376
match ty_kind {
377+
// Named template type parameters trivially use themselves.
368378
Some(&TypeKind::Named) => {
369-
// This is a trivial use of the template type parameter.
370-
self.used.get_mut(&id).unwrap().insert(id);
379+
used_by_this_id.insert(id);
371380
}
381+
382+
// A template instantiation's concrete template argument is
383+
// only used if the template declaration uses the
384+
// corresponding template parameter.
372385
Some(&TypeKind::TemplateInstantiation(ref inst)) => {
373386
let decl = self.ctx.resolve_type(inst.template_definition());
374387
let args = inst.template_arguments();
375388

376-
// A template instantiation's concrete template argument is
377-
// only used if the template declaration uses the
378-
// corresponding template parameter.
379389
let params = decl.self_template_params(self.ctx)
380390
.unwrap_or(vec![]);
381391
for (arg, param) in args.iter().zip(params.iter()) {
382-
if self.used[&inst.template_definition()].contains(param) {
392+
let used_by_definition = self.used[&inst.template_definition()]
393+
.as_ref()
394+
.unwrap();
395+
if used_by_definition.contains(param) {
383396
if let Some(named) = arg.as_named(self.ctx, &()) {
384-
self.used.get_mut(&id).unwrap().insert(named);
397+
used_by_this_id.insert(named);
385398
}
386399
}
387400
}
388401
}
402+
389403
// Otherwise, add the union of each of its referent item's template
390404
// parameter usage.
391405
_ => {
@@ -394,20 +408,23 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
394408
return;
395409
}
396410

397-
// This clone is unfortunate because we are potentially thrashing
398-
// malloc. We could investigate replacing the ItemSet values with
399-
// Rc<RefCell<ItemSet>> to make the borrow checker happy, but it
400-
// isn't clear that the added indirection wouldn't outweigh the cost
401-
// of malloc'ing a new ItemSet here. Ideally, `HashMap` would have a
402-
// `split_entries` method analogous to `slice::split_at_mut`...
403-
let to_add = self.used[&sub_id].clone();
404-
self.used.get_mut(&id).unwrap().extend(to_add);
411+
let used_by_sub_id = self.used[&sub_id]
412+
.as_ref()
413+
.unwrap()
414+
.iter()
415+
.cloned();
416+
used_by_this_id.extend(used_by_sub_id);
405417
}, &());
406418
}
407419
}
408420

409-
let new_len = self.used[&id].len();
421+
let new_len = used_by_this_id.len();
410422
assert!(new_len >= original_len);
423+
424+
// Put the set back in the hash map and restore our invariant.
425+
self.used.insert(id, Some(used_by_this_id));
426+
debug_assert!(self.used.values().all(|v| v.is_some()));
427+
411428
new_len != original_len
412429
}
413430

@@ -425,6 +442,9 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
425442
impl<'ctx, 'gen> From<UsedTemplateParameters<'ctx, 'gen>> for HashMap<ItemId, ItemSet> {
426443
fn from(used_templ_params: UsedTemplateParameters<'ctx, 'gen>) -> Self {
427444
used_templ_params.used
445+
.into_iter()
446+
.map(|(k, v)| (k, v.unwrap()))
447+
.collect()
428448
}
429449
}
430450

0 commit comments

Comments
 (0)