From b440561f5c826b0b10e122b87f2d2a562cb88e08 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Thu, 27 Apr 2017 12:22:24 +0200 Subject: [PATCH] Generate default arguments and varargs lazily The aim to perform optimizations in call generators --- .../codegen/CallBasedArgumentGenerator.java | 17 ++-- .../jetbrains/kotlin/codegen/CallGenerator.kt | 4 +- .../kotlin/codegen/ExpressionCodegen.java | 96 ++++++++++--------- .../jetbrains/kotlin/codegen/StackValue.java | 29 ++++++ .../kotlin/codegen/inline/InlineCodegen.java | 11 +-- .../inline/InlineCodegenForDefaultBody.kt | 2 +- 6 files changed, 95 insertions(+), 64 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallBasedArgumentGenerator.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallBasedArgumentGenerator.java index 49ef24c6795..1670f125c3e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallBasedArgumentGenerator.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallBasedArgumentGenerator.java @@ -17,18 +17,18 @@ package org.jetbrains.kotlin.codegen; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.ValueArgument; -import org.jetbrains.kotlin.resolve.calls.model.*; +import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument; +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument; +import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument; import org.jetbrains.kotlin.types.FlexibleTypesKt; import org.jetbrains.org.objectweb.asm.Type; import java.util.List; -import static org.jetbrains.kotlin.codegen.AsmUtil.pushDefaultValueOnStack; +import static org.jetbrains.kotlin.codegen.StackValue.createDefaulValue; public class CallBasedArgumentGenerator extends ArgumentGenerator { private final ExpressionCodegen codegen; @@ -64,19 +64,16 @@ public class CallBasedArgumentGenerator extends ArgumentGenerator { @Override protected void generateDefault(int i, @NotNull DefaultValueArgument argument) { - Type type = valueParameterTypes.get(i); - pushDefaultValueOnStack(type, codegen.v); - callGenerator.afterParameterPut(type, null, i); + callGenerator.putValueIfNeeded(valueParameterTypes.get(i), createDefaulValue(valueParameterTypes.get(i))); } @Override protected void generateVararg(int i, @NotNull VarargValueArgument argument) { ValueParameterDescriptor parameter = valueParameters.get(i); - Type type = valueParameterTypes.get(i); // Upper bound for type of vararg parameter should always have a form of 'Array', // while its lower bound may be Nothing-typed after approximation - codegen.genVarargs(argument, FlexibleTypesKt.upperIfFlexible(parameter.getType())); - callGenerator.afterParameterPut(type, null, i); + StackValue lazyVararg = codegen.genVarargs(argument, FlexibleTypesKt.upperIfFlexible(parameter.getType())); + callGenerator.putValueIfNeeded(valueParameterTypes.get(i), lazyVararg); } @Override diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt index 58f860612f4..ffebc58c567 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt @@ -46,7 +46,7 @@ abstract class CallGenerator { override fun afterParameterPut( type: Type, - stackValue: StackValue?, + stackValue: StackValue, parameterIndex: Int) { } @@ -113,7 +113,7 @@ abstract class CallGenerator { abstract fun afterParameterPut( type: Type, - stackValue: StackValue?, + stackValue: StackValue, parameterIndex: Int) abstract fun genValueAndPut( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index fc158df1888..b30f6506775 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -39,7 +39,8 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen; import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.coroutines.ResolvedCallWithRealDescriptor; import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; -import org.jetbrains.kotlin.codegen.forLoop.*; +import org.jetbrains.kotlin.codegen.forLoop.AbstractForLoopGenerator; +import org.jetbrains.kotlin.codegen.forLoop.ForLoopGeneratorsKt; import org.jetbrains.kotlin.codegen.inline.*; import org.jetbrains.kotlin.codegen.intrinsics.*; import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt; @@ -106,7 +107,8 @@ import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker; import static org.jetbrains.kotlin.resolve.BindingContext.*; -import static org.jetbrains.kotlin.resolve.BindingContextUtils.*; +import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall; +import static org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; @@ -2535,7 +2537,8 @@ public class ExpressionCodegen extends KtVisitor impleme } - public void genVarargs(@NotNull VarargValueArgument valueArgument, @NotNull KotlinType outType) { + @NotNull + public StackValue genVarargs(@NotNull VarargValueArgument valueArgument, @NotNull KotlinType outType) { Type type = asmType(outType); assert type.getSort() == Type.ARRAY; Type elementType = correctElementType(type); @@ -2553,17 +2556,17 @@ public class ExpressionCodegen extends KtVisitor impleme if (hasSpread) { boolean arrayOfReferences = KotlinBuiltIns.isArray(outType); if (size == 1) { - // Arrays.copyOf(receiverValue, newLength) - ValueArgument argument = arguments.get(0); - Type arrayType = arrayOfReferences ? Type.getType("[Ljava/lang/Object;") - : Type.getType("[" + elementType.getDescriptor()); - gen(argument.getArgumentExpression(), type); - v.dup(); - v.arraylength(); - v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false); - if (arrayOfReferences) { - v.checkcast(type); - } + Type arrayType = getArrayType(arrayOfReferences ? AsmTypes.OBJECT_TYPE : elementType); + return StackValue.operation(type, adapter -> { + gen(arguments.get(0).getArgumentExpression(), type); + v.dup(); + v.arraylength(); + v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false); + if (arrayOfReferences) { + v.checkcast(type); + } + return Unit.INSTANCE; + }); } else { String owner; @@ -2580,42 +2583,49 @@ public class ExpressionCodegen extends KtVisitor impleme addDescriptor = "(" + elementType.getDescriptor() + ")V"; toArrayDescriptor = "()" + type.getDescriptor(); } - v.anew(Type.getObjectType(owner)); - v.dup(); - v.iconst(size); - v.invokespecial(owner, "", "(I)V", false); - for (int i = 0; i != size; ++i) { + + return StackValue.operation(type, adapter -> { + v.anew(Type.getObjectType(owner)); v.dup(); - ValueArgument argument = arguments.get(i); - if (argument.getSpreadElement() != null) { - gen(argument.getArgumentExpression(), OBJECT_TYPE); - v.invokevirtual(owner, "addSpread", "(Ljava/lang/Object;)V", false); + v.iconst(size); + v.invokespecial(owner, "", "(I)V", false); + for (int i = 0; i != size; ++i) { + v.dup(); + ValueArgument argument = arguments.get(i); + if (argument.getSpreadElement() != null) { + gen(argument.getArgumentExpression(), OBJECT_TYPE); + v.invokevirtual(owner, "addSpread", "(Ljava/lang/Object;)V", false); + } + else { + gen(argument.getArgumentExpression(), elementType); + v.invokevirtual(owner, "add", addDescriptor, false); + } + } + if (arrayOfReferences) { + v.dup(); + v.invokevirtual(owner, "size", "()I", false); + newArrayInstruction(outType); + v.invokevirtual(owner, "toArray", toArrayDescriptor, false); + v.checkcast(type); } else { - gen(argument.getArgumentExpression(), elementType); - v.invokevirtual(owner, "add", addDescriptor, false); + v.invokevirtual(owner, "toArray", toArrayDescriptor, false); } - } - if (arrayOfReferences) { - v.dup(); - v.invokevirtual(owner, "size", "()I", false); - newArrayInstruction(outType); - v.invokevirtual(owner, "toArray", toArrayDescriptor, false); - v.checkcast(type); - } - else { - v.invokevirtual(owner, "toArray", toArrayDescriptor, false); - } + return Unit.INSTANCE; + }); } } else { - v.iconst(arguments.size()); - newArrayInstruction(outType); - for (int i = 0; i != size; ++i) { - v.dup(); - StackValue rightSide = gen(arguments.get(i).getArgumentExpression()); - StackValue.arrayElement(elementType, StackValue.onStack(type), StackValue.constant(i, Type.INT_TYPE)).store(rightSide, v); - } + return StackValue.operation(type, adapter -> { + v.iconst(arguments.size()); + newArrayInstruction(outType); + for (int i = 0; i != size; ++i) { + v.dup(); + StackValue rightSide = gen(arguments.get(i).getArgumentExpression()); + StackValue.arrayElement(elementType, StackValue.onStack(type), StackValue.constant(i, Type.INT_TYPE)).store(rightSide, v); + } + return Unit.INSTANCE; + }); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 5a365f52779..fa4c32b579e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -181,6 +181,35 @@ public abstract class StackValue { } } + public static StackValue createDefaulValue(@NotNull Type type) { + if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { + return constant(null, type); + } + else { + return createDefaultPrimitiveValue(type); + } + } + + private static StackValue createDefaultPrimitiveValue(@NotNull Type type) { + assert Type.BOOLEAN <= type.getSort() && type.getSort() <= Type.DOUBLE : + "'createDefaultPrimitiveValue' method should be called only for primitive types, but " + type; + Object value = 0; + if (type.getSort() == Type.BOOLEAN) { + value = Boolean.FALSE; + } + else if (type.getSort() == Type.FLOAT) { + value = new Float(0.0); + } + else if (type.getSort() == Type.DOUBLE) { + value = new Double(0.0); + } + else if (type.getSort() == Type.LONG) { + value = new Long(0); + } + + return constant(value, type); + } + @NotNull public static StackValue cmp(@NotNull IElementType opToken, @NotNull Type type, StackValue left, StackValue right) { return BranchedValue.Companion.cmp(opToken, type, left, right); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java index da018ac5823..6668026a280 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -671,13 +671,13 @@ public class InlineCodegen extends CallGenerator { } @Override - public void afterParameterPut(@NotNull Type type, @Nullable StackValue stackValue, int parameterIndex) { + public void afterParameterPut(@NotNull Type type, @NotNull StackValue stackValue, int parameterIndex) { putArgumentOrCapturedToLocalVal(type, stackValue, -1, parameterIndex); } private void putArgumentOrCapturedToLocalVal( @NotNull Type type, - @Nullable StackValue stackValue, + @NotNull StackValue stackValue, int capturedParamIndex, int parameterIndex ) { @@ -701,12 +701,7 @@ public class InlineCodegen extends CallGenerator { } /*descriptor is null for captured vars*/ - private static boolean shouldPutGeneralValue(@NotNull Type type, @Nullable StackValue stackValue) { - if (stackValue == null) { - //default or vararg - return true; - } - + private static boolean shouldPutGeneralValue(@NotNull Type type, @NotNull StackValue stackValue) { //remap only inline functions (and maybe non primitives) //TODO - clean asserion and remapping logic if (isPrimitive(type) != isPrimitive(stackValue.type)) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenForDefaultBody.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenForDefaultBody.kt index 53031291535..641b319e6ff 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenForDefaultBody.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenForDefaultBody.kt @@ -87,7 +87,7 @@ class InlineCodegenForDefaultBody( transformedMethod.accept(MethodBodyVisitor(codegen.v)) } - override fun afterParameterPut(type: Type, stackValue: StackValue?, parameterIndex: Int) { + override fun afterParameterPut(type: Type, stackValue: StackValue, parameterIndex: Int) { throw UnsupportedOperationException("Shouldn't be called") }