JVM IR: Remove IrReplacementFunction

This commit is contained in:
Steven Schäfer
2019-11-21 11:00:44 +01:00
committed by Georgy Bronnikov
parent 73b4f897b6
commit 66cbe3b1a8
4 changed files with 60 additions and 91 deletions
@@ -14,7 +14,10 @@ import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.* import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.inlineClassFieldName
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.isInlineClassFieldGetter
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.isPrimaryInlineClassConstructor
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
@@ -55,6 +58,12 @@ val jvmInlineClassPhase = makeIrFilePhase(
private class JvmInlineClassLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() { private class JvmInlineClassLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
private val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>() private val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
private fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
for ((param, newParam) in original.explicitParameters.zip(replacement.explicitParameters)) {
valueMap[param.symbol] = newParam
}
}
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid() irFile.transformChildrenVoid()
} }
@@ -62,9 +71,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
override fun visitClassNew(declaration: IrClass): IrStatement { override fun visitClassNew(declaration: IrClass): IrStatement {
// The arguments to the primary constructor are in scope in the initializers of IrFields. // The arguments to the primary constructor are in scope in the initializers of IrFields.
declaration.primaryConstructor?.let { declaration.primaryConstructor?.let {
context.inlineClassReplacements.getReplacementFunction(it)?.let { replacement -> context.inlineClassReplacements.getReplacementFunction(it)?.let { replacement -> addBindingsFor(it, replacement) }
valueMap.putAll(replacement.valueParameterMap)
}
} }
declaration.transformDeclarationsFlat { memberDeclaration -> declaration.transformDeclarationsFlat { memberDeclaration ->
@@ -101,6 +108,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
return null return null
} }
addBindingsFor(function, replacement)
return when (function) { return when (function) {
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement) is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
is IrConstructor -> transformConstructorFlat(function, replacement) is IrConstructor -> transformConstructorFlat(function, replacement)
@@ -108,23 +116,20 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
} }
} }
private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrReplacementFunction): List<IrDeclaration> { private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
val worker = replacement.function replacement.valueParameters.forEach { it.transformChildrenVoid() }
valueMap.putAll(replacement.valueParameterMap) replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
worker.valueParameters.forEach { it.transformChildrenVoid() } (replacement as? IrAttributeContainer)?.copyAttributes(function)
worker.body = function.body?.transform(this, null)?.patchDeclarationParents(worker)
(worker as? IrAttributeContainer)?.copyAttributes(function)
// Don't create a wrapper for functions which are only used in an unboxed context // Don't create a wrapper for functions which are only used in an unboxed context
if (function.overriddenSymbols.isEmpty() || worker.dispatchReceiverParameter != null) if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null)
return listOf(worker) return listOf(replacement)
// Replace the function body with a wrapper // Replace the function body with a wrapper
context.createIrBuilder(function.symbol, function.startOffset, function.endOffset).run { context.createIrBuilder(function.symbol, function.startOffset, function.endOffset).run {
val call = irCall(worker).apply { val call = irCall(replacement).apply {
passTypeArgumentsFrom(function) passTypeArgumentsFrom(function)
for (parameter in function.explicitParameters) { for ((parameter, newParameter) in function.explicitParameters.zip(replacement.explicitParameters)) {
val newParameter = replacement.valueParameterMap[parameter.symbol] ?: continue
putArgument(newParameter, irGet(parameter)) putArgument(newParameter, irGet(parameter))
} }
} }
@@ -132,19 +137,18 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
function.body = irExprBody(call) function.body = irExprBody(call)
} }
return listOf(worker, function) return listOf(replacement, function)
} }
// Secondary constructors for boxed types get translated to static functions returning // Secondary constructors for boxed types get translated to static functions returning
// unboxed arguments. We remove the original constructor. // unboxed arguments. We remove the original constructor.
private fun transformConstructorFlat(constructor: IrConstructor, replacement: IrReplacementFunction): List<IrDeclaration> { private fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
val worker = replacement.function replacement.valueParameters.forEach { it.transformChildrenVoid() }
replacement.body = context.createIrBuilder(replacement.symbol, replacement.startOffset, replacement.endOffset).irBlockBody(
valueMap.putAll(replacement.valueParameterMap) replacement
worker.valueParameters.forEach { it.transformChildrenVoid() } ) {
worker.body = context.createIrBuilder(worker.symbol, worker.startOffset, worker.endOffset).irBlockBody(worker) {
val thisVar = irTemporaryVarDeclaration( val thisVar = irTemporaryVarDeclaration(
worker.returnType, nameHint = "\$this", isMutable = false replacement.returnType, nameHint = "\$this", isMutable = false
) )
valueMap[constructor.constructedClass.thisReceiver!!.symbol] = thisVar valueMap[constructor.constructedClass.thisReceiver!!.symbol] = thisVar
@@ -189,13 +193,13 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
} }
}, null) }, null)
.transform(this@JvmInlineClassLowering, null) .transform(this@JvmInlineClassLowering, null)
.patchDeclarationParents(worker) .patchDeclarationParents(replacement)
} }
+irReturn(irGet(thisVar)) +irReturn(irGet(thisVar))
} }
return listOf(worker) return listOf(replacement)
} }
private fun typedArgumentList(function: IrFunction, expression: IrMemberAccessExpression) = private fun typedArgumentList(function: IrFunction, expression: IrMemberAccessExpression) =
@@ -207,31 +211,27 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
private fun IrMemberAccessExpression.buildReplacement( private fun IrMemberAccessExpression.buildReplacement(
originalFunction: IrFunction, originalFunction: IrFunction,
original: IrMemberAccessExpression, original: IrMemberAccessExpression,
replacement: IrReplacementFunction replacement: IrSimpleFunction
) { ) {
copyTypeArgumentsFrom(original) copyTypeArgumentsFrom(original)
val valueParameterMap = originalFunction.explicitParameters.zip(replacement.explicitParameters).toMap()
for ((parameter, argument) in typedArgumentList(originalFunction, original)) { for ((parameter, argument) in typedArgumentList(originalFunction, original)) {
if (argument == null) continue if (argument == null) continue
val newParameter = replacement.valueParameterMap[parameter.symbol] ?: continue val newParameter = valueParameterMap.getValue(parameter)
putArgument( putArgument(replacement, newParameter, argument.transform(this@JvmInlineClassLowering, null))
replacement.function,
newParameter,
argument.transform(this@JvmInlineClassLowering, null)
)
} }
} }
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
val replacement = context.inlineClassReplacements.getReplacementFunction(expression.symbol.owner) val function = context.inlineClassReplacements.getReplacementFunction(expression.symbol.owner)
?: return super.visitFunctionReference(expression) ?: return super.visitFunctionReference(expression)
val function = replacement.function
return IrFunctionReferenceImpl( return IrFunctionReferenceImpl(
expression.startOffset, expression.endOffset, expression.type, expression.startOffset, expression.endOffset, expression.type,
function.symbol, function.typeParameters.size, function.symbol, function.typeParameters.size,
function.valueParameters.size, expression.reflectionTarget, expression.origin function.valueParameters.size, expression.reflectionTarget, expression.origin
).apply { ).apply {
buildReplacement(expression.symbol.owner, expression, replacement) buildReplacement(expression.symbol.owner, expression, function)
}.copyAttributes(expression) }.copyAttributes(expression)
} }
@@ -240,7 +240,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
val replacement = context.inlineClassReplacements.getReplacementFunction(function) val replacement = context.inlineClassReplacements.getReplacementFunction(function)
?: return super.visitFunctionAccess(expression) ?: return super.visitFunctionAccess(expression)
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset) return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
.irCall(replacement.function).apply { .irCall(replacement).apply {
buildReplacement(function, expression, replacement) buildReplacement(function, expression, replacement)
} }
} }
@@ -278,7 +278,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
this@JvmInlineClassLowering.context.inlineClassReplacements.getSpecializedEqualsMethod(klass, context.irBuiltIns) this@JvmInlineClassLowering.context.inlineClassReplacements.getSpecializedEqualsMethod(klass, context.irBuiltIns)
} else { } else {
val equals = klass.functions.single { it.name.asString() == "equals" && it.overriddenSymbols.isNotEmpty() } val equals = klass.functions.single { it.name.asString() == "equals" && it.overriddenSymbols.isNotEmpty() }
this@JvmInlineClassLowering.context.inlineClassReplacements.getReplacementFunction(equals)!!.function this@JvmInlineClassLowering.context.inlineClassReplacements.getReplacementFunction(equals)!!
} }
return irCall(equalsMethod).apply { return irCall(equalsMethod).apply {
@@ -363,7 +363,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
override fun visitReturn(expression: IrReturn): IrExpression { override fun visitReturn(expression: IrReturn): IrExpression {
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target -> expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
context.inlineClassReplacements.getReplacementFunction(target)?.let { context.inlineClassReplacements.getReplacementFunction(target)?.let {
return context.createIrBuilder(it.function.symbol, expression.startOffset, expression.endOffset).irReturn( return context.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).irReturn(
expression.value.transform(this, null) expression.value.transform(this, null)
) )
} }
@@ -433,7 +433,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
// Add a static bridge method to the primary constructor. // Add a static bridge method to the primary constructor.
// This is a placeholder for null-checks and default arguments. // This is a placeholder for null-checks and default arguments.
val function = context.inlineClassReplacements.getReplacementFunction(irConstructor)!!.function val function = context.inlineClassReplacements.getReplacementFunction(irConstructor)!!
with(context.createIrBuilder(function.symbol)) { with(context.createIrBuilder(function.symbol)) {
val argument = function.valueParameters[0] val argument = function.valueParameters[0]
function.body = irExprBody( function.body = irExprBody(
@@ -107,7 +107,7 @@ private class JvmStringConcatenationLowering(val context: JvmBackendContext) : F
val toStringReplacement = backendContext.inlineClassReplacements.getReplacementFunction(toStringFunction) val toStringReplacement = backendContext.inlineClassReplacements.getReplacementFunction(toStringFunction)
?: return expression ?: return expression
return irCall(toStringReplacement.function).apply { return irCall(toStringReplacement).apply {
putValueArgument(0, expression) putValueArgument(0, expression)
} }
} }
@@ -19,24 +19,14 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionBase import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionBase
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.explicitParameters
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class IrReplacementFunction(
val function: IrFunction,
val valueParameterMap: Map<IrValueParameterSymbol, IrValueParameter>
)
/** /**
* Keeps track of replacement functions and inline class box/unbox functions. * Keeps track of replacement functions and inline class box/unbox functions.
*/ */
@@ -46,7 +36,7 @@ class MemoizedInlineClassReplacements {
/** /**
* Get a replacement for a function or a constructor. * Get a replacement for a function or a constructor.
*/ */
val getReplacementFunction: (IrFunction) -> IrReplacementFunction? = val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
storageManager.createMemoizedFunctionWithNullableValues { storageManager.createMemoizedFunctionWithNullableValues {
when { when {
!it.hasMangledParameters || !it.hasMangledParameters ||
@@ -135,18 +125,12 @@ class MemoizedInlineClassReplacements {
} }
} }
private fun createMethodReplacement(function: IrFunction): IrReplacementFunction? { private fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
require(function.dispatchReceiverParameter != null && function is IrSimpleFunction) buildReplacement(function) {
val overrides = function.overriddenSymbols.mapNotNull { require(function.dispatchReceiverParameter != null && function is IrSimpleFunction)
getReplacementFunction(it.owner)?.function?.symbol as? IrSimpleFunctionSymbol overriddenSymbols = function.overriddenSymbols.map {
} getReplacementFunction(it.owner)?.symbol ?: it
if (function.origin == IrDeclarationOrigin.FAKE_OVERRIDE && overrides.isEmpty()) }
return null
val parameterMap = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
val replacement = buildReplacement(function) {
metadata = function.metadata
overriddenSymbols += overrides
val newValueParameters = ArrayList<IrValueParameter>() val newValueParameters = ArrayList<IrValueParameter>()
for ((index, parameter) in function.explicitParameters.withIndex()) { for ((index, parameter) in function.explicitParameters.withIndex()) {
@@ -162,24 +146,14 @@ class MemoizedInlineClassReplacements {
// Assuming that constructors and non-override functions are always replaced with the unboxed // Assuming that constructors and non-override functions are always replaced with the unboxed
// equivalent, deep-copying the value here is unnecessary. See `JvmInlineClassLowering`. // equivalent, deep-copying the value here is unnecessary. See `JvmInlineClassLowering`.
newParameter.defaultValue = parameter.defaultValue?.patchDeclarationParents(this) newParameter.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
parameterMap[parameter.symbol] = newParameter
} }
valueParameters = newValueParameters valueParameters = newValueParameters
} }
return IrReplacementFunction(replacement, parameterMap)
}
private fun createStaticReplacement(function: IrFunction): IrReplacementFunction { private fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
val parameterMap = mutableMapOf<IrValueParameterSymbol, IrValueParameter>() buildReplacement(function) {
val replacement = buildReplacement(function) { val newValueParameters = ArrayList<IrValueParameter>()
// Generate metadata for the replacement function instead of the original. for ((index, parameter) in function.explicitParameters.withIndex()) {
if (function is IrFunctionBase<*>) {
metadata = function.metadata
function.metadata = null
}
valueParameters += function.explicitParameters.mapIndexed { index, parameter ->
val name = when (parameter) { val name = when (parameter) {
function.dispatchReceiverParameter -> Name.identifier("arg$index") function.dispatchReceiverParameter -> Name.identifier("arg$index")
function.extensionReceiverParameter -> Name.identifier("\$this\$${function.name}") function.extensionReceiverParameter -> Name.identifier("\$this\$${function.name}")
@@ -191,16 +165,12 @@ class MemoizedInlineClassReplacements {
else -> parameter.origin else -> parameter.origin
} }
val newParameter = parameter.copyTo(this, index = index, name = name, defaultValue = null, origin = parameterOrigin) val newParameter = parameter.copyTo(this, index = index, name = name, defaultValue = null, origin = parameterOrigin)
valueParameters += newParameter newValueParameters += newParameter
// See comment next to a similar line above. // See comment next to a similar line above.
newParameter.defaultValue = parameter.defaultValue?.patchDeclarationParents(this) newParameter.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
parameterMap[parameter.symbol] = newParameter
newParameter
} }
valueParameters = newValueParameters
} }
return IrReplacementFunction(replacement, parameterMap)
}
private fun buildReplacement(function: IrFunction, body: IrFunctionImpl.() -> Unit) = private fun buildReplacement(function: IrFunction, body: IrFunctionImpl.() -> Unit) =
buildFunWithDescriptorForInlining(function.descriptor) { buildFunWithDescriptorForInlining(function.descriptor) {
@@ -213,12 +183,10 @@ class MemoizedInlineClassReplacements {
}.apply { }.apply {
parent = function.parent parent = function.parent
annotations += function.annotations annotations += function.annotations
if (function is IrConstructor) { copyTypeParameters(function.allTypeParameters)
copyTypeParameters(function.constructedClass.typeParameters + function.typeParameters) correspondingPropertySymbol = function.safeAs<IrSimpleFunction>()?.correspondingPropertySymbol
} else { metadata = function.metadata
copyTypeParametersFrom(function) function.safeAs<IrFunctionBase<*>>()?.metadata = null
correspondingPropertySymbol = function.safeAs<IrSimpleFunction>()?.correspondingPropertySymbol
}
body() body()
} }
} }
@@ -1,5 +1,6 @@
// !LANGUAGE: +InlineClasses // !LANGUAGE: +InlineClasses
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
abstract class GenericBase<T> { abstract class GenericBase<T> {
abstract fun foo(x: T): T abstract fun foo(x: T): T
@@ -21,4 +22,4 @@ fun box(): String {
if (Derived().foo("OK") != "OK") throw AssertionError() if (Derived().foo("OK") != "OK") throw AssertionError()
return "OK" return "OK"
} }