-
Notifications
You must be signed in to change notification settings - Fork 747
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
OnceReduction: Consider calls to "once" functions in "once" functions #6055
Conversation
(as background, the situation of such functions calling others, and even in cycles, happens in real-world Java code) |
Just want to give a heads up for following situation:
If we start from
However if you remove the
For this reason we don't handle this cycle situation in JsCompiler. We only handle following:
|
Thanks @gkdn , good point... We first set the global to 1 and then call code, which means recursive calls to us return early even though the full code after us has not executed. So in that example This will require an analysis of the call graph, then. |
Given: B$once() {
if(B$done) return;
B$done = true;
A$once(); // can we apply information from this call?
C$once(); // can we remove this?
log("executed B");
} Then it seems like what we want, to answer the first question, is "can But it seems like this is a problem for all functions, not just the case where But given indirect calls, it seems likely we'd fail to infer in most cases, I worry. That is, if Am I missing something? |
After discussing offline with @gkdn I simplified this to handle the straightforward case that is apparently common, where a "once" function's body calls another, and has nothing else. In that case we can optimize here (but in the general case it is safer to avoid the danger of cycles and so forth; if we find a need for it later, we can investigate some call graph analysis, but as I said above, I worry about indirect calls ruining things as they always do...). PTAL @gkdn |
src/passes/OnceReduction.cpp
Outdated
// which is simple to reason about. To see why, consider that it is very | ||
// different from this situation: |
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.
The explanation of why the more complicated case doesn't work didn't really help me understand why the simple case does work. I think it would be more helpful to explain what we are going to do in the simple case more directly, then explain why it doesn't generalize to the more complicated case.
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.
Good idea, I rewrote it that way.
But maybe wait to read it on seeing if @gkdn finds a fundamental problem in my code 😄
@kripken This doesn't optimize the scenario that I was handling on my side. It looks like it doesn't have the same effect. I will take a look what is going on. |
Co-authored-by: Thomas Lively <tlively@google.com>
I disabled partial inlining and this is the left-over code:
In my own handling I rewrite for example
as
which later gets inlined to parent clinit call. |
@gkdn Makes sense, that also looks like a valid optimization. I'd like to do it in a followup PR to this however, to keep this one as simple as possible (the comments are already dozens of lines 😄 ). |
@kripken I think the optimization make sense but I'm not sure if it will have any extra value if we do the other optimization. IIUC this extra information will not be useful with the rewriting as it will provide the same information. I tested both optimizations together and it looks like the code size is exactly the same but I might be missing something. |
Yeah, I think you're right, after other optimizations they end up equal. I implemented the other one and compared them on the tests from this PR: https://gist.github.com/kripken/56a0f4f1d70e9575273c154d6eb981f8 The difference is only in cases that inlining would end up helping out later. So this PR only helps accelerate the process. As it is fairly complex in the necessary comments, maybe it isn't worth it. I'll open a PR with the other one next week after I polish and test the code. |
Replaced by #6061 |
Say that we have a "once" function like this:
This calls
A
the first time it is reached, and otherwise early-exits. It also callsonce.B
, however, which may also be a once function. If it is, then we couldoptimize this:
After the first call we know the second will have executed, so it can be removed.
To make this work, two small fixes were needed: First, we need to not assume
that only the "once" global itself is the only global set in a "once" function.
Second, we need to look at the proper part of the "once" function when we
consider what it is known to always set (see details in the large new comment).