TCO: Re-use parameter values already loaded onto the stack for a recursive call

This commit is contained in:
Andrey Breslav
2013-11-28 19:37:21 +04:00
parent 2a03d40233
commit 9f319e8b24
2 changed files with 40 additions and 31 deletions
@@ -1996,10 +1996,6 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
StackValue receiver, StackValue receiver,
ResolvedCall<? extends CallableDescriptor> resolvedCall ResolvedCall<? extends CallableDescriptor> resolvedCall
) { ) {
if (tailRecursionGeneratorUtil.isTailRecursion(resolvedCall)) {
return tailRecursionGeneratorUtil.generateTailRecursion(resolvedCall, call);
}
if (resolvedCall instanceof VariableAsFunctionResolvedCall) { if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
VariableAsFunctionResolvedCall variableAsFunctionResolvedCall = (VariableAsFunctionResolvedCall) resolvedCall; VariableAsFunctionResolvedCall variableAsFunctionResolvedCall = (VariableAsFunctionResolvedCall) resolvedCall;
ResolvedCallWithTrace<FunctionDescriptor> functionCall = variableAsFunctionResolvedCall.getFunctionCall(); ResolvedCallWithTrace<FunctionDescriptor> functionCall = variableAsFunctionResolvedCall.getFunctionCall();
@@ -2149,6 +2145,12 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
private void pushArgumentsAndInvoke(@NotNull ResolvedCall<?> resolvedCall, @NotNull CallableMethod callable) { private void pushArgumentsAndInvoke(@NotNull ResolvedCall<?> resolvedCall, @NotNull CallableMethod callable) {
int mask = pushMethodArguments(resolvedCall, callable.getValueParameterTypes()); int mask = pushMethodArguments(resolvedCall, callable.getValueParameterTypes());
if (tailRecursionGeneratorUtil.isTailRecursion(resolvedCall)) {
tailRecursionGeneratorUtil.generateTailRecursion(resolvedCall);
return;
}
if (mask == 0) { if (mask == 0) {
callable.invokeWithNotNullAssertion(v, state, resolvedCall); callable.invokeWithNotNullAssertion(v, state, resolvedCall);
} }
@@ -25,16 +25,15 @@ import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.Call;
import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.psi.ValueArgument; import org.jetbrains.jet.lang.psi.ValueArgument;
import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind; import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind;
import org.jetbrains.jet.lang.resolve.calls.model.*; import org.jetbrains.jet.lang.resolve.calls.model.*;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL;
import static org.jetbrains.jet.lang.resolve.BindingContext.TAIL_RECURSION_CALL; import static org.jetbrains.jet.lang.resolve.BindingContext.TAIL_RECURSION_CALL;
public class TailRecursionGeneratorUtil { public class TailRecursionGeneratorUtil {
@@ -65,33 +64,36 @@ public class TailRecursionGeneratorUtil {
return status != null && status.isDoGenerateTailRecursion(); return status != null && status.isDoGenerateTailRecursion();
} }
public StackValue generateTailRecursion(ResolvedCall<? extends CallableDescriptor> resolvedCall, Call call) { public void generateTailRecursion(ResolvedCall<? extends CallableDescriptor> resolvedCall) {
CallableDescriptor fd = resolvedCall.getResultingDescriptor(); CallableDescriptor fd = resolvedCall.getResultingDescriptor();
assert fd instanceof FunctionDescriptor : "the resolved call is not refer to the function descriptor so why do we use generateTailRecursion for something strange?"; assert fd instanceof FunctionDescriptor : "the resolved call is not refer to the function descriptor so why do we use generateTailRecursion for something strange?";
CallableMethod callable = (CallableMethod) codegen.resolveToCallable((FunctionDescriptor) fd, false); CallableMethod callable = (CallableMethod) codegen.resolveToCallable((FunctionDescriptor) fd, false);
List<Type> types = callable.getValueParameterTypes();
List<ValueParameterDescriptor> parametersStored = prepareParameterValuesOnStack(fd, types, resolvedCall.getValueArgumentsByIndex());
// we can't store values to the variables in the loop above because it will affect expressions evaluation assignParameterValues(fd, callable, resolvedCall.getValueArgumentsByIndex());
for (ValueParameterDescriptor parameterDescriptor : Lists.reverse(parametersStored)) { if (callable.getReceiverClass() != null) {
Type asmType = types.get(parameterDescriptor.getIndex()); if (resolvedCall.getReceiverArgument() != fd.getReceiverParameter().getValue()) {
int index = getParameterVariableIndex(parameterDescriptor, call); StackValue expression = context.getReceiverExpression(codegen.typeMapper);
expression.store(callable.getReceiverClass(), v);
}
else {
AsmUtil.pop(v, callable.getReceiverClass());
}
}
v.store(index, asmType); if (callable.getThisType() != null) {
AsmUtil.pop(v, callable.getThisType());
} }
v.goTo(context.getMethodStartLabel()); v.goTo(context.getMethodStartLabel());
return StackValue.none();
} }
private List<ValueParameterDescriptor> prepareParameterValuesOnStack( private void assignParameterValues(
CallableDescriptor fd, CallableDescriptor fd,
List<Type> types, CallableMethod callableMethod,
List<ResolvedValueArgument> valueArguments List<ResolvedValueArgument> valueArguments
) { ) {
List<ValueParameterDescriptor> descriptorsStored = new ArrayList<ValueParameterDescriptor>(valueArguments.size()); List<Type> types = callableMethod.getValueParameterTypes();
for (ValueParameterDescriptor parameterDescriptor : fd.getValueParameters()) { for (ValueParameterDescriptor parameterDescriptor : Lists.reverse(fd.getValueParameters())) {
ResolvedValueArgument arg = valueArguments.get(parameterDescriptor.getIndex()); ResolvedValueArgument arg = valueArguments.get(parameterDescriptor.getIndex());
Type type = types.get(parameterDescriptor.getIndex()); Type type = types.get(parameterDescriptor.getIndex());
@@ -101,39 +103,44 @@ public class TailRecursionGeneratorUtil {
JetExpression argumentExpression = argument == null ? null : argument.getArgumentExpression(); JetExpression argumentExpression = argument == null ? null : argument.getArgumentExpression();
if (argumentExpression instanceof JetSimpleNameExpression) { if (argumentExpression instanceof JetSimpleNameExpression) {
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) argumentExpression; ResolvedCall<? extends CallableDescriptor> resolvedCall = state.getBindingContext().get(RESOLVED_CALL, argumentExpression);
if (nameExpression.getReferencedNameAsName().equals(parameterDescriptor.getName())) { if (resolvedCall != null && resolvedCall.getResultingDescriptor().equals(parameterDescriptor.getOriginal())) {
// do nothing: we shouldn't store argument to itself again // do nothing: we shouldn't store argument to itself again
AsmUtil.pop(v, type);
continue; continue;
} }
} }
//assign the parameter below
codegen.gen(argumentExpression, type);
} }
else if (arg instanceof DefaultValueArgument) { else if (arg instanceof DefaultValueArgument) {
AsmUtil.pop(v, type);
DefaultParameterValueLoader.DEFAULT.putValueOnStack(parameterDescriptor, codegen); DefaultParameterValueLoader.DEFAULT.putValueOnStack(parameterDescriptor, codegen);
} }
else if (arg instanceof VarargValueArgument) { else if (arg instanceof VarargValueArgument) {
VarargValueArgument valueArgument = (VarargValueArgument) arg; // assign the parameter below
codegen.genVarargs(parameterDescriptor, valueArgument);
} }
else { else {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException("Unknown argument type: " + arg + " in " + fd);
} }
descriptorsStored.add(parameterDescriptor); store(parameterDescriptor, type);
} }
return descriptorsStored;
} }
private int getParameterVariableIndex(ValueParameterDescriptor parameterDescriptor, Call call) { private void store(ValueParameterDescriptor parameterDescriptor, Type type) {
int index = getParameterVariableIndex(parameterDescriptor);
v.store(index, type);
}
private int getParameterVariableIndex(ValueParameterDescriptor parameterDescriptor) {
int index = codegen.lookupLocalIndex(parameterDescriptor); int index = codegen.lookupLocalIndex(parameterDescriptor);
if (index == -1) { if (index == -1) {
// in the case of a generic function recursively calling itself, the parameters on the call site are substituted
index = codegen.lookupLocalIndex(parameterDescriptor.getOriginal()); index = codegen.lookupLocalIndex(parameterDescriptor.getOriginal());
} }
if (index == -1) { if (index == -1) {
throw new CompilationException("Failed to obtain parameter index: " + parameterDescriptor.getName(), null, call.getCallElement()); throw new IllegalStateException("Failed to obtain parameter index: " + parameterDescriptor);
} }
return index; return index;