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.
This commit is contained in:
Alexander Udalov
2020-08-13 12:26:10 +02:00
parent f9bd935ac6
commit fa45650fd0
3 changed files with 74 additions and 67 deletions
@@ -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 <init>.
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 <init>.
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)
@@ -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
@@ -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.