Skip to content
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

Optimize compilation of functional-looking assignment RHSs #5589

Merged

Conversation

RustanLeino
Copy link
Collaborator

@RustanLeino RustanLeino commented Jun 29, 2024

Description

Previously, the single-pass compiler would use TrExprOpt to turn nested expressions into target-language statements, ending in a return. This PR generalizes that mechanism so that the ending action is parameterized. In particular, the PR now applies TrExprOpt to single-LHS assignment statements. This allows the RHS of such assignment statements to be deeply nested expressions, without running into, for example, the limits of javac to deal with nested lambda expressions.

Fixes #3868

Implementation

This PR required a change to the target-code strategy of match constructs. Previously, cases turned into a sequence of if statements (note, not a cascading if). If one of those if statements determined that the case applied, then a boolean variable, unmatched, was set to false. Each of these if statements was thus also guarded by the condition unmatched.

This previous strategy caused a problem when the body of the cases is an assignment. Since every assignment was guarded by an if, target languages with definite-assignment rules weren't able to determine that the variable was always assigned.

A more straightforward strategy is to jump (using a break or goto) to the end of the target code for the match, rather than setting the unmatched variable. This lets the target language easily see that the variable has been assigned on all paths. It also means that the unmatched variable and its tests are not needed at all. Furthermore, the last case does not need an enclosing if structure at all, since if the prior cases don't apply, then the last one must apply.

This PR improves the strategy to the one with jumps.

Work along the way

fix: Use Downcast for let RHS and for function body

Fixes #5597

By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

atomb
atomb previously approved these changes Jul 1, 2024
Copy link
Member

@atomb atomb left a comment

Choose a reason for hiding this comment

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

This looks good to me. I'm happy to see that it's a relatively simple change!

@atomb
Copy link
Member

atomb commented Jul 1, 2024

This looks good to me. I'm happy to see that it's a relatively simple change!

The first couple of test failures look like they're just because some files need to be regenerated, but there are a few integration test failures that look real.

@atomb
Copy link
Member

atomb commented Jul 8, 2024

I see a couple of compilation tests failing, here and here.

# Conflicts:
#	Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.cs
# Conflicts:
#	Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.Statement.cs
#	Source/DafnyCore/GeneratedFromDafny/DCOMP.cs
#	Source/DafnyCore/GeneratedFromDafny/RAST.cs
atomb
atomb previously approved these changes Jul 10, 2024
Copy link
Member

@atomb atomb left a comment

Choose a reason for hiding this comment

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

I think this looks good. There's an #if block that I wasn't sure you intended to leave in or not, but I'll let you decide whether or not to remove it.

Contract.Assert(n == inTmps.Count);
// finally, the jump back to the head of the function
EmitJumpToTailCallStart(wr);
Contract.Assert(!inLetExprBody); // a tail call had better not sit inside a target-code lambda
Copy link
Member

Choose a reason for hiding this comment

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

Nice consolidation!

@@ -2749,7 +2750,10 @@ protected class NullClassWriter : IClassWriter {
Coverage.Instrument(f.Body.tok, $"entry to function {f.FullName}", w);
Contract.Assert(enclosingFunction == null);
enclosingFunction = f;
CompileReturnBody(f.Body, f.OriginalResultTypeWithRenamings(), w, accVar);
#if NEW_ATTEMPT
Copy link
Member

Choose a reason for hiding this comment

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

Is this relevant beyond debugging?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oops. That was leftover from straightening out a merge. Thanks for catching that.

@@ -3606,6 +3606,10 @@ protected class ClassWriter : IClassWriter {
message = "unexpected control point";
}

// Wrapping an "if (true) { ... }" around the "break" statement is a way to tell the Java compiler not to give
// errors for any (unreachable) code that may follow.
Copy link
Member

Choose a reason for hiding this comment

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

:-P

@RustanLeino RustanLeino merged commit 04d1cbe into dafny-lang:master Jul 10, 2024
21 checks passed
@RustanLeino RustanLeino deleted the optimize-functional-assignment-rhs branch July 10, 2024 17:29
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.

Missing downcasts Nested LetExpr chain compiles to tons of nested Java lambdas, which javac chokes on
2 participants