This is an archive of the discontinued LLVM Phabricator instance.

CallGraph passes: iterate over all functions rather than just externally visible ones
ClosedPublic

Authored by t.p.northover on Jul 4 2018, 6:52 AM.

Details

Reviewers
dexonsmith
Summary

From https://llvm.org/PR38029, we were failing to always_inline a function that's called from a static function not otherwise referenced. Since libc++ uses always_inline for functions it doesn't want to build into the shared object, this caused the valid C++ code

#include <string> 
static std::string f() { return std::string(); } 
int main() { (void) f; }

to fail with a linker error (there's no basic_string::basic_string in libc++.so). In the big scheme there are two ways this could be fixed. Either we somehow guarantee that unused internal functions are dropped, or we ensure that the always-inliner handles all functions.

This patch fixes the inliner (indirectly) since IMO there could be legitimate reasons to retain unused functions (e.g. debuggability) and some of the passes involved violate correctness contracts if not run on everything -- the AlwaysInliner and coroutine lowering pass for example.

What it actually does is put logic into CallGraphSCCPass to make sure that all nodes of the CallGraph are visited, not just the ones accessible from the externals node.

As an alternative I considered adapting scc_iterator to do this by default when the GraphTraits allow for it, but discarded that both because it involved nasty template magic and because it was inconsistent with the other graph iterators in ADT (which all only pursue nodes accessible from GraphTraits::getEntryNode).

Diff Detail

Repository
rL LLVM

Event Timeline

t.p.northover created this revision.Jul 4 2018, 6:52 AM

Awesome. I wonder, would it be worth measuring compile-time impact for optimized builds?

llvm/include/llvm/ADT/SCCIterator.h
101–102

It's weird to have begin() take a NodeRef and end() take a GraphT, but I wonder: do we need these at all?

llvm/lib/Analysis/CallGraphSCCPass.cpp
455–456

Might as well add a space to CallGraph * (or have clang-format do it) since you're touching this line. Also, I wonder if auto is just as clear as scc_iterator<...> here, since we're calling a begin function?

456

I would have expected DenseSet or SmallDenseSet. Is there a reason you're preferring std::set here?

458

I think if (!Visited.count(I->first)) might be more clear.

459

Can you make this auto *Elt so it's clear that it's a raw pointer?

t.p.northover marked 4 inline comments as done.Jul 5 2018, 7:33 AM

Thanks for looking at the patch.

I wonder, would it be worth measuring compile-time impact for optimized builds?

Very worthwhile because it revealed a problem with the iteration scheme. CallGraph nodes can become stale when the passes are run so I changed it to find the next node we're going to start from before running them.

As for actual compile time, across the test-suite it actually seemed to improve things by a tiny amount (0.1%). I don't actually trust that result, but it probably means any degradation is negligible.

llvm/include/llvm/ADT/SCCIterator.h
101–102

Yep, they are pretty weird. I'll make the constructors public and get rid of them.

llvm/lib/Analysis/CallGraphSCCPass.cpp
456

Yes, I somehow forgot about DenseSet. I deliberately avoided a Small variant because I can't imagine there are many translation units with a usefully small number of functions. So I'll update it do DenseSet.

t.p.northover marked an inline comment as done.

Address review comments and rework iteration to always find next location before modifying the current one.

dexonsmith accepted this revision.Jul 5 2018, 11:26 AM

LGTM.

llvm/lib/Analysis/CallGraphSCCPass.cpp
478–479

You might add " before it's invalidated" or something to make it more clear why this is here.

This revision is now accepted and ready to land.Jul 5 2018, 11:26 AM
t.p.northover closed this revision.Jul 6 2018, 1:11 AM
t.p.northover marked an inline comment as done.

Thanks Duncan. Committed as r336419.