-
-
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
Implement opaque closures #37849
Closed
Closed
Implement opaque closures #37849
Commits on Dec 24, 2020
-
Refactor JL_GC_PUSHFRAME to internalize layout assumptions
Right now callers of JL_GC_PUSHFRAME need to be aware that the roots start three pointers after the end of the struct. That seems like an implementation detail of the macro, so refactor the macro to also take the assignment location for the locals instead. In the process fix an error where the gc-analyzer version of this code assumed a different layout.
Configuration menu - View commit details
-
Copy full SHA for a74e698 - Browse repository at this point
Copy the full SHA a74e698View commit details -
This is the end result of the design process in #31253. # Overview This PR implements a new kind of closure, called an `opaque closure`. It is designed to be complimenatry to the existing closure mechanism and makes some different trade offs. The motivation for this mechanism comes primarily from closure-based AD tools, but I'm expecting it will find other use cases as well. From the end user perspective, opaque closures basically behave like regular closures, expect that they are introduced by adding the `@opaque` macro (not part of this PR, but will be added after). In front of the existing closure. In particular, all scoping, capture, etc. rules are identical. For such user written closures, the primary difference is in the performance characteristics. In particular: 1) Passing an opaque closure to a high order function will specialize on the argument and return types of the closure, but not on the closure identity. (This also means that the opaque closure will not be eligible for inlining into the higher order function, unless the inliner can see both the definition and the call site). 2) The optimizer is allowed to modify the capture environment of the opaque closure (e.g. dropping unused captures, or reducing `Box`ed values back to value captures). The `opaque` part of the naming comes from the notion that semantically, nothing is supposed to inspect either the code or the capture environment of the opaque closure, since the optimizer is allowed to choose any value for these that preserves the behavior of calling the opaque closure itself. # Motivation ## Optimization across closure boundaries Consider the following situation (type annotations are inference results, not type asserts) ``` function foo() a = expensive_but_effect_free()::Any b = something()::Float64 ()->isa(b, Float64) ? return nothing : return a end ``` now, the traditional closure mechanism will lower this to: ``` struct ###{T, S} a::T b::S end (x::###{T,S}) = isa(x.b, Float64) ? return nothing : return x.a function foo() a = expensive_but_effect_free()::Any b = something()::Float64 new(a, b) end ``` the problem with this is apparent: Even though (after inference), we know that `a` is unused in the closure (and thus would be able to delete the expensive call were it not for the capture), we may not delete it, simply because we need to satisfy the full capture list of the closure. Ideally, we would like to have a mechanism where the optimizer may modify the capture list of a closure in response to information it discovers. ## Closures from Casette transforms Compiler passes like Zygote would like to generate new closures from untyped IR (i.e. after the frontend runs) (and in the future potentially typed IR also). We currently do not have a great mechanism to support this. This provides a very straightforward implementation of this feature, as opaque closures may be inserted at any point during the compilation process (unlike types, which may only be inserted by the frontend). # Mechanism The primary concept introduced by this PR is the `OpaqueClosure{A<:Tuple, R}` type, constructed, by the new `Core._opaque_closure` builtin, with the following signature: ``` _opaque_closure(argt::Type{<:Tuple}, lb::Type, ub::Type, source::CodeInfo, captures...) Create a new OpaqueClosure taking arguments specified by the types `argt`. When called, this opaque closure will execute the source specified in `source`. The `lb` and `ub` arguments constrain the return type of the opaque closure. In particular, any return value of type `Core.OpaqueClosure{argt, R} where lb<:R<:ub` is semantically valid. If the optimizer runs, it may replace `R` by the narrowest possible type inference was able to determine. To guarantee a particular value of `R`, set lb===ub. ``` Captures are available to the CodeInfo as `getfield` from Slot 1 (referenced by position). # Examples I think the easiest way to understand opaque closures is look through a few examples. These make use of the `@opaque` macro which isn't implemented yet, but makes understanding the semantics easier. Some of these examples, in currently available syntax can be seen in test/opaque_closure.jl ``` oc_trivial() = @opaque ()::Any->1 @show oc_trivial() # ()::Any->◌ @show oc_trivial()() # 1 oc_inf() = @opaque ()->1 # Int return type is inferred @show oc_inf() # ()::Int->◌ @show oc_inf()() # 1 function local_call(b::Int) f = @opaque (a::Int)->a + b f(2) end oc_capture_opt(A) = @opaque (B::typeof(A))->ndims(A)*B @show oc_capture_opt([1; 2]) # (::Vector{Int},)::Vector{Int}->◌ @show sizeof(oc_capture_opt([1; 2]).env) # 0 @show oc_capture_opt([1 2])([3 4]) # [6 8] ```
Configuration menu - View commit details
-
Copy full SHA for f860f8b - Browse repository at this point
Copy the full SHA f860f8bView commit details -
Configuration menu - View commit details
-
Copy full SHA for be57eaf - Browse repository at this point
Copy the full SHA be57eafView commit details -
Configuration menu - View commit details
-
Copy full SHA for 2ccaa2b - Browse repository at this point
Copy the full SHA 2ccaa2bView commit details -
Configuration menu - View commit details
-
Copy full SHA for 61d9ba9 - Browse repository at this point
Copy the full SHA 61d9ba9View commit details -
Configuration menu - View commit details
-
Copy full SHA for c129e34 - Browse repository at this point
Copy the full SHA c129e34View commit details -
Configuration menu - View commit details
-
Copy full SHA for 190f4e9 - Browse repository at this point
Copy the full SHA 190f4e9View commit details -
Configuration menu - View commit details
-
Copy full SHA for 147502b - Browse repository at this point
Copy the full SHA 147502bView commit details -
Configuration menu - View commit details
-
Copy full SHA for 4b884a5 - Browse repository at this point
Copy the full SHA 4b884a5View commit details
Commits on Dec 25, 2020
-
Configuration menu - View commit details
-
Copy full SHA for 6f97ef0 - Browse repository at this point
Copy the full SHA 6f97ef0View commit details -
Configuration menu - View commit details
-
Copy full SHA for b40a7bd - Browse repository at this point
Copy the full SHA b40a7bdView commit details
Commits on Dec 27, 2020
-
Configuration menu - View commit details
-
Copy full SHA for 5998195 - Browse repository at this point
Copy the full SHA 5998195View commit details
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.