Skip to content

[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

Merged
merged 2 commits into from
Jul 18, 2022

Conversation

asl
Copy link
Contributor

@asl asl commented May 18, 2022

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

…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
@asl asl requested a review from dan-zheng May 18, 2022 17:26
@asl
Copy link
Contributor Author

asl commented May 18, 2022

Note that this PR effectively makes large part of a1e138b obsolete and, more importantly, removes cycle-breaking hacks as they are not needed anymore

@asl
Copy link
Contributor Author

asl commented May 18, 2022

@swift-ci please test

@asl
Copy link
Contributor Author

asl commented May 18, 2022

Tagging @rxwei @BradLarson

Co-authored-by: Philip Turner <[email protected]>
@asl
Copy link
Contributor Author

asl commented May 18, 2022

@swift-ci please test

Copy link
Contributor

@dan-zheng dan-zheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks!

Comment on lines +1210 to +1213
forEachSourceFile([](SourceFile &SF) {
loadDerivativeConfigurations(SF);
return false;
});
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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
  2. 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) with foo not defined anywhere.
  3. 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.

@rxwei rxwei self-requested a review May 19, 2022 06:00
@@ -375,6 +375,44 @@ void swift::performWholeModuleTypeChecking(SourceFile &SF) {
}
}

void swift::loadDerivativeConfigurations(SourceFile &SF) {
if (!isDifferentiableProgrammingEnabled(SF))
Copy link
Contributor

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.

Copy link
Contributor

@dan-zheng dan-zheng May 19, 2022

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.

Copy link
Contributor

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'.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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 )

Copy link
Contributor

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.

Copy link
Contributor Author

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.

@philipturner
Copy link
Contributor

philipturner commented Jun 18, 2022

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 asl:55170-fix. For more context, see #59467 (comment).

@asl
Copy link
Contributor Author

asl commented Jun 18, 2022

@rxwei Do you happen to have any further comments on this? Or we can land this PR?

Thanks!

Copy link
Contributor

@rxwei rxwei left a 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

@asl asl requested a review from slavapestov June 21, 2022 15:30
@philipturner
Copy link
Contributor

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.

@asl
Copy link
Contributor Author

asl commented Jun 29, 2022

We are currently waiting for @slavapestov's opinion per @rxwei request

@philipturner
Copy link
Contributor

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.

@asl
Copy link
Contributor Author

asl commented Jul 18, 2022

@slavapestov gently ping :)

@slavapestov
Copy link
Contributor

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) {
Copy link
Contributor

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?

@asl
Copy link
Contributor Author

asl commented Jul 18, 2022

@slavapestov Thanks! I created #58965 to track it. Will post follow-up patch

@asl asl merged commit 6e2c4fa into swiftlang:main Jul 18, 2022
@philipturner
Copy link
Contributor

philipturner commented Jul 18, 2022

@asl Thank you so much! But what happened to the regression test from asl#1? Should I make a new PR to merge it into apple/swift, or did you decide that it's not a good idea?

@asl
Copy link
Contributor Author

asl commented Jul 18, 2022

@philipturner I will take care of it separately, do not worry :)

Catfish-Man pushed a commit to Catfish-Man/swift that referenced this pull request Jul 28, 2022
…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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 10, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 10, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 10, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 10, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 14, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 14, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 14, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 17, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 17, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 17, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 21, 2024
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
kovdan01 added a commit to kovdan01/swift that referenced this pull request Oct 21, 2024
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
DougGregor pushed a commit that referenced this pull request Nov 11, 2024
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants