-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
WIP: Generated function recompilation with edges #32774
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Closed
NHDaly
wants to merge
20
commits into
JuliaLang:master
from
NHDaly:generated_functions--recompilation-with-edges
+152
−1
Closed
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
5cd866b
Initial version: trying to add edges to all generated functions
NHDaly f98ed40
Changed to have a weird manually constructed Tuple type, which seems …
NHDaly bb7966f
AHA!! Apparently b/c the generator is never specialized, all the type…
NHDaly 84eb92e
Remove printlns
NHDaly f670295
Change implementation to add forward edges to CodeInfo, rather than d…
NHDaly 5f57247
reenabled print statements and reorderded for clarity
NHDaly 1e2b78e
Cool: fix segfault in boostrap -- use different function to specializ…
NHDaly e2a086d
OOPS remove spurious JL_GC_POP
NHDaly 037eb79
Switch to using jl_specializations_get_linfo b/c (I thought) this wil…
NHDaly 0f32fea
Remove printlns from my tests
NHDaly 576b14c
ACTUALLY, it looks like jl_get_specialization1 works!
NHDaly 063c920
Cleanup
NHDaly 9b10e0d
Move all this logic into a function
NHDaly 06a7011
Start adding unit tests for invalidating generated functions.
NHDaly ba6c254
Fix MWE broken test to _actually_ show the problem
NHDaly e75224b
Remove world-age freezing when calling generator
NHDaly 58c60ac
Revert "Remove world-age freezing when calling generator"
NHDaly 12687d8
Fix unit tests to _actually_ show broken behavior
NHDaly 8c05fc6
Fix redef generated for functions with type sparams
NHDaly f20d374
Fix recompiling generated functions w/ varargs...
NHDaly File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
using Test | ||
|
||
@testset "Normal generated functions" begin | ||
@generated g1(x) = :(x) | ||
@test g1(1) == 1 | ||
@test g1("hi") == "hi" | ||
end | ||
|
||
# Invalidating Generated Functions | ||
# TODO: For some reason this doesn't work inside a testset right now (See below) | ||
@generated foo() = bar() | ||
bar() = 2 | ||
# We can still call foo() even though bar() was defined after! Hooray! :) | ||
@test foo() == 2 | ||
# Now we can change bar(), and foo() is also updated! Woohoo! :D | ||
bar() = 3 | ||
@test foo() == 3 | ||
|
||
|
||
@testset "invalidating generated functions in a testset" begin | ||
@generated foo() = bar() | ||
bar() = 2 | ||
|
||
# TODO: It seems like this doesn't work because of the @testset. Is that expected? | ||
# Would this work for regular functions? I think it's broken... | ||
@test foo() == 2 | ||
bar() = 3 | ||
@test_broken foo() == 3 | ||
end | ||
|
||
|
||
# Functions that take arguments | ||
@generated f(x) = f2(x) + f2(x) | ||
f2(x::Type) = sizeof(x) | ||
@test f(1) == 16 | ||
f2(x::Type) = sizeof(x)÷2 | ||
@test f(1) == 8 | ||
|
||
|
||
# Method at bottom of call-stack accepts ::Type, not ::Any | ||
# The simple case -- bar(::Any): | ||
@generated foo(x) = bar(x) | ||
bar(x) = 2 | ||
@test foo(1) == 2 | ||
bar(x) = 3 | ||
@test foo(1) == 3 | ||
# This also works, with t(::Type{Int}) | ||
@generated f_type(x) = t(x) | ||
t(::Type{Int}) = 2 | ||
@test f_type(1) == 2 | ||
t(::Type{Int}) = 3 | ||
@test f_type(1) == 3 | ||
# Yet for some reason this does not work: | ||
# Somehow having t1(T) call typemax prevents forming a backedge from t1 to the generator. | ||
@generated f_type2(x) = t1(x) | ||
t1(T) = typemax(T) | ||
@test f_type2(1) == typemax(Int) | ||
t1(T) = 3 | ||
@test_broken f_type2(1) == 3 | ||
|
||
|
||
# Functions with type params | ||
@generated f(x::T) where T<:Number = width(T) | ||
width(::Type{Int}) where T = 5 | ||
@test f(10) == 5 | ||
width(::Type{Int}) where T = 100 | ||
@test f(10) == 100 | ||
|
||
# It also works for newly defined types | ||
struct MyNum <: Number x::Int end | ||
width(::Type{MyNum}) where T = MyNum(5) | ||
@test f(MyNum(10)) == MyNum(5) | ||
width(::Type{MyNum}) where T = MyNum(100) | ||
@test f(MyNum(10)) == MyNum(100) | ||
|
||
|
||
# Functions with varargs | ||
@generated f(a::T, b, c...) where T<:Number = bar(T) + bar(a) + bar(b) + sum(bar(v) for v in c) | ||
bar(x) = 2 | ||
@test f(2,3,4) == 8 | ||
bar(x) = 3 | ||
@test f(2,3,4) == 12 | ||
@test f(2,3,4,5) == 15 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back 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.
This is one of the currently broken examples. Somehow, when the generator (
#s5#3
in this case) is compiled, it doesn't cause any backedges to be set fromt1
to it.This simple utility function (defined here) prints out all the backedges for all specializations of a function, and you can see that there are none for
t1
:Whereas the previous, working example does get such backedges set:
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.
After some digging, I think I've found a MWE that explains why this case doesn't work, but it boggles my understanding of how julia recompiles functions...
As I show in this example, functions that call
typemax
don't get backedges set from typemax, despite the fact that they do inline the result.But by some mysterious magic, they still get recompiled when
typemax
is updated, so somehow there's something other than backedges that can cause function recompilation??So my test is failing because there's no backedge from
typemax
tof1
. And in my test case above,f1
is the generator body. So the generator body is never properly invalidated, and so my generated function is never re-generated.But somehow typemax is able to cause
f1
to recompile via some mystery voodoo that i don't understand. And so I guess I want to also apply that to my generator body?@vtjnash you pointed to this test case as an example of why "this approach won't work". Is this what you were referring to? That sometimes there is a different mechanism besides backedges that is used to trigger function recompilation?
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 now explained via my explanation below, #32774 (comment).
It turns out that -- despite what it looks like -- the call to
typemax(t)
inf1
above is triggering a dynamic dispatch, so as explained in that comment, the backedges mechanism implemented in this PR so far has no way to trigger regenerating the generated function. (It looks like the call to typemax is being inlined above, but this is actually due to@code_typed
showing fully specialized code, even though it isn't actually fully specialized in reality: #32834.)A simpler example that showcases why this doesn't work can be found here -- this example fails for the current implementation in this PR (as of f20d374):