Skip to content

Filter std::rt::lang_start from graphs#119

Open
dkcumming wants to merge 4 commits intomasterfrom
dc/remove-lang-start
Open

Filter std::rt::lang_start from graphs#119
dkcumming wants to merge 4 commits intomasterfrom
dc/remove-lang-start

Conversation

@dkcumming
Copy link
Collaborator

@dkcumming dkcumming commented Feb 18, 2026

This PR allows us to set a SKIP_LANG_START=1 env var to remove the std::rt::lang_start items from the graph output. Perhaps sometimes they are useful, but I have not ever needed to look at them and would prefer them to be removed from the graph.

I had a bit of trouble getting the correct items for the BFS and I used claude to help for full disclosure, however I have vetted the code and agree with it. Also the output has been tested and it does remove the items correctly. I will post before and after of a contrived program that contains black_box to show that the algorithm will only remove items exclusively downstream of std::rt::lang_start.

fn main() {
    let x = std::hint::black_box(42);
    std::hint::black_box(x);
}

Before

image

After

image

This filters out all `lang_start` items (functions, closures, drop glue
etc.). It only removes the items if they are `std::rt::lang_start` or
are exclusively reachable by those functions. That means `black_box` is
removed if it is not called otherwise in the program, but if it is then
it is not removed. There is a couple of stages to get the data together
in the right form so that only the items desired to be removed are.
Copy link
Collaborator

@cds-amal cds-amal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really nice @dkcumming, I love the story in your commits. I feel strongly about including a test to surface regressions, as I expect there are many more features to come.

There are no integration or unit tests for the SKIP_LANG_START behavior. The existing .smir.json.expected files all contain lang_start items, so there's good data to work from. At minimum, an integration test that runs with SKIP_LANG_START=1 and verifies the output lacks lang_start entries would give confidence this doesn't regress.

Comment on lines +23 to +29
let excluded: HashSet<String> = if skip_lang_start() {
let excluded = compute_lang_start_exclusions(&self.items, &ctx);
self.items.retain(|i| !excluded.contains(&i.symbol_name));
excluded
} else {
HashSet::new()
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a useful filter op. I recommend moving it; could be a free fn in the mk_graph mod.

Comment on lines 139 to 144
let Some(callee_name) = ctx.resolve_call_target(func) else {
continue;
};
if !is_unqualified(&callee_name) {
if !is_unqualified(&callee_name) || excluded.contains(&callee_name) {
continue;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to point out that we have to thread excluded into this function to filter a target. Seems. like a smell -- Not asking for a change :)

Right now the flow is:

  1. Build ctx: populates ctx.functions with everything
  2. Compute excluded
  3. self.items.retain(...) to remove excluded items
  4. Thread excluded through every render function to check at call-edge time

If instead, step 3 also did:

ctx.functions.retain(|_, name| !excluded.contains(name));

then ctx.resolve_call_target() would return None for excluded functions, and the existing early-return in D2 handles it already:

let Some(callee_name) = ctx.resolve_call_target(func) else {
  continue;  // already skips when resolve returns None
};
// no need for: || excluded.contains(&callee_name)
if !is_unqualified(&callee_name) {
  continue;
}

That would also eliminate the need to compute (locally) and thread &excluded entirely. NOTE: This would likely need the dot renderer to be factored.

Comment on lines +26 to +32
let excluded: HashSet<String> = if skip_lang_start() {
let excluded = compute_lang_start_exclusions(&self.items, &ctx);
self.items.retain(|i| !excluded.contains(&i.symbol_name));
excluded
} else {
HashSet::new()
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha! Good ole copy-and-paste inheritance (or composition in this case). This supports the filter refaction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants