-
Notifications
You must be signed in to change notification settings - Fork 18k
cmd/compile: better const-based optimizations handling in compiler frontend #29095
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
Comments
Change https://golang.org/cl/152478 mentions this issue: |
https://golang.org/cl/152478 addresses (1). |
Probably partially related: DCE before inlining removes dead |
What do you mean by “but not tests”? |
Oh, you mean the condition of the if statement. Plus the if statement itself. Yeah, we should just fix that. Want to file a new bug so that that fix (which should be fairly straightforward) doesn’t get lost in the discussion of these bugs about other topics? |
I'm very nervous about introducing more constant folding back in the Go frontend. We've had way too many correctness issues relating to confusing "Go constants" (i.e., values that are constant according to the Go language specification), and compile-time constants (i.e., expressions that we can determine at compile-time to always evaluate to the same value). We just recently finally went as far as eliminating all constant folding in the front-end that isn't required by the Go spec. This proposal amounts to suggesting we undo that work. |
OK, I don't mind giving up on it. |
I just looked at CL 152478, and I think it's worth clarifying something. I'm nervous about doing more constant folding/propagation during typechecking (which is what I usually think of when I hear "compiler frontend"). We used to do that, and it led to lots of issues like I mentioned. I'm okay doing constant optimizations that happen entirely after typechecking though. For example, |
It looks like the code for inlined functions with constant arguments is still not optimized in a way we may want it to be (checked on Go tip). I'm interested in coming back to this. Can I start from my-best-effort attempt at this and then rely on your review to make sure that everything is according to the plan, @mdempsky? |
@quasilyte I think it's better to delay this until after Unified IR is used. It's a big refactoring of the frontend, and will remove current |
@cuonglm, sounds good to me. Is there any plan/proposal for it? I found a few "Unified IR" CLs and some mentions in the issues, but no concrete info about it. I would like to learn more about it. :) |
You may want to look at https://go-review.googlesource.com/c/go/+/324573 for the starting. Since that CL, there're many CLs for unified IR. I don't think there's any proposal for it, maybe we can have more after go1.18 release. |
There has been a lot of work on the compiler since this issue was filed. Does anybody want to summarize what ideas here are still relevant for future work? Thanks. |
This issue describes a problem, potential solutions and results collected by a prototype implementation.
It will be followed by a go-review CL.
The current state
Some important Go optimization phases happen at the frontend stage where we have annotated AST.
Notable examples:
While the idea of porting everything to SSA backend sounds exciting:
information is erased during the SSA construction.
My proposal is to extend frontend part a little bit so there is just a bit more information so some useful optimizations
that implemented in frontend can work better. It won't make (potential) transition to SSA harder as the changes are
very focused and affect only a few places.
The problem
The problem I want to address in this document is a loss of const nature of values used through local variables.
If you use constant value directly, frontend recognizes it. If you're using it through never modified local variable,
it does not apply optimizations.
I'll describe two things I was investigating, but there can be a few more places where the proposed solution is applicable.
Problematic case 1: escape analysis for make-slice
(See
#24577
)One might argue that this is not useful piece of code.
True, but the code below does essentially the same thing, but is more meaningful:
allocSlice
is inlined. After inlining, the produced code looks like this:Note that it introduces the additional assignment.
It makes escape analysis to be too conservative there.
If const info is preserved, there could be no allocation at all.
Problematic case 2: short string comparison optimizations
Functions like
strings.HasPrefix
andstrings.HasSuffix
also suffer from constness info loss during the inlining.Now let's define two routines:
The first version is more readable and idiomatic. And it can be optimized to the
isComment2
if weinline
HasPrefix
body manually, even without substitutinglen("#")
since it's const expression anyway,but it won't be as efficient due to intermediate variables reason.
Here is a difference in performance:
The amount of generated code also different (for AMD64 it's 105 vs 31 bytes).
Other potentially related issues
Effectively constant value definition
We can consider local variable as effectively constant if:
The closure value capturing mechanism uses somewhat similar definition to figure out whether to capture something
by value or by reference.
Proposed solution
There are 2 parts. One is very simple and solves the simplest case, the other solves inlining-related problem.
Node.Name.Defn
for effectively constant nodes. So, if they're never assigned to,just use constant value in analysis instead of
ONAME
node inside the optimizations and escape analysis.The good news is that it's very trivial to do. First CL includes this step.
The bad news is that inliner doesn't assign
Name.Defn
and usesOAS2
nodes.So inlining problem remains.
Node.Name.Defn
field for constant initializers.More below.
The first step is solved by introducing
getConstValue(n *Node) *Node
.When node is passed to it, it looks into
n.Name.Defn
field and if it'sOAS
andRight
field is constant, returns it.Otherwise, it returns
n
itself (It could returnnil
as well, it's almost irrelevant detail).In the place where constness-dependent optimization is about to happen, instead of checking the nodes itself, one
queries the const value of the node with
getConstValue
.The second step implies that
getConstValue
can also return appropriate constant values passed into inlined function.Note that it won't help to handle cases like this:
The proposed mechanism is very trivial (or primitive, even), but it covers useful cases and is deterministic, which means programmers can rely on it. It also makes functions like
strings.HasPrefix
appropriate in a hot-spots so you don't have to inline it manually or write specialization as demonstrated in one of the examples above.Extra cases
Another example that can be enhanced by
getConstValue
isOSTR2BYTES
.Any use of
Isconst
inside compiler frontend can be a potential use case forgetConstValue
, but one shouldnot go too far (it can change compile-time semantics, like reporting out-of-bound access to an array through a variable that is effectively constant, but the spec does not permit that to happen and other spots are something that SSA backend can optimize on its own).
compilebench results
Effect on compilation time is negligible:
With changes from initial CL (short string cmp + isSimpleSliceMake) there is a slight improvement in binary sizes
due to better optimization opportunities:
The text was updated successfully, but these errors were encountered: