Conversation
|
👍 I think you also need to make sure the DCE in inference does not delete it. e.g. Lines 3664 to 3669 in 508c9f5 |
| Value **vals = (Value**)alloca(sizeof(Value *) * nargs); | ||
| for (size_t i = 0; i < nargs; ++i) { | ||
| if (!argv[i].isboxed) { | ||
| jl_error("Expr(:gc_preserve) only works on boxed values"); |
There was a problem hiding this comment.
We allow this for ccall so we should also just ignore it here?
There was a problem hiding this comment.
No, I don't think so. This doesn't make sense on non-boxed values and people may be surprised, by e.g.:
n = 100000
@gc_preserve n do_something(pointer_from_objref(n))
otherwise. With this framework, we can also add a version of pointer_form_objref that gives you the pointer as well as a lifetime token, but @vtjnash seemed to think we should get rid of pointer_form_objref for non-obviously-boxed values anyway.
There was a problem hiding this comment.
The ccall version also doesn't guarantee boxing of isbits objects if it is passed in as a gc root, i.e. the following def is invalid.
cconvert(::Type{Ref{Int}}, v::Int) = v
unsafe_convert(::Type{Ref{Int}}, v::Int) = Ptr{Int}(pointer_from_objref(v))I think this is not so different from your example.
This mainly makes it very hard to write generic unsafe code in a safe way. The most obvious usecase that I'd like to support is to implement an unsafe C function in julia and being able to replace
ccall(:c_vertion, Void, (Ptr{Void},), obj)with
root = cconvert(Ptr{Void}, obj)
@gc_preserve root begin
ptr = unsafe_convert(Ptr{Void}, root)
julia_vertion(ptr)
endAnd not having to worry about the type of root.
This also makes optimization in inference harder since we might want to split the root just like what we do for ccall.
Also, in any case, this should emit an error instead of throwing one from codegen.
There was a problem hiding this comment.
With this framework, we can also add a version of pointer_form_objref that gives you the pointer as well as a lifetime token
You mean ((a = cconvert(Ref{Any}, v)), unsafe_convert(Ref{Any}, a))?
get rid of pointer_form_objref for non-obviously-boxed values anyway.
Having it raise an error is certainly doable. As lone as we still have a more hidden version for debugging like ccall(:jl_value_ptr, Ptr{Void}, (Any,), v)
There was a problem hiding this comment.
This mainly makes it very hard to write generic unsafe code in a safe way
I think Ref already should already provide a way to handle this case:
julia> Base.unsafe_convert(Ref{Any}, Base.cconvert(Ref{Any}, 1.0))
Ptr{Any} @0x0000000117b5c1a0
julia> unsafe_load(ans)
1.0
There was a problem hiding this comment.
I think Ref already should already provide a way to handle this case:
Not when you don't know the type of the input and not when pointer_to_objref isn't what you want to get (and it's not what my code above is doing anyway).
There was a problem hiding this comment.
If we disallow pointer_from_objref in the problematic case, I'm fine with ignoring this error here. If not, I think I'd be worried that people expect that
@gc_preserve x use(pointer_from_objref(x)) will keep the pointer returned from pointer_from_objref valid.
There was a problem hiding this comment.
What's the difference between this and the unsafe_convert code I give? Both of them are writing code for unsafe code so the user should be already warned of possible issue and should read the doc carefully about the semantics.
| s, r = gensym(), gensym() | ||
| esc(quote | ||
| $s = $(Expr(:gc_preserve, syms...)) | ||
| $r = $(args[end]) |
There was a problem hiding this comment.
Is there a way to either catch (and raise error) or support control flow out of this block?
There was a problem hiding this comment.
Depends on what you mean. Control flow out of the block should work fine. Whether the thing will still be rooted then will depend on dominance in general.
|
Actually no inference.jl DCE should be fine. I just wasn't sure if this is following control flow or lexical order ( |
| end | ||
| s, r = gensym(), gensym() | ||
| esc(quote | ||
| $s = $(Expr(:gc_preserve, syms...)) |
There was a problem hiding this comment.
Should this maybe be called :gc_preserve_begin to pair with :gc_preserve_end?
There was a problem hiding this comment.
I don't care too much. It is called begin/end at the llvm level. I had a vague notion that there was a meaningful difference, but I'm not sure I want to spend too much time thinking about it.
There was a problem hiding this comment.
Seems sensible to match the LLVM names. I would guess that the difference is that the LLVM ones have to be paired while you can maybe have the Julia-level :gc_preserve expression without a paired :gc_preserve_end and have things still work (I'm guessing at that). Matching LLVM still seems sensible to me even with that difference.
| (The `llvm.` in the name is required in order to be able to use the `token` | ||
| type). The semantics of these intrinsics are as follows: | ||
| At any safepoint that is dominated by a `gc_preserve_begin` call, but that does | ||
| not dominate a corresponding `gc_preserve_end` call (i.e. a call whose argument |
There was a problem hiding this comment.
Should "but that does not dominate a corresponding gc_preserve_end call" be "and is not dominated by a corresponding gc_preserve_end call"? I would think the preserve region is between them, right?
69d2b61 to
7db327e
Compare
7db327e to
d68e42f
Compare
|
Rebased, removed error for unboxed values, renamed |
|
Any more comments? |
|
Mergie, mergie? |
@vtjnash and @yuyichao complained that the gcuse intrinsic I introduced wasn't dce safe. This version should be.