Expand default parameters conditions on inlining default function

#KT-14564 Fixed
 #KT-10848 Fixed
 #KT-12497 Fixed
This commit is contained in:
Mikhael Bogdanov
2017-04-26 16:08:47 +02:00
parent d49e9d7064
commit 02cc5f6bc1
16 changed files with 483 additions and 23 deletions
@@ -21,6 +21,12 @@ import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.org.objectweb.asm.Type
enum class ValueKind {
GENERAL,
DEFAULT_MASK,
METHOD_HANDLE_IN_DEFAULT
}
abstract class CallGenerator {
internal class DefaultCallGenerator(private val codegen: ExpressionCodegen) : CallGenerator() {
@@ -67,8 +73,7 @@ abstract class CallGenerator {
stackValue.put(stackValue.type, codegen.v)
}
override fun putValueIfNeeded(
parameterType: Type, value: StackValue) {
override fun putValueIfNeeded(parameterType: Type, value: StackValue, kind: ValueKind) {
value.put(value.type, codegen.v)
}
@@ -117,9 +122,16 @@ abstract class CallGenerator {
parameterType: Type,
parameterIndex: Int)
fun putValueIfNeeded(
parameterType: Type,
value: StackValue) {
putValueIfNeeded(parameterType, value, ValueKind.GENERAL)
}
abstract fun putValueIfNeeded(
parameterType: Type,
value: StackValue)
value: StackValue,
kind: ValueKind)
abstract fun putCapturedValueOnStack(
stackValue: StackValue,
@@ -32,7 +32,7 @@ class DefaultCallArgs(val size: Int) {
bits.set(index)
}
fun toInts(): List<Int> {
private fun toInts(): List<Int> {
if (bits.isEmpty || size == 0) {
return emptyList()
}
@@ -56,11 +56,11 @@ class DefaultCallArgs(val size: Int) {
val toInts = toInts()
if (!toInts.isEmpty()) {
for (mask in toInts) {
callGenerator.putValueIfNeeded(Type.INT_TYPE, StackValue.constant(mask, Type.INT_TYPE))
callGenerator.putValueIfNeeded(Type.INT_TYPE, StackValue.constant(mask, Type.INT_TYPE), ValueKind.DEFAULT_MASK)
}
val parameterType = if (isConstructor) AsmTypes.DEFAULT_CONSTRUCTOR_MARKER else AsmTypes.OBJECT_TYPE
callGenerator.putValueIfNeeded(parameterType, StackValue.constant(null, parameterType))
callGenerator.putValueIfNeeded(parameterType, StackValue.constant(null, parameterType), ValueKind.METHOD_HANDLE_IN_DEFAULT)
}
return toInts.isNotEmpty()
}
@@ -741,9 +741,9 @@ public abstract class StackValue {
}
}
private static class Constant extends StackValue {
public static class Constant extends StackValue {
@Nullable
private final Object value;
public final Object value;
public Constant(@Nullable Object value, Type type) {
super(type, false);
@@ -101,6 +101,10 @@ public class InlineCodegen extends CallGenerator {
private Runnable delayedHiddenWriting;
private List<Integer> maskValues = new ArrayList<>();
private int maskStartIndex = -1;
private int methodHandleInDefaultMethodIndex = -1;
public InlineCodegen(
@NotNull ExpressionCodegen codegen,
@NotNull GenerationState state,
@@ -177,7 +181,7 @@ public class InlineCodegen extends CallGenerator {
SMAPAndMethodNode nodeAndSmap = null;
try {
nodeAndSmap = createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault, resolvedCall);
endCall(inlineCall(nodeAndSmap));
endCall(inlineCall(nodeAndSmap, callDefault));
}
catch (CompilationException e) {
throw e;
@@ -378,7 +382,7 @@ public class InlineCodegen extends CallGenerator {
implementationOwner.getInternalName()
);
if (!(element instanceof KtNamedFunction)) {
throw new IllegalStateException("Propertiy accessors with default parameters not supported " + callableDescriptor);
throw new IllegalStateException("Property accessors with default parameters not supported " + callableDescriptor);
}
FunctionCodegen.generateDefaultImplBody(
methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
@@ -404,11 +408,14 @@ public class InlineCodegen extends CallGenerator {
}
@NotNull
private InlineResult inlineCall(@NotNull SMAPAndMethodNode nodeAndSmap) {
private InlineResult inlineCall(@NotNull SMAPAndMethodNode nodeAndSmap, boolean callDefault) {
assert delayedHiddenWriting == null : "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'";
DefaultSourceMapper defaultSourceMapper = codegen.getParentCodegen().getOrCreateSourceMapper();
defaultSourceMapper.setCallSiteMarker(new CallSiteMarker(codegen.getLastLineNumber()));
MethodNode node = nodeAndSmap.getNode();
if (callDefault) {
MethodInlinerUtilKt.expandMaskConditions(node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex);
}
ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node);
generateClosuresBodies();
@@ -676,7 +683,7 @@ public class InlineCodegen extends CallGenerator {
) {
if (!asFunctionInline && Type.VOID_TYPE != type) {
//TODO remap only inlinable closure => otherwise we could get a lot of problem
boolean couldBeRemapped = !shouldPutValue(type, stackValue);
boolean couldBeRemapped = !shouldPutGeneralValue(type, stackValue);
StackValue remappedValue = couldBeRemapped ? stackValue : null;
ParameterInfo info;
@@ -694,7 +701,7 @@ public class InlineCodegen extends CallGenerator {
}
/*descriptor is null for captured vars*/
private static boolean shouldPutValue(@NotNull Type type, @Nullable StackValue stackValue) {
private static boolean shouldPutGeneralValue(@NotNull Type type, @Nullable StackValue stackValue) {
if (stackValue == null) {
//default or vararg
return true;
@@ -899,7 +906,7 @@ public class InlineCodegen extends CallGenerator {
}
else {
StackValue value = codegen.gen(argumentExpression);
putValueIfNeeded(parameterType, value, valueParameterDescriptor.getIndex());
putValueIfNeeded(parameterType, value, valueParameterDescriptor.getIndex(), ValueKind.GENERAL);
}
}
@@ -918,20 +925,44 @@ public class InlineCodegen extends CallGenerator {
}
@Override
public void putValueIfNeeded(@NotNull Type parameterType, @NotNull StackValue value) {
putValueIfNeeded(parameterType, value, -1);
public void putValueIfNeeded(@NotNull Type parameterType, @NotNull StackValue value, @NotNull ValueKind kind) {
putValueIfNeeded(parameterType, value, -1, kind);
}
private void putValueIfNeeded(@NotNull Type parameterType, @NotNull StackValue value, int index) {
if (shouldPutValue(parameterType, value)) {
private void putValueIfNeeded(@NotNull Type parameterType, @NotNull StackValue value, int index, @NotNull ValueKind kind) {
if (processDefaultMaskOrMethodHandler(value, kind)) return;
assert maskValues.isEmpty() : "Additional default call arguments should be last ones, but " + value;
if (shouldPutGeneralValue(parameterType, value)) {
value.put(parameterType, codegen.v);
}
afterParameterPut(parameterType, value, index);
}
private boolean processDefaultMaskOrMethodHandler(@NotNull StackValue value, @NotNull ValueKind kind) {
if (kind != ValueKind.DEFAULT_MASK && kind != ValueKind.METHOD_HANDLE_IN_DEFAULT) {
return false;
}
assert value instanceof StackValue.Constant : "Additional default method argument should be constant, but " + value;
Object constantValue = ((StackValue.Constant) value).value;
if (kind == ValueKind.DEFAULT_MASK) {
assert constantValue instanceof Integer : "Mask should be of Integer type, but " + constantValue;
maskValues.add((Integer) constantValue);
if (maskStartIndex == -1) {
maskStartIndex = invocationParamBuilder.getNextParameterOffset();
}
}
else {
assert constantValue == null : "Additional method handle for default argument should be null, but " + constantValue;
methodHandleInDefaultMethodIndex = maskStartIndex + maskValues.size();
}
return true;
}
@Override
public void putCapturedValueOnStack(@NotNull StackValue stackValue, @NotNull Type valueType, int paramIndex) {
if (shouldPutValue(stackValue.type, stackValue)) {
if (shouldPutGeneralValue(stackValue.type, stackValue)) {
stackValue.put(stackValue.type, codegen.v);
}
putArgumentOrCapturedToLocalVal(stackValue.type, stackValue, paramIndex, paramIndex);
@@ -95,7 +95,7 @@ class InlineCodegenForDefaultBody(
throw UnsupportedOperationException("Shouldn't be called")
}
override fun putValueIfNeeded(parameterType: Type, value: StackValue) {
override fun putValueIfNeeded(parameterType: Type, value: StackValue, kind: ValueKind) {
//original method would be inlined directly into default impl body without any inline magic
//so we no need to load variables on stack to further method call
}
@@ -16,12 +16,12 @@
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
@@ -88,3 +88,63 @@ private fun MethodInliner.getLambdaIfExistsAndMarkInstructions(
}
fun SourceValue.singleOrNullInsn() = insns.singleOrNull()
fun expandMaskConditions(node: MethodNode, maskStartIndex: Int, masks: List<Int>, methodHandlerIndex: Int) {
class Condition(mask: Int, constant: Int, val maskInstruction: VarInsnNode, val jumpInstruction: JumpInsnNode) {
val expandNotDelete = mask and constant != 0
}
val maskProcessingHeader = node.instructions.asSequence().takeWhile {
if (it is VarInsnNode) {
if (isMaskIndex(it, maskStartIndex, masks)) {
/*if slot for default mask is updated than we occurred in actual function body*/
return@takeWhile it.opcode == Opcodes.ILOAD
}
else if (isMethodHandleIndex(methodHandlerIndex, it)) {
return@takeWhile it.opcode == Opcodes.ALOAD
}
}
true
}
val conditions = maskProcessingHeader.filterIsInstance<VarInsnNode>().mapNotNull {
if (isMaskIndex(it, maskStartIndex, masks) &&
it.next?.next?.opcode == Opcodes.IAND &&
it.next.next.next?.opcode == Opcodes.IFEQ) {
Condition(
masks[it.`var` - maskStartIndex],
InlineCodegenUtil.getConstant(it.next),
it,
it.next?.next?.next as JumpInsnNode
)
}
else if (isMethodHandleIndex(methodHandlerIndex, it) &&
it.next?.opcode == Opcodes.IFNULL &&
it.next.next?.opcode == Opcodes.NEW) {
//Always delete method handle for now
//This logic should be updated when method handles would be supported
Condition(0, 0, it,it.next as JumpInsnNode)
}
else null
}
val toDelete = arrayListOf<AbstractInsnNode>()
conditions.forEach {
val jumpInstruction = it.jumpInstruction
InsnSequence(it.maskInstruction, (if (it.expandNotDelete) jumpInstruction.next else jumpInstruction.label)).forEach {
toDelete.add(it)
}
}
toDelete.forEach {
node.instructions.remove(it)
}
}
private fun isMethodHandleIndex(methodHandlerIndex: Int, it: VarInsnNode) = methodHandlerIndex == it.`var`
private fun isMaskIndex(variable: VarInsnNode, maskStartIndex: Int, masks: List<Int>): Boolean {
val varIndex = variable.`var`
return maskStartIndex <= varIndex && varIndex < maskStartIndex + masks.size
}