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
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
bab7cf5
Parameterize TrOptExpr in its continuation
RustanLeino Jun 29, 2024
cf34d7e
Use TrExprOpt for simple assignment statements
RustanLeino Jun 29, 2024
93f5ccf
Add release notes
RustanLeino Jun 29, 2024
013f6d2
Add tests
RustanLeino Jul 3, 2024
0e27fa6
fix: Thread inLetExprBody through EmitNestedMatchGeneric
RustanLeino Jul 3, 2024
8c85e32
Use last case unconditionally
RustanLeino Jul 3, 2024
ac36a0c
Remove “unmatched” mechanism
RustanLeino Jul 3, 2024
97ac904
Always emit exception-throw for zero-case match
RustanLeino Jul 3, 2024
0f4d7d9
chore: Improve code
RustanLeino Jul 3, 2024
15afb89
Emit no tests for last case
RustanLeino Jul 3, 2024
df493ff
Introduce OptimizedExpressionContinuation record type
RustanLeino Jul 3, 2024
434e3fb
Prevent case fall-through by using break/goto
RustanLeino Jul 3, 2024
5502bff
fix: Use Downcast for let RHS and for function body
RustanLeino Jul 4, 2024
d89fb6b
Add tests
RustanLeino Jul 4, 2024
e02d322
Handle only non-degenerate matches differently for Java
RustanLeino Jul 4, 2024
ab826e3
fix: Add a missing case in C# type conversions
RustanLeino Jul 4, 2024
fa1bb32
Updated code generated from Dafny
RustanLeino Jul 4, 2024
8e392d9
chore: Code improvements
RustanLeino Jul 8, 2024
69bf890
fix: Revert the added downcast for exprs being returned
RustanLeino Jul 8, 2024
64c7840
Improve and unify code
RustanLeino Jul 8, 2024
64ba677
chore: Improve code
RustanLeino Jul 8, 2024
498ed9c
fix: Always wrap a block around each case
RustanLeino Jul 8, 2024
065b415
fix: Rename override-member type parameters
RustanLeino Jul 9, 2024
7da8a60
refactor: Extract method TrTailCall
RustanLeino Jul 9, 2024
a6a08e0
fix: Pass “inLetExprBody” through
RustanLeino Jul 9, 2024
7742cd8
Add comment to test file
RustanLeino Jul 9, 2024
9b2ca24
Merge branch 'master' into optimize-functional-assignment-rhs
RustanLeino Jul 9, 2024
89d062a
Merge branch 'master' into optimize-functional-assignment-rhs
RustanLeino Jul 9, 2024
f7763ab
Regenerate Dafny code
RustanLeino Jul 9, 2024
12361cc
Merge branch 'master' into optimize-functional-assignment-rhs
RustanLeino Jul 10, 2024
df1adf5
Update ignore file
RustanLeino Jul 10, 2024
6e15f06
Regenerate Dafny
RustanLeino Jul 10, 2024
5b28ba8
Merge branch 'master' into optimize-functional-assignment-rhs
RustanLeino Jul 10, 2024
9b46ec4
chore: Remove dead code leftover from merge
RustanLeino Jul 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ protected void TrStmt(Statement stmt, ConcreteSyntaxTree wr, ConcreteSyntaxTree
} else {
var eRhs = (ExprRhs)s.Rhs;
if (eRhs.Expr.Resolved is FunctionCallExpr fce && IsTailRecursiveByMethodCall(fce)) {
TrTailCallStmt(s.Tok, fce.Function.ByMethodDecl, fce.Receiver, fce.Args, null, wr);
TrTailCallStmt(s.Tok, fce.Function.ByMethodDecl, fce.Receiver, fce.Args, wr);
} else {
var lvalue = CreateLvalue(s.Lhs, wr, wStmts);
var doAssignment = (Expression e, Type resultType, bool inLetExprBody, ConcreteSyntaxTree wrAssignment) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3014,57 +3014,8 @@ protected void TrExprOpt(Expression expr, Type resultType, ConcreteSyntaxTree wr
} else if (expr is FunctionCallExpr fce && fce.Function == enclosingFunction && enclosingFunction.IsTailRecursive) {
var e = fce;
// compile call as tail-recursive

// assign the actual in-parameters to temporary variables
var inTmps = new List<string>();
var inTypes = new List<Type/*?*/>();
if (!e.Function.IsStatic) {
string inTmp = ProtectedFreshId("_in");
inTmps.Add(inTmp);
inTypes.Add(null);
DeclareLocalVar(inTmp, null, null, e.Receiver, inLetExprBody, wr);
}
for (int i = 0; i < e.Function.Ins.Count; i++) {
Formal p = e.Function.Ins[i];
if (!p.IsGhost) {
string inTmp = ProtectedFreshId("_in");
inTmps.Add(inTmp);
inTypes.Add(e.Args[i].Type);
DeclareLocalVar(inTmp, e.Args[i].Type, p.tok, e.Args[i], inLetExprBody, wr);
}
}
// Now, assign to the formals
int n = 0;
if (!e.Function.IsStatic) {
ConcreteSyntaxTree wRHS = EmitAssignment(IdentLvalue("_this"), null, null, wr, e.tok);
if (thisContext == null) {
wRHS = wr;
} else {
var instantiatedType = e.Receiver.Type.Subst(thisContext.ParentFormalTypeParametersToActuals);

var contextType = UserDefinedType.FromTopLevelDecl(e.tok, thisContext);
if (contextType.ResolvedClass is ClassLikeDecl { NonNullTypeDecl: { } } cls) {
contextType = UserDefinedType.FromTopLevelDecl(e.tok, cls.NonNullTypeDecl);
}

wRHS = EmitCoercionIfNecessary(instantiatedType, contextType, e.tok, wRHS);
}
EmitIdentifier(inTmps[n], wRHS);
EndStmt(wr);
n++;
}
foreach (var p in e.Function.Ins) {
if (!p.IsGhost) {
EmitIdentifier(
inTmps[n],
EmitAssignment(IdentLvalue(IdName(p)), p.Type, inTypes[n], wr, e.tok)
);
n++;
}
}
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!

TrTailCall(e.tok, e.Function.IsStatic, e.Function.Ins, e.Receiver, e.Args, wr);

} else if (expr is BinaryExpr bin
&& bin.AccumulatesForTailRecursion != BinaryExpr.AccumulationOperand.None
Expand Down Expand Up @@ -4374,13 +4325,18 @@ private static Type TypeOfRhs(AssignmentRhs rhs) {
}
}

/// <summary>
/// Emit translation of a call statement.
/// The "receiverReplacement" parameter is allowed to be "null". It must be null for tail recursive calls.
/// </summary>
protected virtual void TrCallStmt(CallStmt s, string receiverReplacement, ConcreteSyntaxTree wr, ConcreteSyntaxTree wStmts, ConcreteSyntaxTree wStmtsAfterCall) {
Contract.Requires(s != null);
Contract.Assert(s.Method != null); // follows from the fact that stmt has been successfully resolved

if (s.Method == enclosingMethod && enclosingMethod.IsTailRecursive) {
// compile call as tail-recursive
TrTailCallStmt(s.Tok, s.Method, s.Receiver, s.Args, receiverReplacement, wr);
Contract.Assert(receiverReplacement == null); // "receiverReplacement" is expected to be "null" for tail recursive calls
TrTailCallStmt(s.Tok, s.Method, s.Receiver, s.Args, wr);
} else {
// compile call as a regular call
var lvalues = new List<ILvalue>(); // contains an entry for each non-ghost formal out-parameter, but the entry is null if the actual out-parameter is ghost
Expand Down Expand Up @@ -4576,37 +4532,38 @@ protected virtual void TrCallStmt(CallStmt s, string receiverReplacement, Concre
}
}

void TrTailCallStmt(IToken tok, Method method, Expression receiver, List<Expression> args, string receiverReplacement, ConcreteSyntaxTree wr) {
void TrTailCallStmt(IToken tok, Method method, Expression receiver, List<Expression> args, ConcreteSyntaxTree wr) {
Contract.Requires(tok != null);
Contract.Requires(method != null);
Contract.Requires(receiver != null);
Contract.Requires(args != null);
Contract.Requires(method.IsTailRecursive);
Contract.Requires(wr != null);
TrTailCall(tok, method.IsStatic, method.Ins, receiver, args, wr);
}

void TrTailCall(IToken tok, bool isStatic, List<Formal> inParameters, Expression receiver, List<Expression> args, ConcreteSyntaxTree wr) {
// assign the actual in-parameters to temporary variables
var inTmps = new List<string>();
var inTypes = new List<Type/*?*/>();
if (receiverReplacement != null) {
// TODO: What to do here? When does this happen, what does it mean?
} else if (!method.IsStatic) {
string inTmp = ProtectedFreshId("_in");
var inTypes = new List<Type>();
if (!isStatic) {
var inTmp = ProtectedFreshId("_in");
inTmps.Add(inTmp);
inTypes.Add(null);
DeclareLocalVar(inTmp, null, null, receiver, false, wr);
}
for (int i = 0; i < method.Ins.Count; i++) {
Formal p = method.Ins[i];
for (int i = 0; i < inParameters.Count; i++) {
var p = inParameters[i];
if (!p.IsGhost) {
string inTmp = ProtectedFreshId("_in");
var inTmp = ProtectedFreshId("_in");
inTmps.Add(inTmp);
inTypes.Add(args[i].Type);
DeclareLocalVar(inTmp, args[i].Type, p.tok, args[i], false, wr);
}
}
// Now, assign to the formals
int n = 0;
if (!method.IsStatic) {
if (!isStatic) {
ConcreteSyntaxTree wRHS = EmitAssignment(IdentLvalue("_this"), null, null, wr, tok);
if (thisContext == null) {
wRHS = wr;
Expand All @@ -4624,7 +4581,7 @@ void TrTailCallStmt(IToken tok, Method method, Expression receiver, List<Express
EndStmt(wr);
n++;
}
foreach (var p in method.Ins) {
foreach (var p in inParameters) {
if (!p.IsGhost) {
EmitIdentifier(
inTmps[n],
Expand Down