-
Notifications
You must be signed in to change notification settings - Fork 2
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
Get this working again, add betray! #10
base: master
Are you sure you want to change the base?
Conversation
CC @timholy if case you have any thoughts or comments. |
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.
Wow, thanks for doing this, that is really cool!
However, I believe this can be solved through the injection of backedges into the generated function body. I think the above worldage issue would be solved by just copying the backedges from
Traitor.trait_dispatch
into the code info for the generated function returned by@traitor
.
Yes, I think this is the approach we should take. The idea here was to have fully-compiled code (rather than memoization like Salsa.jl) and also to make a proof-of-principle that could potentially (if the core devs were interested) be integrated into Julia proper.
|
||
(See also `Traitor` and `@traitor`.) | ||
Betraying functions is not a very kind thing to do, and can cause unintended side effects. |
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.
Do you have examples of these unintended side effects?
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.
One example would be if you betray!
'd Traitor.get_trait_table
, you would delete the trait methods for a given signature because it'd have to recompile so the dispatch dict would get emptied.
Presumably, that'd be a pretty unfortunate unintended consequence.
them to be compatible with traits-based dispatch, so that new submethods | ||
defined by `@traitor` do not overwrite existing default methods (and used where | ||
no more specific trait match is found). | ||
Et tu? |
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.
Lol.
end | ||
# TODO: make this work with TT of the form (Tuple{T, T} where T) | ||
|
||
m = (last ∘ collect ∘ methods)(f, TT) |
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.
Why last
? Should it be only
?
Also, purely out of curiousity, do you find this easier to read than last(collect(methods(f, TT)))
?
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 use last
because methods
will give all the more specific methods as well as the one you ask for. For example,
julia> methods(sin, Tuple{Real})
# 4 methods for generic function "sin":
[1] sin(a::Float16) in Base.Math at math.jl:1159
[2] sin(x::T) where T<:Union{Float32, Float64} in Base.Math at special/trig.jl:29
[3] sin(x::BigFloat) in Base.MPFR at mpfr.jl:728
[4] sin(x::Real) in Base.Math at math.jl:404
Here, we just wanted the last one.
Also, purely out of curiousity, do you find this easier to read than
last(collect(methods(f, TT)))
?
Slightly yeah. Even with coloured parens, I find it hard for my eye to quickly verify that they're all matched once you have that many nested calls. It's not a huge deal either way for me though.
Hm, from that POV then I think the AbstractInterpreter machinery would be the way to go because it'd be using the same API that julia already uses for compilation and deciding dispatch. I can't imagine tje Compiler team being at all enthusiastic about a PR to Core.Compiler that uses generated functions to decide dispatch. Either way though, I'll get the backedge injection route working first because I understand it better. |
There are a couple of packages that subtype @0x0f0f0f 's metatheory.jl is one candidate for this higher level compiler infra, which might shift to integrate with SSA IR. |
relevant JuliaLang/julia#39697 |
Okay, so the good news is that I managed to get the invalidation machinery to cooperate and recompile stuff as needed: julia> using Traitor
julia> begin
abstract type Size end
struct Small <: Size end
Size(::Union{Type{Int32},Type{Int64}}) = Small
end
Size
julia> @traitor f(x::::Small) = x + 1
f (generic function with 1 method)
julia> f(1)
2
julia> Size(::Type{Float64}) = Small
Size
julia> f(1.0)
2.0 The bad new is that I can't get it to work without a Cassette overdub pass through I suspect that maybe the pass is forcing some specializations that the compiler was giving up on or something. I'm not sure and I've given up for now on figuring it out. So for now, my branch depends on Cassette.jl. This is weird. @NHDaly, you wouldn't happen to know what's going on here, would you? The code in Lines 279 to 356 in 98ded52
|
Kinda swamped but just chiming in to say exciting stuff! @andyferris is more than capable of handling this but I will look sometime (before or after merging). |
Not a problem Tim, I know you're a busy guy! |
So I've gotten the package working on julia version 1.4+, and I've got a working implementation of the
betray!
functionality.One big annoyance that's still present is worldage issues:
However, I believe this can be solved through the injection of backedges into the generated function body. I think the above worldage issue would be solved by just copying the backedges from
Traitor.trait_dispatch
into the code info for the generated function returned by@traitor
.Another approach that might help us get away from generated functions would be to use https://github.com/RelationalAI-oss/Salsa.jl rather than generated functions to cache, memoize, and reactively update the dispatch lookup tables.
Yet another option would be to use the AbstractInterpreter machinery. I'm really excited about this machinery and would like to learn to use it, but it's still pretty intimidating to me and looks quite difficult to learn to use so I haven't gotten around to it yet.