-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[AutoDiff] Lookup for custom derivatives in non-primary source files #58965
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
Conversation
…heck is finished for the primary source. This registers all custom derivatives before autodiff transformations and makes them available to them. Fully resolves swiftlang#55170
Note that this PR effectively makes large part of a1e138b obsolete and, more importantly, removes cycle-breaking hacks as they are not needed anymore |
@swift-ci please test |
Tagging @rxwei @BradLarson |
Co-authored-by: Philip Turner <[email protected]>
@swift-ci please test |
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.
Looks good, thanks!
forEachSourceFile([](SourceFile &SF) { | ||
loadDerivativeConfigurations(SF); | ||
return false; | ||
}); |
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 any sense of the performance implications (empirical or theoretical) of this derivative function resolution approach, compared with the old approach?
A high-level performance summary seems to be:
- No overhead if
import _Differentiation
is missing. - Otherwise, every declaration in every source file is visited.
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.
Right. And any custom derivative declaration is typechecked. Regardless whether the derivative is used or not. This is quite a big hammer, but for now I do not see other way to "expose" them.
Old approach had its own pros and cons:
- Pros: other source files were visited only when derivative was needed. And only those required were typechecked
- Cons: other source files were visited on every derivative lookup. And we had typechecking cycles.
@differentiable(reverse) | ||
func clamp(_ value: Double, _ lowerBound: Double, _ upperBound: Double) -> Double { | ||
// No error expected | ||
return max(min(value, upperBound), lowerBound) |
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.
Could you please clarify the functionality added by this PR on top of #58644?
My understanding:
- [AutoDiff] Implement cross-file lookup of derivatives #58644 adds an option for non-primary-file derivative resolution to
AbstractFunctionDecl::getDerivativeFunctionConfigurations
, as well as original declaration serialization for@derivative
attribute. - This PR moves the non-primary-file derivative resolution logic to
CompilerInstance::finishTypeChecking
instead, visiting each source file.- This resolves the "cross-file derivative not found" issue in [SR-14235] [AutoDiff] Cross-file differentiation error: derivative configuration not registered for
@derivative
attribute #55170. - Does this also avoid circular requests?
- Are there other implications to this new resolution-triggering condition? Ostensibly, to fix [SR-14235] [AutoDiff] Cross-file differentiation error: derivative configuration not registered for
@derivative
attribute #55170, this PR is causing derivative resolution to happen in more cases than previously in [AutoDiff] Implement cross-file lookup of derivatives #58644.
- This resolves the "cross-file derivative not found" issue in [SR-14235] [AutoDiff] Cross-file differentiation error: derivative configuration not registered for
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, as I mentioned, this avoids circular requests. As custom derivative resolution is decoupled from obtaining derivatives configurations. Also, after this PR the differentiation will not trigger typechecking
- After this PR indeed, we lookup and register all explicit derivates regardless of their original function and regardless whether this function is used. The nice side effect is that we will error out in case of
@derivative(of: foo)
withfoo
not defined anywhere. - Effectively this resolves the following cross-file derivate requests:
- File A.swift is using (wants a derivative of) function from B.swift with derivatives defined in C.swift
- The important special case is when B.swift is effectively a part of stdlib (like in the testcase). Here C.swift is a non-primary source but we have no way to resolve the derivative in other way as there is no mechanism to "pull" anything from C.swift – there are simply no forward dependency edges in the dependency graph, only backward.
@@ -375,6 +375,44 @@ void swift::performWholeModuleTypeChecking(SourceFile &SF) { | |||
} | |||
} | |||
|
|||
void swift::loadDerivativeConfigurations(SourceFile &SF) { | |||
if (!isDifferentiableProgrammingEnabled(SF)) |
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.
When is this true? The recursive walk over the AST in secondary files is going to trigger delayed parsing in all nominal types and extensions, which will impact performance in non-WMO builds.
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.
isDifferentiableProgrammingEnabled
(implementation) returns true only for files that have import _Differentiation
, so I hope there would be negligible impact for other files.
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.
Ah ok. Can you add a comment to that effect? Something that mentions that this defeats delayed parsing, but only triggers with 'import _Differentiation'.
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.
@slavapestov This is true when there is an import _Differentiation
in the given SourceFile (so, corresponding ImportDecl).
What is the viable alternative? Essentially the task is: for function f
find all functions that has @derivative(of : f)
attribute specified. Both in primary and secondary sources.
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.
What is the viable alternative? Essentially the task is: for function f find all functions that has @Derivative(of : f) attribute specified. Both in primary and secondary sources.
The only real alternative would be to change the design of this attribute so that this kind of search does not have to be performed. By definition, it requires parsing all source files ahead of time which is incompatible with our strategy of skipping bodies of nominal types and extensions in secondary files.
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.
Yeah. Though the alternative design would not easily allow "pluggable" derivatives.... (see #58644 (comment) for the design explanation, etc. by @dan-zheng )
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 alternative is to handle this like we do operators and AnyObject lookup. When skipping over the body of a nominal type or extension, make a note if the lexer sees '@_derivative', then eagerly parse only those delayed bodies when performing this global lookup. That's still not great but better than disabling delayed parsing entirely when 'import _Differentiation' appears in the program.
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.
@slavapestov Yes, I though about this as well, thanks! Though I was a bit hesitant to introduce such derivative-specific logic into lexer and parser :) But if you're thinking it's acceptable, I can try to do something around this in the subsequent PRs.
I wanted to make the #59467 regression test as a "suggestion" in a GitHub review, but it's too large to merge a contribution that way. Please check out asl#1, which will put the test into |
@rxwei Do you happen to have any further comments on this? Or we can land this PR? Thanks! |
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.
LGTM but I would also get a ✅ from @slavapestov
Could we get this merged? The longer we wait, the more nightly snapshots are produced that are unusable with my AutoDiff client library. If general consensus is that it’s acceptable, let’s merge it (and the uncommitted regression test) sooner rather than later. |
We are currently waiting for @slavapestov's opinion per @rxwei request |
Would it be possible to temporarily revert #58644 until #58965 is approved? That would remove the crash it introduced, letting other modifications to the compiler (such as RQM, SIL patches) be tested against AutoDiff. Furthermore, #58644 is like an incomplete version of #58965. We should wait to incorporate cross-file lookup the Swift compiler until its implementation is complete. This solution would also provide reviewers with as much time as necessary to decide whether to merge this PR. There is also a precedent for this, as it is how the merging of #42189 played out. The initial version caused a regression on Windows, much like the how #58644 introduced #59467. After being temporarily reverted, the PR was reapplied with fixes for Windows. |
@slavapestov gently ping :) |
I would still like to see you redo this to work like the global operator and AnyObject lookup, however, avoiding paging in all delayed parsed bodies in all source files. |
void CompilerInstance::finishTypeChecking() { | ||
forEachFileToTypeCheck([](SourceFile &SF) { | ||
performWholeModuleTypeChecking(SF); | ||
return false; | ||
}); | ||
|
||
forEachSourceFile([](SourceFile &SF) { |
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.
Is there a way to avoid the walk over all source files in the case where none of them have differentiable programming enabled?
@slavapestov Thanks! I created #58965 to track it. Will post follow-up patch |
@philipturner I will take care of it separately, do not worry :) |
…wiftlang#58965) * Lookup for custom derivatives in non-primary source files after typecheck is finished for the primary source. This registers all custom derivatives before autodiff transformations and makes them available to them. Fully resolves swiftlang#55170
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesLookupRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesLookupRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesLookupRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesLookupRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesLookupRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesLookupRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesLookupRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesRequest` to address the issue. Resolves swiftlang#60102
In swiftlang#58965, lookup for custom derivatives in non-primary source files was introduced. It required traversing all delayed parsed function bodies of a file if the file was compiled with differential programming enabled (even for functions with no `@derivative` attribute). This patch introduces `CustomDerivativesRequest` to address the issue. Resolves swiftlang#60102
In #58965, lookup for custom derivatives in non-primary source files was introduced. It required triggering delayed members parsing of nominal types in a file if the file was compiled with differential programming enabled. This patch introduces `CustomDerivativesRequest` to address the issue. We only parse delayed members if tokens `@` and `derivative` appear together inside skipped nominal type body (similar to how member operators are handled). Resolves #60102
This is done after typecheck is finished for the primary source.
This registers all custom derivatives before autodiff transformations and makes them available to them.
Fully resolves #55170