JVM: refactor extraction of default inline lambdas a bit more
This commit is contained in:
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||||
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||||
import org.jetbrains.kotlin.types.upperIfFlexible
|
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -51,7 +52,7 @@ class CallBasedArgumentGenerator(
|
|||||||
callGenerator.putValueIfNeeded(
|
callGenerator.putValueIfNeeded(
|
||||||
getJvmKotlinType(i),
|
getJvmKotlinType(i),
|
||||||
StackValue.createDefaultValue(valueParameterTypes[i]),
|
StackValue.createDefaultValue(valueParameterTypes[i]),
|
||||||
ValueKind.DEFAULT_PARAMETER,
|
if (InlineUtil.isInlineParameter(valueParameters[i])) ValueKind.DEFAULT_INLINE_PARAMETER else ValueKind.DEFAULT_PARAMETER,
|
||||||
i
|
i
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ enum class ValueKind {
|
|||||||
GENERAL,
|
GENERAL,
|
||||||
GENERAL_VARARG,
|
GENERAL_VARARG,
|
||||||
DEFAULT_PARAMETER,
|
DEFAULT_PARAMETER,
|
||||||
|
DEFAULT_INLINE_PARAMETER,
|
||||||
DEFAULT_MASK,
|
DEFAULT_MASK,
|
||||||
METHOD_HANDLE_IN_DEFAULT,
|
METHOD_HANDLE_IN_DEFAULT,
|
||||||
CAPTURED,
|
|
||||||
NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND,
|
NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND,
|
||||||
NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER
|
NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,10 +71,12 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
|||||||
private fun inlineCall(nodeAndSmap: SMAPAndMethodNode, isInlineOnly: Boolean): InlineResult {
|
private fun inlineCall(nodeAndSmap: SMAPAndMethodNode, isInlineOnly: Boolean): InlineResult {
|
||||||
val node = nodeAndSmap.node
|
val node = nodeAndSmap.node
|
||||||
if (maskStartIndex != -1) {
|
if (maskStartIndex != -1) {
|
||||||
val infos = expandMaskConditionsAndUpdateVariableNodes(
|
|
||||||
node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex, computeDefaultLambdaOffsets()
|
|
||||||
)
|
|
||||||
val parameters = invocationParamBuilder.buildParameters()
|
val parameters = invocationParamBuilder.buildParameters()
|
||||||
|
val infos = expandMaskConditionsAndUpdateVariableNodes(
|
||||||
|
node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex,
|
||||||
|
parameters.parameters.filter { it.functionalArgument === DefaultValueOfInlineParameter }
|
||||||
|
.mapTo(mutableSetOf()) { parameters.getDeclarationSlot(it) }
|
||||||
|
)
|
||||||
for (info in infos) {
|
for (info in infos) {
|
||||||
val lambda = DefaultLambda(info, sourceCompiler)
|
val lambda = DefaultLambda(info, sourceCompiler)
|
||||||
parameters.getParameterByDeclarationSlot(info.offset).functionalArgument = lambda
|
parameters.getParameterByDeclarationSlot(info.offset).functionalArgument = lambda
|
||||||
@@ -83,7 +85,11 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
|||||||
if (info.needReification) {
|
if (info.needReification) {
|
||||||
lambda.reifiedTypeParametersUsages.mergeAll(reifiedTypeInliner.reifyInstructions(lambda.node.node))
|
lambda.reifiedTypeParametersUsages.mergeAll(reifiedTypeInliner.reifyInstructions(lambda.node.node))
|
||||||
}
|
}
|
||||||
rememberCapturedForDefaultLambda(lambda)
|
for (captured in lambda.capturedVars) {
|
||||||
|
val param = invocationParamBuilder.addCapturedParam(captured, captured.fieldName, false)
|
||||||
|
param.remapValue = StackValue.local(codegen.frameMap.enterTemp(param.type), param.type)
|
||||||
|
param.isSynthetic = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,8 +147,6 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun computeDefaultLambdaOffsets(): Set<Int>
|
|
||||||
|
|
||||||
private fun generateAndInsertFinallyBlocks(
|
private fun generateAndInsertFinallyBlocks(
|
||||||
intoNode: MethodNode,
|
intoNode: MethodNode,
|
||||||
insertPoints: List<MethodInliner.PointForExternalFinallyBlocks>,
|
insertPoints: List<MethodInliner.PointForExternalFinallyBlocks>,
|
||||||
@@ -235,10 +239,12 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
|||||||
NonInlineableArgumentForInlineableParameterCalledInSuspend
|
NonInlineableArgumentForInlineableParameterCalledInSuspend
|
||||||
ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER ->
|
ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER ->
|
||||||
NonInlineableArgumentForInlineableSuspendParameter
|
NonInlineableArgumentForInlineableSuspendParameter
|
||||||
|
ValueKind.DEFAULT_INLINE_PARAMETER ->
|
||||||
|
DefaultValueOfInlineParameter
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
kind === ValueKind.DEFAULT_PARAMETER ->
|
kind === ValueKind.DEFAULT_PARAMETER || kind === ValueKind.DEFAULT_INLINE_PARAMETER ->
|
||||||
codegen.frameMap.enterTemp(info.type) // the inline function will put the value into this slot
|
codegen.frameMap.enterTemp(info.type) // the inline function will put the value into this slot
|
||||||
stackValue.isLocalWithNoBoxing(jvmKotlinType) ->
|
stackValue.isLocalWithNoBoxing(jvmKotlinType) ->
|
||||||
info.remapValue = stackValue
|
info.remapValue = stackValue
|
||||||
@@ -257,14 +263,6 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rememberCapturedForDefaultLambda(defaultLambda: DefaultLambda) {
|
|
||||||
for (captured in defaultLambda.capturedVars) {
|
|
||||||
val info = invocationParamBuilder.addCapturedParam(captured, captured.fieldName, false)
|
|
||||||
info.remapValue = StackValue.local(codegen.frameMap.enterTemp(info.type), info.type)
|
|
||||||
info.isSynthetic = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processDefaultMaskOrMethodHandler(value: StackValue, kind: ValueKind) {
|
private fun processDefaultMaskOrMethodHandler(value: StackValue, kind: ValueKind) {
|
||||||
assert(value is StackValue.Constant) { "Additional default method argument should be constant, but $value" }
|
assert(value is StackValue.Constant) { "Additional default method argument should be constant, but $value" }
|
||||||
val constantValue = (value as StackValue.Constant).value
|
val constantValue = (value as StackValue.Constant).value
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ abstract class LambdaInfo : FunctionalArgument {
|
|||||||
|
|
||||||
object NonInlineableArgumentForInlineableParameterCalledInSuspend : FunctionalArgument
|
object NonInlineableArgumentForInlineableParameterCalledInSuspend : FunctionalArgument
|
||||||
object NonInlineableArgumentForInlineableSuspendParameter : FunctionalArgument
|
object NonInlineableArgumentForInlineableSuspendParameter : FunctionalArgument
|
||||||
|
object DefaultValueOfInlineParameter : FunctionalArgument
|
||||||
|
|
||||||
abstract class ExpressionLambda : LambdaInfo() {
|
abstract class ExpressionLambda : LambdaInfo() {
|
||||||
fun generateLambdaBody(sourceCompiler: SourceCompilerForInline) {
|
fun generateLambdaBody(sourceCompiler: SourceCompilerForInline) {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
|||||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
|
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
|
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
||||||
@@ -18,15 +17,6 @@ import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.*
|
import org.jetbrains.org.objectweb.asm.tree.analysis.*
|
||||||
|
|
||||||
fun parameterOffsets(isStatic: Boolean, valueParameters: List<JvmMethodParameterSignature>): Array<Int> {
|
|
||||||
var nextOffset = if (isStatic) 0 else 1
|
|
||||||
return Array(valueParameters.size) { index ->
|
|
||||||
nextOffset.also {
|
|
||||||
nextOffset += valueParameters[index].asmType.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MethodNode.remove(instructions: Sequence<AbstractInsnNode>) =
|
fun MethodNode.remove(instructions: Sequence<AbstractInsnNode>) =
|
||||||
instructions.forEach {
|
instructions.forEach {
|
||||||
this@remove.instructions.remove(it)
|
this@remove.instructions.remove(it)
|
||||||
|
|||||||
@@ -111,14 +111,6 @@ class PsiInlineCodegen(
|
|||||||
hiddenParameters.clear()
|
hiddenParameters.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*lambda or callable reference*/
|
|
||||||
private fun isInliningParameter(expression: KtExpression, valueParameterDescriptor: ValueParameterDescriptor): Boolean {
|
|
||||||
//TODO deparenthesize typed
|
|
||||||
val deparenthesized = KtPsiUtil.deparenthesize(expression)
|
|
||||||
|
|
||||||
return InlineUtil.isInlineParameter(valueParameterDescriptor) && isInlinableParameterExpression(deparenthesized)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun genValueAndPut(
|
override fun genValueAndPut(
|
||||||
valueParameterDescriptor: ValueParameterDescriptor?,
|
valueParameterDescriptor: ValueParameterDescriptor?,
|
||||||
argumentExpression: KtExpression,
|
argumentExpression: KtExpression,
|
||||||
@@ -130,7 +122,9 @@ class PsiInlineCodegen(
|
|||||||
"which cannot be declared in Kotlin and thus be inline: $codegen"
|
"which cannot be declared in Kotlin and thus be inline: $codegen"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInliningParameter(argumentExpression, valueParameterDescriptor)) {
|
val isInlineParameter = InlineUtil.isInlineParameter(valueParameterDescriptor)
|
||||||
|
//TODO deparenthesize typed
|
||||||
|
if (isInlineParameter && isInlinableParameterExpression(KtPsiUtil.deparenthesize(argumentExpression))) {
|
||||||
rememberClosure(argumentExpression, parameterType.type, valueParameterDescriptor)
|
rememberClosure(argumentExpression, parameterType.type, valueParameterDescriptor)
|
||||||
} else {
|
} else {
|
||||||
val value = codegen.gen(argumentExpression)
|
val value = codegen.gen(argumentExpression)
|
||||||
@@ -191,15 +185,6 @@ class PsiInlineCodegen(
|
|||||||
putCapturedToLocalVal(stackValue, activeLambda!!.capturedVars[paramIndex], stackValue.kotlinType)
|
putCapturedToLocalVal(stackValue, activeLambda!!.capturedVars[paramIndex], stackValue.kotlinType)
|
||||||
|
|
||||||
override fun reorderArgumentsIfNeeded(actualArgsWithDeclIndex: List<ArgumentAndDeclIndex>, valueParameterTypes: List<Type>) = Unit
|
override fun reorderArgumentsIfNeeded(actualArgsWithDeclIndex: List<ArgumentAndDeclIndex>, valueParameterTypes: List<Type>) = Unit
|
||||||
|
|
||||||
override fun computeDefaultLambdaOffsets(): Set<Int> {
|
|
||||||
val contextKind = (sourceCompiler as PsiSourceCompilerForInline).context.contextKind
|
|
||||||
return parameterOffsets(DescriptorAsmUtil.isStaticMethod(contextKind, functionDescriptor), jvmSignature.valueParameters)
|
|
||||||
.drop(jvmSignature.valueParameters.indexOfFirst { it.kind == JvmMethodParameterKind.VALUE })
|
|
||||||
.filterIndexedTo(mutableSetOf()) { index, _ ->
|
|
||||||
functionDescriptor.valueParameters[index].let { InlineUtil.isInlineParameter(it) && it.declaresDefaultValue() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val FunctionDescriptor.explicitParameters
|
private val FunctionDescriptor.explicitParameters
|
||||||
|
|||||||
+11
-21
@@ -70,7 +70,7 @@ class IrInlineCodegen(
|
|||||||
IrDeclarationOrigin.METHOD_HANDLER_IN_DEFAULT_FUNCTION -> ValueKind.METHOD_HANDLE_IN_DEFAULT
|
IrDeclarationOrigin.METHOD_HANDLER_IN_DEFAULT_FUNCTION -> ValueKind.METHOD_HANDLE_IN_DEFAULT
|
||||||
else -> when {
|
else -> when {
|
||||||
argumentExpression is IrContainerExpression && argumentExpression.origin == IrStatementOrigin.DEFAULT_VALUE ->
|
argumentExpression is IrContainerExpression && argumentExpression.origin == IrStatementOrigin.DEFAULT_VALUE ->
|
||||||
ValueKind.DEFAULT_PARAMETER
|
if (isInlineParameter) ValueKind.DEFAULT_INLINE_PARAMETER else ValueKind.DEFAULT_PARAMETER
|
||||||
// TODO ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND?
|
// TODO ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND?
|
||||||
isInlineParameter && irValueParameter.type.isSuspendFunctionTypeOrSubtype() ->
|
isInlineParameter && irValueParameter.type.isSuspendFunctionTypeOrSubtype() ->
|
||||||
ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER
|
ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER
|
||||||
@@ -78,20 +78,17 @@ class IrInlineCodegen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val onStack = when {
|
val onStack = when (kind) {
|
||||||
kind == ValueKind.METHOD_HANDLE_IN_DEFAULT -> StackValue.constant(null, AsmTypes.OBJECT_TYPE)
|
ValueKind.METHOD_HANDLE_IN_DEFAULT -> StackValue.constant(null, AsmTypes.OBJECT_TYPE)
|
||||||
kind == ValueKind.DEFAULT_MASK -> StackValue.constant((argumentExpression as IrConst<*>).value, Type.INT_TYPE)
|
ValueKind.DEFAULT_MASK -> StackValue.constant((argumentExpression as IrConst<*>).value, Type.INT_TYPE)
|
||||||
kind == ValueKind.DEFAULT_PARAMETER -> StackValue.createDefaultValue(parameterType)
|
ValueKind.DEFAULT_PARAMETER, ValueKind.DEFAULT_INLINE_PARAMETER -> StackValue.createDefaultValue(parameterType)
|
||||||
irValueParameter.index >= 0
|
// Here we replicate the old backend: reusing the locals for everything except extension receivers.
|
||||||
// Reuse an existing local if possible. NOTE: when stopping at a breakpoint placed
|
// TODO when stopping at a breakpoint placed in an inline function, arguments which reuse an existing
|
||||||
// in an inline function, arguments which reuse an existing local will not be visible
|
// local will not be visible in the debugger, so this needs to be reconsidered.
|
||||||
// in the debugger.
|
else -> if (irValueParameter.index >= 0)
|
||||||
-> codegen.genOrGetLocal(argumentExpression, parameterType, irValueParameter.type, blockInfo)
|
codegen.genOrGetLocal(argumentExpression, parameterType, irValueParameter.type, blockInfo)
|
||||||
else
|
else
|
||||||
// Do not reuse locals for receivers. While it's actually completely fine, the non-IR
|
codegen.gen(argumentExpression, parameterType, irValueParameter.type, blockInfo)
|
||||||
// backend does not do it for internal reasons, and here we replicate the debugging
|
|
||||||
// experience.
|
|
||||||
-> codegen.gen(argumentExpression, parameterType, irValueParameter.type, blockInfo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val expectedType = JvmKotlinType(parameterType, irValueParameter.type.toIrBasedKotlinType())
|
val expectedType = JvmKotlinType(parameterType, irValueParameter.type.toIrBasedKotlinType())
|
||||||
@@ -115,13 +112,6 @@ class IrInlineCodegen(
|
|||||||
override fun genCycleStub(text: String, codegen: ExpressionCodegen) {
|
override fun genCycleStub(text: String, codegen: ExpressionCodegen) {
|
||||||
generateStub(text, codegen)
|
generateStub(text, codegen)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun computeDefaultLambdaOffsets(): Set<Int> =
|
|
||||||
parameterOffsets(function.isStatic, jvmSignature.valueParameters)
|
|
||||||
.drop(if (function.extensionReceiverParameter != null) 1 else 0)
|
|
||||||
.filterIndexedTo(mutableSetOf()) { index, _ ->
|
|
||||||
function.valueParameters[index].let { it.defaultValue != null && it.isInlineParameter() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class IrExpressionLambdaImpl(
|
class IrExpressionLambdaImpl(
|
||||||
|
|||||||
Reference in New Issue
Block a user