-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
WIP/RFC: Change lowering of GC.preserve to be lexical #59129
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1141,4 +1141,23 @@ typename(union::UnionAll) = typename(union.body) | |
|
||
include(Core, "optimized_generics.jl") | ||
|
||
struct GCPreserveDuring | ||
f::Any | ||
# N.B: This field is opaque - the compiler is allowed to arbitrarily change it | ||
# as long as it has the same GC rooting behavior. | ||
root::Any | ||
GCPreserveDuring(@nospecialize(f), @nospecialize(root)) = new(f, root) | ||
end | ||
|
||
# This has special support in inference and codegen and is only ever actually called | ||
# in fallback cases. | ||
function (this::GCPreserveDuring)(args...) | ||
@noinline | ||
r = this.f(args...) | ||
# N.B.: This is correct, but stronger than required. If the call to `f` is deleted, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically this is not correct, since it only preserves the root if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case the compiler isn't allowed to know what |
||
# this may be deleted as well. | ||
donotdelete(this.root) | ||
return r | ||
end | ||
|
||
ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Core, true) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -186,8 +186,8 @@ end | |
""" | ||
GC.@preserve x1 x2 ... xn expr | ||
|
||
Mark the objects `x1, x2, ...` as being *in use* during the evaluation of the | ||
expression `expr`. This is only required in unsafe code where `expr` | ||
Mark the objects `x1, x2, ...` as being *in use* during the evaluation of all | ||
calls lexically in the expression `expr`. This is only required in unsafe code where `expr` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is still an ambiguity here whether it is forced to be in-use before the first call, particularly if those later get optimized out. I think the previous formulation added an implicit use at the gc-begin, but I don't know if that was implied to be reliable before (or used)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My expectation was that it did not. I.e. |
||
*implicitly uses* memory or other resources owned by one of the `x`s. | ||
|
||
*Implicit use* of `x` covers any indirect use of resources logically owned by | ||
|
@@ -236,7 +236,12 @@ macro preserve(args...) | |
for x in syms | ||
isa(x, Symbol) || error("Preserved variable must be a symbol") | ||
end | ||
esc(Expr(:gc_preserve, args[end], syms...)) | ||
sym = length(syms) == 1 ? only(syms) : Expr(:tuple, syms...) | ||
g = gensym() | ||
esc(quote | ||
$g = $sym | ||
$(Expr(:gc_preserve, args[end], g)) | ||
end) | ||
end | ||
|
||
""" | ||
|
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 is why I wasn't particularly in favor of adding a different, unrelated meaning to
invoke_modify
, instead of just adding some other metadata representation. I am somewhat curious if we can make the initialGCPreserveDuring
implementation here go fully to being aCallOperand
builtin function wrapper.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.
You mean that the codegen for
invoke
would recognize it when doing a specfun call and automatically strip it? I think that's a reasonable resolution to the TODO also, but I was a little worried of what would happen if that type information got lost shomehow.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 still think wrapping the first argument in a marker is a sensible way to get the distinction in, because it really is like you're just calling the method instance with the preserve semantics, except codegen inlines that to a jl_roots operand bundle. I think that could be quite generic of a mechanism.
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 part seems to make sense. Another design thought though is whether your call preconditions proposal is in essence the same mechanism also?
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.
That proposal actually versions the generated code though, where here we want to version the caller, but specifically not the generated code, so while there are some similarities, I think the mechanisms are complements of each other.