Expand default parameters conditions on inlining default function
#KT-14564 Fixed #KT-10848 Fixed #KT-12497 Fixed
This commit is contained in:
@@ -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);
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user