-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Outline potentially undefined globals during lowering #51970
Conversation
src/ast.c
Outdated
jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); | ||
jl_sym_t *var = jl_symbol(symbol_name(fl_ctx, args[0])); | ||
jl_binding_t *b = jl_get_module_binding(ctx->module, var, 0); | ||
return b != NULL ? fl_ctx->T : fl_ctx->F; |
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.
return b != NULL ? fl_ctx->T : fl_ctx->F; | |
return b != NULL && b->value != NULL ? fl_ctx->T : fl_ctx->F; |
src/julia-syntax.scm
Outdated
@@ -5006,6 +5006,15 @@ f(x) = yt(x) | |||
(set! code (cons e code)) | |||
(set! i (+ i 1)) | |||
(set! locs (cons current-loc locs))) | |||
(define (maybe-outline-outerref e) | |||
(if (and (outerref? e) (not (nothrow-julia-global (cadr e)))) |
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.
I think this logic should go in compile-args
instead.
@@ -2081,6 +2081,8 @@ function bodyfunction(basemethod::Method) | |||
else | |||
return nothing | |||
end | |||
elseif isa(fsym, Core.SSAValue) | |||
fsym = ast.code[fsym.id] |
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.
We might want to add the ability to annotate uses as nothrow, for auto-generated globals we know at lowering time will be defined before they're used.
src/ast.c
Outdated
(void)tosymbol(fl_ctx, args[0], "nothrow-julia-global"); | ||
jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); | ||
jl_sym_t *var = jl_symbol(symbol_name(fl_ctx, args[0])); | ||
jl_binding_t *b = jl_get_module_binding(ctx->module, var, 0); |
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.
jl_binding_t *b = jl_get_binding(ctx->module, var);
you need the real binding, not the stub
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.
Doesn't that cause the binding to resolve? Currently merely lowering a function that mentions a symbol does not resolve that binding in case you want to define it to something else later.
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.
alright, jl_get_binding_if_bound
then
@@ -2998,6 +2998,8 @@ end | |||
@test ig27907(Int, Int, 1, 0) == 0 | |||
|
|||
# issue #28279 | |||
# ensure that lowering doesn't move these into statement position, which would require renumbering | |||
using Base: +, - |
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 state dependence, where we give very different lowering depending on whether something has been used already in the module, seems very ripe for causing future heisenbugs
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.
I agree, but on the other hand, this is supposed to be a storage optimization without semantic effect (though of course there can be bugs).
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.
True. I guess in the future we may want to optimize cases like this more aggressively pre-inference too. I just worry about cases where people may pre-lower the code, then ship it off to another process to execute which as slightly different globals at the time. I think Jeff had some code where he was planning to do that and Distributed.jl does that for remotecall.
…hi block We don't fully set up the interpreter state when evaluating the phi block, because we expect all statements to be valid in value-position (using the IR definition of value position). However, the interpreter was overapproximating the size of the phi-block, leading to the possibility that the evaluation may throw. Correct that by truncating the phi block to the actual last phi. Will be tested in existing tests after #51970.
Looks like the sysimage size increase is about 1%. We could claw some of that back with more precise never-undef analysis, but I'm not sure it's worth it at this stage. If lowering will get rewritten in julia eventually anyway, we might as well do it then, since we have a much better algorithm for this in julia already. |
…hi block We don't fully set up the interpreter state when evaluating the phi block, because we expect all statements to be valid in value-position (using the IR definition of value position). However, the interpreter was overapproximating the size of the phi-block, leading to the possibility that the evaluation may throw. Correct that by truncating the phi block to the actual last phi. Will be tested in existing tests after #51970.
If you have a chance, how many megabytes larger is that, just for reference? It seems okay, and especially so that it is worthwhile |
1.5MB |
…hi block (#51990) We don't fully set up the interpreter state when evaluating the phi block, because we expect all statements to be valid in value-position (using the IR definition of value position). However, the interpreter was overapproximating the size of the phi-block, leading to the possibility that the evaluation may throw. Correct that by truncating the phi block to the actual last phi. Will be tested in existing tests after #51970.
Is this ready to merge? |
(and (pair? e) | ||
(memq (car e) '(quote inert top core globalref outerref | ||
(memq (car e) '(quote inert top core | ||
slot static_parameter))))) |
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.
Should we remove static_parameter
from here since it can also throw?
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.
Hmm, I suppose, but for some reason I always see static parameters getting moved out anyway.
(define (valid-ir-argument? e) | ||
(or (simple-atom? e) (symbol? e) | ||
(or (simple-atom? e) | ||
(and (outerref? e) (nothrow-julia-global (cadr e))) |
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.
nothrow-julia-global
should be able to accept a globalref as well.
src/julia-syntax.scm
Outdated
(cons (if (and | ||
;; Even if we have side effects, we know that singly-assigned | ||
;; locals cannot be affected them, so we can inline them anyway. | ||
(or simple? (single-assign-var? aval)) |
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.
I think this or
should also accept simple-atom, quote, inert, top and core? (Everything in valid-ir-argument except globals, slots, and static_parameter.)
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.
Tried it and it shaves ~450k off the sysimage.
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.
Yes, that seems reasonable
In #51852, we are coercing a boxed `Union{@NamedTuple{progress::String}, @NamedTuple{progress::Float64}}` to `@NamedTuple{progress::String}` via convert_julia_type. This results in a jl_cgval_t that has a Vboxed that points to a boxed `@NamedTuple{progress::Float64}` but with a `@NamedTuple{progress::String}` type tag that the up upsilonnode code then tries to unbox into the typed PhiC slot. This ends up treating the Float64 as a pointer and crashing in GC. Avoid this by adding a runtime check that the converted value is actually compatible (we already had this kind of check for the non-boxed cases) and then making the unboxing runtime-conditional on the type of the union matching the type of the phic. Fixes #51852
Currently [1] it is illegal [2] in IRCode to have a GlobalRef in value position that could potentially throw. This is because in IRCode, we want to assign flags to every statement and if there are multiple things with effects in a statement, we lose precision in tracking which they apply to. However, we currently do allow this in `CodeInfo`. Now that we're starting to make more use of flags in inference also, this is becoming annoying (as it did for IRCode), so I would like to do this transformation earlier. This is an attempt to do this during lowering. It is not entirely clear that this is precisely the correct place for it. We could alternatively consider doing it during the global resolve pass in method.c, but that currently does not renumber SSAValues, so doing it during the renumbering inside lowering may be easier. [1] 39c278b [2] https://github.com/JuliaLang/julia/blob/2f63cc99fb134fb4adb7f11ba86a4e2ab5adcd48/base/compiler/ssair/verify.jl#L54-L58
…hi block We don't fully set up the interpreter state when evaluating the phi block, because we expect all statements to be valid in value-position (using the IR definition of value position). However, the interpreter was overapproximating the size of the phi-block, leading to the possibility that the evaluation may throw. Correct that by truncating the phi block to the actual last phi. Will be tested in existing tests after #51970.
ea1b1f7
to
9c40f43
Compare
9c40f43
to
a0c6204
Compare
In particular, JuliaLang/julia#51970 which adds more nodes in lowered ir.
After #51970, `Expr(:static_parameter, i::Int)` is now consistently outlined during the lowering, so there's no longer a need for `abstract_eval_value_expr` to handle this expression. This update removes the outdated handling, clarifying where we need to handle the expression.
After #51970, `Expr(:static_parameter, i::Int)` is now consistently outlined during the lowering, so there's no longer a need for `abstract_eval_value_expr` to handle this expression. This update removes the outdated handling, clarifying where we need to handle the expression.
After #51970, `Expr(:static_parameter, i::Int)` is now consistently outlined during the lowering, so there's no longer a need for `abstract_eval_value_expr` to handle this expression. This update removes the outdated handling, clarifying where we need to handle the expression.
This partially reverts #51970, once again allowing throwing GlobalRefs in value position for `CodeInfo`, (but not `IRCode`). The primary motivation for this reversal is that after binding partition the throwiness of a global is world-age dependent and we do not want to have lowering be world-age dependent, because that would require us to rewrite the lowered code upon invalidation (#56649). With respect to the original motivation for this change (being able to use the statement flag in inference), we retain the property that the statement flags apply only to the optimizer version of the statement (i.e. with the GlobalRefs moved out). The only place in inference where we were not doing this, we can rely on `exct` instead. This does assume that `exct` is precise, but we've done a lot of work on that in the past year, so hopefully we can rely on that now. The revert is partial, because we go in the opposite direction in a few places: For `GotoIfNot` cond and `EnterNode` scope operands, we just forbid any `GlobalRef` entirely. Constants are rare in these, so no need to burden inference with the possibility of handling these. We do however, keep (and add appropriate handling code) for GlobalRef arguments to `ReturnNode` to make sure that trivial code remains one statement.
Currently [1] it is illegal [2] in IRCode to have a GlobalRef in value position that could potentially throw. This is because in IRCode, we want to assign flags to every statement and if there are multiple things with effects in a statement, we lose precision in tracking which they apply to. However, we currently do allow this in
CodeInfo
. Now that we're starting to make more use of flags in inference also, this is becoming annoying (as it did for IRCode), so I would like to do this transformation earlier. This is an attempt to do this during lowering. It is not entirely clear that this is precisely the correct place for it. We could alternatively consider doing it during the global resolve pass in method.c, but that currently does not renumber SSAValues, so doing it during the renumbering inside lowering may be easier.N.B.: This is against #51853, because this needs some of the inference precision improvements in that PR to avoid regressing the try/catch elision tests (which before that PR, we were incorrectly computing effects for statement-position GlobalRefs).
[1] 39c278b
[2]
julia/base/compiler/ssair/verify.jl
Lines 54 to 58 in 2f63cc9