Skip to content

Commit 9da3d2f

Browse files
cy6erGn0mabreslav
authored andcommitted
Support tail-call optimization in the JVM back-end
1 parent cfbcbb8 commit 9da3d2f

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
111111
* we put it into this map to emit access to that variable instead of evaluating the whole expression
112112
*/
113113
private final Map<JetElement, StackValue.Local> tempVariables = Maps.newHashMap();
114+
@NotNull
115+
private final TailRecursionGeneratorUtil tailRecursionGeneratorUtil;
114116

115117
public CalculatedClosure generateObjectLiteral(GenerationState state, JetObjectLiteralExpression literal) {
116118
JetObjectDeclaration objectDeclaration = literal.getObjectDeclaration();
@@ -180,6 +182,7 @@ public ExpressionCodegen(
180182
this.bindingContext = state.getBindingContext();
181183
this.context = context;
182184
this.statementVisitor = new CodegenStatementVisitor(this);
185+
this.tailRecursionGeneratorUtil = new TailRecursionGeneratorUtil(context, this, this.v, state);
183186
}
184187

185188
protected InstructionAdapter createInstructionAdapter(MethodVisitor mv) {
@@ -1590,6 +1593,16 @@ private void genFinallyBlockOrGoto(
15901593
public StackValue visitReturnExpression(@NotNull JetReturnExpression expression, StackValue receiver) {
15911594
JetExpression returnedExpression = expression.getReturnedExpression();
15921595
if (returnedExpression != null) {
1596+
if (returnedExpression instanceof JetCallExpression) {
1597+
JetCallExpression callExpression = (JetCallExpression) returnedExpression;
1598+
if (tailRecursionGeneratorUtil.isTailRecursion(callExpression) && callExpression.getCalleeExpression() != null) {
1599+
ResolvedCall<? extends CallableDescriptor> resolvedCall = bindingContext.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
1600+
if (resolvedCall != null) {
1601+
return tailRecursionGeneratorUtil.generateTailRecursion(resolvedCall, callExpression);
1602+
}
1603+
}
1604+
}
1605+
15931606
gen(returnedExpression, returnType);
15941607
doFinallyOnReturn();
15951608
v.areturn(returnType);
@@ -1919,6 +1932,10 @@ public StackValue visitCallExpression(@NotNull JetCallExpression expression, Sta
19191932
}
19201933
}
19211934

1935+
if (tailRecursionGeneratorUtil.isTailRecursion(expression)) {
1936+
return tailRecursionGeneratorUtil.generateTailRecursion(resolvedCall, expression);
1937+
}
1938+
19221939
return invokeFunction(call, receiver, resolvedCall);
19231940
}
19241941

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright 2010-2013 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.jet.codegen;
18+
19+
import com.google.common.collect.Lists;
20+
import com.intellij.psi.PsiElement;
21+
import org.jetbrains.annotations.NotNull;
22+
import org.jetbrains.asm4.Type;
23+
import org.jetbrains.asm4.commons.InstructionAdapter;
24+
import org.jetbrains.jet.codegen.context.MethodContext;
25+
import org.jetbrains.jet.codegen.state.GenerationState;
26+
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
27+
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
28+
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
29+
import org.jetbrains.jet.lang.psi.JetCallExpression;
30+
import org.jetbrains.jet.lang.psi.JetExpression;
31+
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
32+
import org.jetbrains.jet.lang.psi.ValueArgument;
33+
import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind;
34+
import org.jetbrains.jet.lang.resolve.calls.model.*;
35+
36+
import java.util.ArrayList;
37+
import java.util.List;
38+
39+
import static org.jetbrains.jet.lang.resolve.BindingContext.TAIL_RECURSION_CALL;
40+
41+
public class TailRecursionGeneratorUtil {
42+
43+
@NotNull
44+
private final MethodContext context;
45+
@NotNull
46+
private final ExpressionCodegen codegen;
47+
@NotNull
48+
private final InstructionAdapter v;
49+
@NotNull
50+
private final GenerationState state;
51+
52+
public TailRecursionGeneratorUtil(
53+
@NotNull MethodContext context,
54+
@NotNull ExpressionCodegen codegen,
55+
@NotNull InstructionAdapter v,
56+
@NotNull GenerationState state
57+
) {
58+
this.context = context;
59+
this.codegen = codegen;
60+
this.v = v;
61+
this.state = state;
62+
}
63+
64+
public boolean isTailRecursion(@NotNull JetCallExpression expression) {
65+
TailRecursionKind status = state.getBindingContext().get(TAIL_RECURSION_CALL, expression);
66+
return status != null && status.isDoGenerateTailRecursion();
67+
}
68+
69+
public StackValue generateTailRecursion(ResolvedCall<? extends CallableDescriptor> resolvedCall, JetCallExpression callExpression) {
70+
CallableDescriptor fd = resolvedCall.getResultingDescriptor();
71+
assert fd instanceof FunctionDescriptor : "the resolved call is not refer to the function descriptor so why do we use generateTailRecursion for something strange?";
72+
CallableMethod callable = (CallableMethod) codegen.resolveToCallable((FunctionDescriptor) fd, false);
73+
List<Type> types = callable.getValueParameterTypes();
74+
List<ValueParameterDescriptor> parametersStored = prepareParameterValuesOnStack(fd, types, resolvedCall.getValueArgumentsByIndex());
75+
76+
// we can't store values to the variables in the loop above because it will affect expressions evaluation
77+
for (ValueParameterDescriptor parameterDescriptor : Lists.reverse(parametersStored)) {
78+
Type asmType = types.get(parameterDescriptor.getIndex());
79+
int index = getParameterVariableIndex(parameterDescriptor, callExpression);
80+
81+
v.store(index, asmType);
82+
}
83+
84+
v.goTo(context.getMethodStartLabel());
85+
86+
return StackValue.none();
87+
}
88+
89+
private List<ValueParameterDescriptor> prepareParameterValuesOnStack(
90+
CallableDescriptor fd,
91+
List<Type> types,
92+
List<ResolvedValueArgument> valueArguments
93+
) {
94+
List<ValueParameterDescriptor> descriptorsStored = new ArrayList<ValueParameterDescriptor>(valueArguments.size());
95+
for (ValueParameterDescriptor parameterDescriptor : fd.getValueParameters()) {
96+
ResolvedValueArgument arg = valueArguments.get(parameterDescriptor.getIndex());
97+
Type type = types.get(parameterDescriptor.getIndex());
98+
99+
if (arg instanceof ExpressionValueArgument) {
100+
ExpressionValueArgument ev = (ExpressionValueArgument) arg;
101+
ValueArgument argument = ev.getValueArgument();
102+
JetExpression argumentExpression = argument == null ? null : argument.getArgumentExpression();
103+
104+
if (argumentExpression instanceof JetSimpleNameExpression) {
105+
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) argumentExpression;
106+
if (nameExpression.getReferencedNameAsName().equals(parameterDescriptor.getName())) {
107+
// do nothing: we shouldn't store argument to itself again
108+
continue;
109+
}
110+
}
111+
112+
codegen.gen(argumentExpression, type);
113+
}
114+
else if (arg instanceof DefaultValueArgument) {
115+
DefaultParameterValueLoader.DEFAULT.putValueOnStack(parameterDescriptor, codegen);
116+
}
117+
else if (arg instanceof VarargValueArgument) {
118+
VarargValueArgument valueArgument = (VarargValueArgument) arg;
119+
codegen.genVarargs(parameterDescriptor, valueArgument);
120+
}
121+
else {
122+
throw new UnsupportedOperationException();
123+
}
124+
125+
descriptorsStored.add(parameterDescriptor);
126+
}
127+
return descriptorsStored;
128+
}
129+
130+
private int getParameterVariableIndex(ValueParameterDescriptor parameterDescriptor, PsiElement node) {
131+
int index = codegen.lookupLocalIndex(parameterDescriptor);
132+
if (index == -1) {
133+
index = codegen.lookupLocalIndex(parameterDescriptor.getOriginal());
134+
}
135+
136+
if (index == -1) {
137+
throw new CompilationException("Failed to obtain parameter index: " + parameterDescriptor.getName(), null, node);
138+
}
139+
140+
return index;
141+
}
142+
}

0 commit comments

Comments
 (0)