-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add optimizations for dead code removal #1444
base: main
Are you sure you want to change the base?
Conversation
b93a7b9
to
4f33757
Compare
9b0a754
to
1ab2f25
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good overall. I can't quite judge all the cases we might run into, but I guess that's what the test suite is for: ensuring we don't remove too much. It seems the logic is a bit more pessimistic at some places than it'd need to be, but that can be extended later. In particular, we could extend the pure
notion to more expressions: e.g., an access to a global can be safe if it's read-only (but not sure how much that would buy us in the end).
Have you done any performance comparisons on an actual analyzer yet (and/or an analysis on what/how much actually gets removed?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good overall. I can't quite judge all the cases we might run into, but I guess that's what the test suite is for: ensuring we don't remove too much. It seems the logic is a bit more pessimistic at some places than it'd need to be, but that can be extended later. In particular, we could extend the pure
notion to more expressions: e.g., an access to a global can be safe if it's read-only (but not sure how much that would buy us in the end).
Have you done any performance comparisons on an actual analyzer yet (and/or an analysis on what/how much actually gets removed?)
a4be6ee
to
e9ee59e
Compare
e9ee59e
to
3f7b548
Compare
3f7b548
to
234b156
Compare
I benchmarked this with a big internal parser and the optimizations here make no measurable difference. I suspect that this is due to that parser not containing a lot local, dead code. Doing more global dead code removal based on data and control flow analysis might be able to remove more code in that code base. That being said, the changes here should still be able to remove unneeded code if it is actually present. With that maybe the question whether to merge this or not depends whether the added complexity is too much for us. WDYT? |
Can you tell how much code is actually removed (independent of performance)?
I'm leaning towards not merging at this point if there's no immediate benefit. The optimizer is getting pretty complex and I think it'd be a good time to revisit our approach to doing these things. |
From looking at the generated C++ code it looks like pass removed almost no actual user code in this case. There are a lot (thousands) of removals of code like __location__("foo.spicy:131:2");
(void()); Such I also saw a few instances of temporaries we emit being removed ::hilti::rt::stream::SafeConstIterator __parse_lahe;
...
::hilti::rt::stream::SafeConstIterator __parse_lahe_2; Overall it looks like for this code base this pass has no practical relevance.
I'll keep this branch around since should we at some point use control and data flow information for optimizations we will probably still need annotations for pure functions (at least if their bodies are invisible to our optimizer in C++ library code). |
We could add a narrow, pattern-based optimization to the C++ code generator to skip these directly. Same for
Won't have an impact on performance, but makes the code a bit prettier. |
I was hoping to build something on top of your AST rewrite to implement true AST node removal instead of just emptying them out like now. I am not really a fan of doing one-off fixes in the code generator since this seems fragile and also pretty hard (e.g., these roundtripping assignments between |
This PR adds optimizations related to dead code removal. For that we add detection of whether a function is side effect-free and elimination of dead stores. Marking a store as dead allows us to completely remove the function call then.
For void pure function we also add a pass which simplifies their bodies.
The dead code optimizations around pure functions shouldn't be observable at runtime, but users might be able to see them by e.g., looking at the profiling output.