From fa45650fd0b6a5d4279781feb7d06f958610fb3d Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 13 Aug 2020 12:26:10 +0200 Subject: [PATCH] JVM IR: specialize ExpressionCodegen.visitFunctionAccess for constructors This makes the code a bit clearer, and gets rid of some extra type mapper invocations when generating constructor calls. --- .../backend/jvm/codegen/ExpressionCodegen.kt | 112 +++++++++++------- .../jvm/codegen/MethodSignatureMapper.kt | 25 +--- .../backend/jvm/codegen/PromisedValue.kt | 4 - 3 files changed, 74 insertions(+), 67 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index fc5dabad893..7ad80af3b49 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -52,6 +52,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.keysToMap import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import java.util.* @@ -386,7 +387,7 @@ class ExpressionCodegen( override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) = visitStatementContainer(expression, data) - override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue { + override fun visitCall(expression: IrCall, data: BlockInfo): PromisedValue { classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol) ?.invoke(expression, this, data)?.let { return it } @@ -394,47 +395,14 @@ class ExpressionCodegen( require(callee.parent is IrClass) { "Unhandled intrinsic in ExpressionCodegen: ${callee.render()}" } val callable = methodSignatureMapper.mapToCallableMethod(irFunction, expression) val callGenerator = getOrCreateCallGenerator(expression, data, callable.signature) - val asmType = if (expression is IrConstructorCall) typeMapper.mapTypeAsDeclaration(expression.type) else expression.asmType val isSuspensionPoint = expression.isSuspensionPoint() if (isSuspensionPoint != SuspensionPointKind.NEVER) { addInlineMarker(mv, isStartNotEnd = true) } - when { - expression is IrConstructorCall -> { - closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let { - if (it.wereUsedReifiedParameters()) { - putNeedClassReificationMarker(v) - propagateChildReifiedTypeParametersUsages(it) - } - } - - // IR constructors have no receiver and return the new instance, but on JVM they are void-returning - // instance methods named . - markLineNumber(expression) - mv.anew(asmType) - mv.dup() - } - expression is IrDelegatingConstructorCall -> { - expression.markLineNumber(startOffset = true) - - // In this case the receiver is `this` (not specified in IR) and the return value is discarded anyway. - mv.load(0, OBJECT_TYPE) - - for (argumentIndex in 0 until expression.typeArgumentsCount) { - val classifier = expression.getTypeArgument(argumentIndex)?.classifierOrNull - if (classifier is IrTypeParameterSymbol && classifier.owner.isReified) { - consumeReifiedOperationMarker(classifier) - } - } - } - expression.symbol.owner is IrConstructor -> - throw AssertionError("IrCall to IrConstructor: ${expression.javaClass.simpleName}") - } - expression.dispatchReceiver?.let { receiver -> - val type = if ((expression as? IrCall)?.superQualifierSymbol != null) receiver.asmType else callable.owner + val type = if (expression.superQualifierSymbol != null) receiver.asmType else callable.owner callGenerator.genValueAndPut(callee.dispatchReceiverParameter!!, receiver, type, this, data) } @@ -458,14 +426,10 @@ class ExpressionCodegen( addSuspendMarker(mv, isStartNotEnd = true, isSuspensionPoint == SuspensionPointKind.NOT_INLINE) } - if (irFunction.isInvokeSuspendOfContinuation()) { + val generatorForActualCall = // Do not inline callee to continuation, instead, call it - with(callable) { - mv.visitMethodInsn(invokeOpcode, owner.internalName, asmMethod.name, asmMethod.descriptor, isInterfaceMethod) - } - } else { - callGenerator.genCall(callable, this, expression) - } + if (irFunction.isInvokeSuspendOfContinuation()) IrCallGenerator.DefaultCallGenerator else callGenerator + generatorForActualCall.genCall(callable, this, expression) if (isSuspensionPoint != SuspensionPointKind.NEVER) { addSuspendMarker(mv, isStartNotEnd = false, isSuspensionPoint == SuspensionPointKind.NOT_INLINE) @@ -473,8 +437,6 @@ class ExpressionCodegen( } return when { - expression is IrConstructorCall -> - MaterialValue(this, asmType, expression.type) (expression.type.isNothing() || expression.type.isUnit()) && irFunction.shouldContainSuspendMarkers() -> { // NewInference allows casting `() -> T` to `() -> Unit`. A CHECKCAST here will fail. // Also, if the callee is a suspend function with a suspending tail call, the next `resumeWith` @@ -511,6 +473,68 @@ class ExpressionCodegen( else -> SuspensionPointKind.ALWAYS } + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: BlockInfo): PromisedValue { + val callee = expression.symbol.owner + val owner = typeMapper.mapClass(callee.constructedClass) + val signature = methodSignatureMapper.mapSignatureSkipGeneric(callee) + + markLineNumber(expression) + + // In this case the receiver is `this` (not specified in IR) and the return value is discarded anyway. + mv.load(0, OBJECT_TYPE) + + for (argumentIndex in 0 until expression.typeArgumentsCount) { + val classifier = expression.getTypeArgument(argumentIndex)?.classifierOrNull + if (classifier is IrTypeParameterSymbol && classifier.owner.isReified) { + consumeReifiedOperationMarker(classifier) + } + } + + generateConstructorArguments(expression, signature, data) + markLineNumber(expression) + + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner.internalName, signature.asmMethod.name, signature.asmMethod.descriptor, false) + + return unitValue + } + + override fun visitConstructorCall(expression: IrConstructorCall, data: BlockInfo): PromisedValue { + classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol) + ?.invoke(expression, this, data)?.let { return it } + + val callee = expression.symbol.owner + val owner = typeMapper.mapClass(callee.constructedClass) + val signature = methodSignatureMapper.mapSignatureSkipGeneric(callee) + + closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let { + if (it.wereUsedReifiedParameters()) { + putNeedClassReificationMarker(v) + propagateChildReifiedTypeParametersUsages(it) + } + } + + // IR constructors have no receiver and return the new instance, but on JVM they are void-returning + // instance methods named . + markLineNumber(expression) + mv.anew(owner) + mv.dup() + + generateConstructorArguments(expression, signature, data) + markLineNumber(expression) + + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner.internalName, signature.asmMethod.name, signature.asmMethod.descriptor, false) + + return MaterialValue(this, owner, expression.type) + } + + private fun generateConstructorArguments(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, data: BlockInfo) { + expression.symbol.owner.valueParameters.forEachIndexed { i, irParameter -> + val arg = expression.getValueArgument(i) + ?: error("Null argument in ExpressionCodegen for parameter ${irParameter.render()}") + gen(arg, signature.valueParameters[i].asmType, irParameter.type, data) + } + } + override fun visitVariable(declaration: IrVariable, data: BlockInfo): PromisedValue { val varType = typeMapper.mapType(declaration) val index = frameMap.enter(declaration.symbol, varType) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt index 8cc4cc0a3fa..afbb2de025e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt @@ -33,9 +33,7 @@ import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunctionBase import org.jetbrains.kotlin.ir.descriptors.IrBasedSimpleFunctionDescriptor import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType -import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrConst -import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.* @@ -300,26 +298,15 @@ class MethodSignatureMapper(private val context: JvmBackendContext) { if (valueArgumentsCount > 0) (getValueArgument(0) as? IrConst<*>)?.value as? Boolean ?: true else null } - private fun IrFunctionAccessExpression.computeCalleeParent(): IrClass { - if (this is IrCall) { - superQualifierSymbol?.let { return it.owner } - } - return dispatchReceiver?.type?.classOrNull?.owner - ?: symbol.owner.parentAsClass // Static call or type parameter - } - - fun mapToCallableMethod(caller: IrFunction, expression: IrFunctionAccessExpression): IrCallableMethod { + fun mapToCallableMethod(caller: IrFunction, expression: IrCall): IrCallableMethod { val callee = expression.symbol.owner - val calleeParent = expression.computeCalleeParent() + val calleeParent = expression.superQualifierSymbol?.owner + ?: expression.dispatchReceiver?.type?.classOrNull?.owner + ?: callee.parentAsClass // Static call or type parameter val owner = typeMapper.mapOwner(calleeParent) - if (callee !is IrSimpleFunction) { - check(callee is IrConstructor) { "Function must be a simple function or a constructor: ${callee.render()}" } - return IrCallableMethod(owner, Opcodes.INVOKESPECIAL, mapSignatureSkipGeneric(callee), false) - } - val isInterface = calleeParent.isJvmInterface - val isSuperCall = (expression as? IrCall)?.superQualifierSymbol != null + val isSuperCall = expression.superQualifierSymbol != null val invokeOpcode = when { callee.dispatchReceiverParameter == null -> Opcodes.INVOKESTATIC diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt index 3b86a8e1354..36e5eeebc7c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt @@ -14,7 +14,6 @@ import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.resolve.jvm.AsmTypes -import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -66,9 +65,6 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val val typeMapper: IrTypeMapper get() = codegen.typeMapper - - val kotlinType: KotlinType - get() = irType.toKotlinType() } // A value that *has* been fully constructed.