Generate default arguments and varargs lazily

The aim to perform optimizations in call generators
This commit is contained in:
Mikhael Bogdanov
2017-04-27 12:22:24 +02:00
parent 02cc5f6bc1
commit b440561f5c
6 changed files with 95 additions and 64 deletions
@@ -17,18 +17,18 @@
package org.jetbrains.kotlin.codegen; package org.jetbrains.kotlin.codegen;
import org.jetbrains.annotations.NotNull; 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.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.ValueArgument; 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.kotlin.types.FlexibleTypesKt;
import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.Type;
import java.util.List; 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 { public class CallBasedArgumentGenerator extends ArgumentGenerator {
private final ExpressionCodegen codegen; private final ExpressionCodegen codegen;
@@ -64,19 +64,16 @@ public class CallBasedArgumentGenerator extends ArgumentGenerator {
@Override @Override
protected void generateDefault(int i, @NotNull DefaultValueArgument argument) { protected void generateDefault(int i, @NotNull DefaultValueArgument argument) {
Type type = valueParameterTypes.get(i); callGenerator.putValueIfNeeded(valueParameterTypes.get(i), createDefaulValue(valueParameterTypes.get(i)));
pushDefaultValueOnStack(type, codegen.v);
callGenerator.afterParameterPut(type, null, i);
} }
@Override @Override
protected void generateVararg(int i, @NotNull VarargValueArgument argument) { protected void generateVararg(int i, @NotNull VarargValueArgument argument) {
ValueParameterDescriptor parameter = valueParameters.get(i); ValueParameterDescriptor parameter = valueParameters.get(i);
Type type = valueParameterTypes.get(i);
// Upper bound for type of vararg parameter should always have a form of 'Array<out T>', // Upper bound for type of vararg parameter should always have a form of 'Array<out T>',
// while its lower bound may be Nothing-typed after approximation // while its lower bound may be Nothing-typed after approximation
codegen.genVarargs(argument, FlexibleTypesKt.upperIfFlexible(parameter.getType())); StackValue lazyVararg = codegen.genVarargs(argument, FlexibleTypesKt.upperIfFlexible(parameter.getType()));
callGenerator.afterParameterPut(type, null, i); callGenerator.putValueIfNeeded(valueParameterTypes.get(i), lazyVararg);
} }
@Override @Override
@@ -46,7 +46,7 @@ abstract class CallGenerator {
override fun afterParameterPut( override fun afterParameterPut(
type: Type, type: Type,
stackValue: StackValue?, stackValue: StackValue,
parameterIndex: Int) { parameterIndex: Int) {
} }
@@ -113,7 +113,7 @@ abstract class CallGenerator {
abstract fun afterParameterPut( abstract fun afterParameterPut(
type: Type, type: Type,
stackValue: StackValue?, stackValue: StackValue,
parameterIndex: Int) parameterIndex: Int)
abstract fun genValueAndPut( abstract fun genValueAndPut(
@@ -39,7 +39,8 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.coroutines.ResolvedCallWithRealDescriptor; import org.jetbrains.kotlin.codegen.coroutines.ResolvedCallWithRealDescriptor;
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; 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.inline.*;
import org.jetbrains.kotlin.codegen.intrinsics.*; import org.jetbrains.kotlin.codegen.intrinsics.*;
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt; 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.binding.CodegenBinding.*;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker;
import static org.jetbrains.kotlin.resolve.BindingContext.*; 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.isEnumEntry;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
@@ -2535,7 +2537,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
} }
public void genVarargs(@NotNull VarargValueArgument valueArgument, @NotNull KotlinType outType) { @NotNull
public StackValue genVarargs(@NotNull VarargValueArgument valueArgument, @NotNull KotlinType outType) {
Type type = asmType(outType); Type type = asmType(outType);
assert type.getSort() == Type.ARRAY; assert type.getSort() == Type.ARRAY;
Type elementType = correctElementType(type); Type elementType = correctElementType(type);
@@ -2553,17 +2556,17 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (hasSpread) { if (hasSpread) {
boolean arrayOfReferences = KotlinBuiltIns.isArray(outType); boolean arrayOfReferences = KotlinBuiltIns.isArray(outType);
if (size == 1) { if (size == 1) {
// Arrays.copyOf(receiverValue, newLength) Type arrayType = getArrayType(arrayOfReferences ? AsmTypes.OBJECT_TYPE : elementType);
ValueArgument argument = arguments.get(0); return StackValue.operation(type, adapter -> {
Type arrayType = arrayOfReferences ? Type.getType("[Ljava/lang/Object;") gen(arguments.get(0).getArgumentExpression(), type);
: Type.getType("[" + elementType.getDescriptor()); v.dup();
gen(argument.getArgumentExpression(), type); v.arraylength();
v.dup(); v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false);
v.arraylength(); if (arrayOfReferences) {
v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false); v.checkcast(type);
if (arrayOfReferences) { }
v.checkcast(type); return Unit.INSTANCE;
} });
} }
else { else {
String owner; String owner;
@@ -2580,42 +2583,49 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
addDescriptor = "(" + elementType.getDescriptor() + ")V"; addDescriptor = "(" + elementType.getDescriptor() + ")V";
toArrayDescriptor = "()" + type.getDescriptor(); toArrayDescriptor = "()" + type.getDescriptor();
} }
v.anew(Type.getObjectType(owner));
v.dup(); return StackValue.operation(type, adapter -> {
v.iconst(size); v.anew(Type.getObjectType(owner));
v.invokespecial(owner, "<init>", "(I)V", false);
for (int i = 0; i != size; ++i) {
v.dup(); v.dup();
ValueArgument argument = arguments.get(i); v.iconst(size);
if (argument.getSpreadElement() != null) { v.invokespecial(owner, "<init>", "(I)V", false);
gen(argument.getArgumentExpression(), OBJECT_TYPE); for (int i = 0; i != size; ++i) {
v.invokevirtual(owner, "addSpread", "(Ljava/lang/Object;)V", false); 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 { else {
gen(argument.getArgumentExpression(), elementType); v.invokevirtual(owner, "toArray", toArrayDescriptor, false);
v.invokevirtual(owner, "add", addDescriptor, false);
} }
} return Unit.INSTANCE;
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);
}
} }
} }
else { else {
v.iconst(arguments.size()); return StackValue.operation(type, adapter -> {
newArrayInstruction(outType); v.iconst(arguments.size());
for (int i = 0; i != size; ++i) { newArrayInstruction(outType);
v.dup(); for (int i = 0; i != size; ++i) {
StackValue rightSide = gen(arguments.get(i).getArgumentExpression()); v.dup();
StackValue.arrayElement(elementType, StackValue.onStack(type), StackValue.constant(i, Type.INT_TYPE)).store(rightSide, v); 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;
});
} }
} }
@@ -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 @NotNull
public static StackValue cmp(@NotNull IElementType opToken, @NotNull Type type, StackValue left, StackValue right) { public static StackValue cmp(@NotNull IElementType opToken, @NotNull Type type, StackValue left, StackValue right) {
return BranchedValue.Companion.cmp(opToken, type, left, right); return BranchedValue.Companion.cmp(opToken, type, left, right);
@@ -671,13 +671,13 @@ public class InlineCodegen extends CallGenerator {
} }
@Override @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); putArgumentOrCapturedToLocalVal(type, stackValue, -1, parameterIndex);
} }
private void putArgumentOrCapturedToLocalVal( private void putArgumentOrCapturedToLocalVal(
@NotNull Type type, @NotNull Type type,
@Nullable StackValue stackValue, @NotNull StackValue stackValue,
int capturedParamIndex, int capturedParamIndex,
int parameterIndex int parameterIndex
) { ) {
@@ -701,12 +701,7 @@ public class InlineCodegen extends CallGenerator {
} }
/*descriptor is null for captured vars*/ /*descriptor is null for captured vars*/
private static boolean shouldPutGeneralValue(@NotNull Type type, @Nullable StackValue stackValue) { private static boolean shouldPutGeneralValue(@NotNull Type type, @NotNull StackValue stackValue) {
if (stackValue == null) {
//default or vararg
return true;
}
//remap only inline functions (and maybe non primitives) //remap only inline functions (and maybe non primitives)
//TODO - clean asserion and remapping logic //TODO - clean asserion and remapping logic
if (isPrimitive(type) != isPrimitive(stackValue.type)) { if (isPrimitive(type) != isPrimitive(stackValue.type)) {
@@ -87,7 +87,7 @@ class InlineCodegenForDefaultBody(
transformedMethod.accept(MethodBodyVisitor(codegen.v)) 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") throw UnsupportedOperationException("Shouldn't be called")
} }