diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index ce75b3c9ea7..d4661d38dc7 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -111,6 +111,8 @@ public class ExpressionCodegen extends JetVisitor implem * we put it into this map to emit access to that variable instead of evaluating the whole expression */ private final Map tempVariables = Maps.newHashMap(); + @NotNull + private final TailRecursionGeneratorUtil tailRecursionGeneratorUtil; public CalculatedClosure generateObjectLiteral(GenerationState state, JetObjectLiteralExpression literal) { JetObjectDeclaration objectDeclaration = literal.getObjectDeclaration(); @@ -180,6 +182,7 @@ public class ExpressionCodegen extends JetVisitor implem this.bindingContext = state.getBindingContext(); this.context = context; this.statementVisitor = new CodegenStatementVisitor(this); + this.tailRecursionGeneratorUtil = new TailRecursionGeneratorUtil(context, this, this.v, state); } protected InstructionAdapter createInstructionAdapter(MethodVisitor mv) { @@ -1590,6 +1593,16 @@ public class ExpressionCodegen extends JetVisitor implem public StackValue visitReturnExpression(@NotNull JetReturnExpression expression, StackValue receiver) { JetExpression returnedExpression = expression.getReturnedExpression(); if (returnedExpression != null) { + if (returnedExpression instanceof JetCallExpression) { + JetCallExpression callExpression = (JetCallExpression) returnedExpression; + if (tailRecursionGeneratorUtil.isTailRecursion(callExpression) && callExpression.getCalleeExpression() != null) { + ResolvedCall resolvedCall = bindingContext.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression()); + if (resolvedCall != null) { + return tailRecursionGeneratorUtil.generateTailRecursion(resolvedCall, callExpression); + } + } + } + gen(returnedExpression, returnType); doFinallyOnReturn(); v.areturn(returnType); @@ -1919,6 +1932,10 @@ public class ExpressionCodegen extends JetVisitor implem } } + if (tailRecursionGeneratorUtil.isTailRecursion(expression)) { + return tailRecursionGeneratorUtil.generateTailRecursion(resolvedCall, expression); + } + return invokeFunction(call, receiver, resolvedCall); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/TailRecursionGeneratorUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/TailRecursionGeneratorUtil.java new file mode 100644 index 00000000000..03bf669669a --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/TailRecursionGeneratorUtil.java @@ -0,0 +1,142 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen; + +import com.google.common.collect.Lists; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.asm4.Type; +import org.jetbrains.asm4.commons.InstructionAdapter; +import org.jetbrains.jet.codegen.context.MethodContext; +import org.jetbrains.jet.codegen.state.GenerationState; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; +import org.jetbrains.jet.lang.psi.JetCallExpression; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.jet.lang.psi.ValueArgument; +import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind; +import org.jetbrains.jet.lang.resolve.calls.model.*; + +import java.util.ArrayList; +import java.util.List; + +import static org.jetbrains.jet.lang.resolve.BindingContext.TAIL_RECURSION_CALL; + +public class TailRecursionGeneratorUtil { + + @NotNull + private final MethodContext context; + @NotNull + private final ExpressionCodegen codegen; + @NotNull + private final InstructionAdapter v; + @NotNull + private final GenerationState state; + + public TailRecursionGeneratorUtil( + @NotNull MethodContext context, + @NotNull ExpressionCodegen codegen, + @NotNull InstructionAdapter v, + @NotNull GenerationState state + ) { + this.context = context; + this.codegen = codegen; + this.v = v; + this.state = state; + } + + public boolean isTailRecursion(@NotNull JetCallExpression expression) { + TailRecursionKind status = state.getBindingContext().get(TAIL_RECURSION_CALL, expression); + return status != null && status.isDoGenerateTailRecursion(); + } + + public StackValue generateTailRecursion(ResolvedCall resolvedCall, JetCallExpression callExpression) { + 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?"; + CallableMethod callable = (CallableMethod) codegen.resolveToCallable((FunctionDescriptor) fd, false); + List types = callable.getValueParameterTypes(); + List parametersStored = prepareParameterValuesOnStack(fd, types, resolvedCall.getValueArgumentsByIndex()); + + // we can't store values to the variables in the loop above because it will affect expressions evaluation + for (ValueParameterDescriptor parameterDescriptor : Lists.reverse(parametersStored)) { + Type asmType = types.get(parameterDescriptor.getIndex()); + int index = getParameterVariableIndex(parameterDescriptor, callExpression); + + v.store(index, asmType); + } + + v.goTo(context.getMethodStartLabel()); + + return StackValue.none(); + } + + private List prepareParameterValuesOnStack( + CallableDescriptor fd, + List types, + List valueArguments + ) { + List descriptorsStored = new ArrayList(valueArguments.size()); + for (ValueParameterDescriptor parameterDescriptor : fd.getValueParameters()) { + ResolvedValueArgument arg = valueArguments.get(parameterDescriptor.getIndex()); + Type type = types.get(parameterDescriptor.getIndex()); + + if (arg instanceof ExpressionValueArgument) { + ExpressionValueArgument ev = (ExpressionValueArgument) arg; + ValueArgument argument = ev.getValueArgument(); + JetExpression argumentExpression = argument == null ? null : argument.getArgumentExpression(); + + if (argumentExpression instanceof JetSimpleNameExpression) { + JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) argumentExpression; + if (nameExpression.getReferencedNameAsName().equals(parameterDescriptor.getName())) { + // do nothing: we shouldn't store argument to itself again + continue; + } + } + + codegen.gen(argumentExpression, type); + } + else if (arg instanceof DefaultValueArgument) { + DefaultParameterValueLoader.DEFAULT.putValueOnStack(parameterDescriptor, codegen); + } + else if (arg instanceof VarargValueArgument) { + VarargValueArgument valueArgument = (VarargValueArgument) arg; + codegen.genVarargs(parameterDescriptor, valueArgument); + } + else { + throw new UnsupportedOperationException(); + } + + descriptorsStored.add(parameterDescriptor); + } + return descriptorsStored; + } + + private int getParameterVariableIndex(ValueParameterDescriptor parameterDescriptor, PsiElement node) { + int index = codegen.lookupLocalIndex(parameterDescriptor); + if (index == -1) { + index = codegen.lookupLocalIndex(parameterDescriptor.getOriginal()); + } + + if (index == -1) { + throw new CompilationException("Failed to obtain parameter index: " + parameterDescriptor.getName(), null, node); + } + + return index; + } +}