Skip to content

[sil-optimizer] Add FP comparison support in constant folder #69828

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 3 commits into from
Nov 28, 2023

Conversation

jkshtj
Copy link
Contributor

@jkshtj jkshtj commented Nov 13, 2023

This pull request adds support for handling floating point comparisons to the constant folder.

Partially fixes #68901

@jkshtj
Copy link
Contributor Author

jkshtj commented Nov 13, 2023

@BradLarson could you please trigger swift-ci?

@jkshtj
Copy link
Contributor Author

jkshtj commented Nov 13, 2023

@eeckstein could you please take a look at the PR?

bool result;
bool isOrdered = !lhs.isNaN() && !rhs.isNaN();

switch (ID) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please ensure your coding style is same as in the file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is actually git-clang-format. But I can bypass that step to keep the style same.

Copy link
Contributor

Choose a reason for hiding this comment

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

Well... git-clang-format is dumb and overall the style guide is not simply clang-format everything.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not simply clang-format everything.

Shouldn't it be? What is the benefit of using a formatter then?

Copy link
Contributor

@asl asl Nov 14, 2023

Choose a reason for hiding this comment

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

It can handle maybe 80% of cases. However, the remaining 20% of cases are about things like readability, etc.

However, in your particular case it's not configured properly. The repo's style config is explicitly saying based on LLVM. And LLVM defaults to 2 spaces indent. So, something is not correct here and this is why one need to examine the output of formatter not merely commit it as-is.

@@ -40,7 +40,12 @@ func caller_of_simple_vjp() -> Float {
@_silgen_name("pb_with_control_flow")
func pb_with_control_flow(_ x: Float) -> Float {
if (x > 0) {
return sin(x) * cos(x)
Copy link
Contributor

@asl asl Nov 14, 2023

Choose a reason for hiding this comment

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

I would just ensure you won't have x constant folded here. For example, you could have an argument of caller_of_pb_with_control_flow that you will use to evaluate gradient at.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah nice catch. Fixed.

Copy link
Contributor

@eeckstein eeckstein left a comment

Choose a reason for hiding this comment

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

Nice!
Do you mind adding a swift (not sil) test file which compares constant folded results with not-constant folded floating point operations? This would not only test the implementation but also the assumptions about the floating point behavior (like "Ordered comparisons with NaN always return false", etc).

You can do that for example by generating the test file, like e.g. done here: https://github.com/apple/swift/blob/main/validation-test/SILOptimizer/static_enums_fuzzing.swift.
Then it's easy to test all combinations of fp-types * operations * relevant-operand-values

@jkshtj
Copy link
Contributor Author

jkshtj commented Nov 16, 2023

@eeckstein Added the validation tests as you suggested. They helped catch quite a lot of edge cases so thanks for pointing it out here!

Can you please take a look at the revision when you get time?

Copy link
Contributor

@eeckstein eeckstein left a comment

Choose a reason for hiding this comment

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

Very nice! Thanks!
LGTM

}
}

func generateComparisonFuncDecls() {
Copy link
Contributor

Choose a reason for hiding this comment

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

wrong indentation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@inline(never) @_silgen_name("\(comparisonFuncName)") @_optimize(none)
func \(comparisonFuncName)() {
let \(resultVarName) = \(optFuncName)() == \(unoptFuncName)()
print("[\(resultVarName)] result equal? \\(\(resultVarName))")
Copy link
Contributor

Choose a reason for hiding this comment

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

You could just emit a precondition(result, "message") instead of FileChecking the output. But checking the output is also fine

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice! Changed.

@jkshtj
Copy link
Contributor Author

jkshtj commented Nov 20, 2023

@eeckstein we may need to restart swift-ci.

@asl
Copy link
Contributor

asl commented Nov 20, 2023

@swift-ci please test

@jkshtj
Copy link
Contributor Author

jkshtj commented Nov 20, 2023

Looks like a validation test failed. I'm taking a look.

@jkshtj
Copy link
Contributor Author

jkshtj commented Nov 20, 2023

Fixed the failing validation test.

This commit modifes the constant folder to not handle equality comparisons b/w
infinity and non-infinity operands. In such comparisons, special floating point
types - Float80 and Float16, may come into play and pattern matching againt them
complicates the constant folding logic more than we'd like.

@BradLarson
Copy link
Contributor

@swift-ci Please test

@jkshtj
Copy link
Contributor Author

jkshtj commented Nov 27, 2023

@eeckstein I believe this can be merged?

@eeckstein
Copy link
Contributor

Basically yes.
Please rebase + retest it first

This commit modifes the constant folder to not handle equality comparisons b/w
infinity and non-infinity operands. In such comparisons, special floating point
types - Float80 and Float16, may come into play and pattern matching againt them
complicates the constant folding logic more than we'd like.
@jkshtj
Copy link
Contributor Author

jkshtj commented Nov 27, 2023

@eeckstein Here's my changes rebased on top of trunk. Can you please kick off swift-ci?

@eeckstein
Copy link
Contributor

@swift-ci test

@jkshtj
Copy link
Contributor Author

jkshtj commented Nov 28, 2023

Windows build seems to have timed out.

@asl
Copy link
Contributor

asl commented Nov 28, 2023

@swift-ci please test windows

@tbkka
Copy link
Contributor

tbkka commented Feb 15, 2024

CC: @stephentyrone

@tbkka
Copy link
Contributor

tbkka commented Feb 15, 2024

@jkshtj -- Do the LLVM optimizations not handle these cases? Generally, LLVM is pretty good at this sort of thing.

@jkshtj
Copy link
Contributor Author

jkshtj commented Feb 15, 2024

@jkshtj -- Do the LLVM optimizations not handle these cases? Generally, LLVM is pretty good at this sort of thing.

@tbkka we were motivated to perform FP constant-folding at the SIL level because it vastly simplified the generated derivative code. Below is a minimal example of the optimizations we wanted the FP constant-folding to enable.

@differentiable(reverse)
func f(_ x: Float) -> Float {
  if (x > 0) {
    return sin(x) * cos(x)
  } else {
    return sin(x) + cos(x)
  }
}

// With FP constant-folding the control-flow in VJP of `f` 
// is removed after it's inlined into `foo`, which eventually 
// enables further inlining of all VJPs and pullbacks involved.
//
// Inlining of VJPs and pullbacks lets us get rid of closure context
// allocations, which form the most latent chunk of AD code.
@inline(never)
func foo() -> Float {
  gradient(at: Float(4), of: f(x) )
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Investigate possible optimization opportunities for autodiff code with control flow
5 participants