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.jvm.JvmBackendContext
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.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
@@ -55,6 +58,12 @@ val jvmInlineClassPhase = makeIrFilePhase(
private class JvmInlineClassLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
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) {
irFile.transformChildrenVoid()
}
@@ -62,9 +71,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
override fun visitClassNew(declaration: IrClass): IrStatement {
// The arguments to the primary constructor are in scope in the initializers of IrFields.
declaration.primaryConstructor?.let {
context.inlineClassReplacements.getReplacementFunction(it)?.let { replacement ->
valueMap.putAll(replacement.valueParameterMap)
}
context.inlineClassReplacements.getReplacementFunction(it)?.let { replacement -> addBindingsFor(it, replacement) }
}
declaration.transformDeclarationsFlat { memberDeclaration ->
@@ -101,6 +108,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
return null
}
addBindingsFor(function, replacement)
return when (function) {
is IrSimpleFunction -> transformSimpleFunctionFlat(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> {
val worker = replacement.function
valueMap.putAll(replacement.valueParameterMap)
worker.valueParameters.forEach { it.transformChildrenVoid() }
worker.body = function.body?.transform(this, null)?.patchDeclarationParents(worker)
(worker as? IrAttributeContainer)?.copyAttributes(function)
private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach { it.transformChildrenVoid() }
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
(replacement as? IrAttributeContainer)?.copyAttributes(function)
// Don't create a wrapper for functions which are only used in an unboxed context
if (function.overriddenSymbols.isEmpty() || worker.dispatchReceiverParameter != null)
return listOf(worker)
if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null)
return listOf(replacement)
// Replace the function body with a wrapper
context.createIrBuilder(function.symbol, function.startOffset, function.endOffset).run {
val call = irCall(worker).apply {
val call = irCall(replacement).apply {
passTypeArgumentsFrom(function)
for (parameter in function.explicitParameters) {
val newParameter = replacement.valueParameterMap[parameter.symbol] ?: continue
for ((parameter, newParameter) in function.explicitParameters.zip(replacement.explicitParameters)) {
putArgument(newParameter, irGet(parameter))
}
}
@@ -132,19 +137,18 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
function.body = irExprBody(call)
}
return listOf(worker, function)
return listOf(replacement, function)
}
// Secondary constructors for boxed types get translated to static functions returning
// unboxed arguments. We remove the original constructor.
private fun transformConstructorFlat(constructor: IrConstructor, replacement: IrReplacementFunction): List<IrDeclaration> {
val worker = replacement.function
valueMap.putAll(replacement.valueParameterMap)
worker.valueParameters.forEach { it.transformChildrenVoid() }
worker.body = context.createIrBuilder(worker.symbol, worker.startOffset, worker.endOffset).irBlockBody(worker) {
private fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach { it.transformChildrenVoid() }
replacement.body = context.createIrBuilder(replacement.symbol, replacement.startOffset, replacement.endOffset).irBlockBody(
replacement
) {
val thisVar = irTemporaryVarDeclaration(
worker.returnType, nameHint = "\$this", isMutable = false
replacement.returnType, nameHint = "\$this", isMutable = false
)
valueMap[constructor.constructedClass.thisReceiver!!.symbol] = thisVar
@@ -189,13 +193,13 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
}
}, null)
.transform(this@JvmInlineClassLowering, null)
.patchDeclarationParents(worker)
.patchDeclarationParents(replacement)
}
+irReturn(irGet(thisVar))
}
return listOf(worker)
return listOf(replacement)
}
private fun typedArgumentList(function: IrFunction, expression: IrMemberAccessExpression) =
@@ -207,31 +211,27 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
private fun IrMemberAccessExpression.buildReplacement(
originalFunction: IrFunction,
original: IrMemberAccessExpression,
replacement: IrReplacementFunction
replacement: IrSimpleFunction
) {
copyTypeArgumentsFrom(original)
val valueParameterMap = originalFunction.explicitParameters.zip(replacement.explicitParameters).toMap()
for ((parameter, argument) in typedArgumentList(originalFunction, original)) {
if (argument == null) continue
val newParameter = replacement.valueParameterMap[parameter.symbol] ?: continue
putArgument(
replacement.function,
newParameter,
argument.transform(this@JvmInlineClassLowering, null)
)
val newParameter = valueParameterMap.getValue(parameter)
putArgument(replacement, newParameter, argument.transform(this@JvmInlineClassLowering, null))
}
}
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)
val function = replacement.function
return IrFunctionReferenceImpl(
expression.startOffset, expression.endOffset, expression.type,
function.symbol, function.typeParameters.size,
function.valueParameters.size, expression.reflectionTarget, expression.origin
).apply {
buildReplacement(expression.symbol.owner, expression, replacement)
buildReplacement(expression.symbol.owner, expression, function)
}.copyAttributes(expression)
}
@@ -240,7 +240,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
val replacement = context.inlineClassReplacements.getReplacementFunction(function)
?: return super.visitFunctionAccess(expression)
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
.irCall(replacement.function).apply {
.irCall(replacement).apply {
buildReplacement(function, expression, replacement)
}
}
@@ -278,7 +278,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
this@JvmInlineClassLowering.context.inlineClassReplacements.getSpecializedEqualsMethod(klass, context.irBuiltIns)
} else {
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 {
@@ -363,7 +363,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
override fun visitReturn(expression: IrReturn): IrExpression {
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
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)
)
}
@@ -433,7 +433,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
// Add a static bridge method to the primary constructor.
// 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)) {
val argument = function.valueParameters[0]
function.body = irExprBody(
@@ -107,7 +107,7 @@ private class JvmStringConcatenationLowering(val context: JvmBackendContext) : F
val toStringReplacement = backendContext.inlineClassReplacements.getReplacementFunction(toStringFunction)
?: return expression
return irCall(toStringReplacement.function).apply {
return irCall(toStringReplacement).apply {
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.IrFunctionImpl
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.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.util.constructedClass
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.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
import org.jetbrains.kotlin.storage.LockBasedStorageManager
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.
*/
@@ -46,7 +36,7 @@ class MemoizedInlineClassReplacements {
/**
* Get a replacement for a function or a constructor.
*/
val getReplacementFunction: (IrFunction) -> IrReplacementFunction? =
val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
storageManager.createMemoizedFunctionWithNullableValues {
when {
!it.hasMangledParameters ||
@@ -135,18 +125,12 @@ class MemoizedInlineClassReplacements {
}
}
private fun createMethodReplacement(function: IrFunction): IrReplacementFunction? {
require(function.dispatchReceiverParameter != null && function is IrSimpleFunction)
val overrides = function.overriddenSymbols.mapNotNull {
getReplacementFunction(it.owner)?.function?.symbol as? IrSimpleFunctionSymbol
}
if (function.origin == IrDeclarationOrigin.FAKE_OVERRIDE && overrides.isEmpty())
return null
val parameterMap = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
val replacement = buildReplacement(function) {
metadata = function.metadata
overriddenSymbols += overrides
private fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function) {
require(function.dispatchReceiverParameter != null && function is IrSimpleFunction)
overriddenSymbols = function.overriddenSymbols.map {
getReplacementFunction(it.owner)?.symbol ?: it
}
val newValueParameters = ArrayList<IrValueParameter>()
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
// equivalent, deep-copying the value here is unnecessary. See `JvmInlineClassLowering`.
newParameter.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
parameterMap[parameter.symbol] = newParameter
}
valueParameters = newValueParameters
}
return IrReplacementFunction(replacement, parameterMap)
}
private fun createStaticReplacement(function: IrFunction): IrReplacementFunction {
val parameterMap = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
val replacement = buildReplacement(function) {
// Generate metadata for the replacement function instead of the original.
if (function is IrFunctionBase<*>) {
metadata = function.metadata
function.metadata = null
}
valueParameters += function.explicitParameters.mapIndexed { index, parameter ->
private fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function) {
val newValueParameters = ArrayList<IrValueParameter>()
for ((index, parameter) in function.explicitParameters.withIndex()) {
val name = when (parameter) {
function.dispatchReceiverParameter -> Name.identifier("arg$index")
function.extensionReceiverParameter -> Name.identifier("\$this\$${function.name}")
@@ -191,16 +165,12 @@ class MemoizedInlineClassReplacements {
else -> parameter.origin
}
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.
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) =
buildFunWithDescriptorForInlining(function.descriptor) {
@@ -213,12 +183,10 @@ class MemoizedInlineClassReplacements {
}.apply {
parent = function.parent
annotations += function.annotations
if (function is IrConstructor) {
copyTypeParameters(function.constructedClass.typeParameters + function.typeParameters)
} else {
copyTypeParametersFrom(function)
correspondingPropertySymbol = function.safeAs<IrSimpleFunction>()?.correspondingPropertySymbol
}
copyTypeParameters(function.allTypeParameters)
correspondingPropertySymbol = function.safeAs<IrSimpleFunction>()?.correspondingPropertySymbol
metadata = function.metadata
function.safeAs<IrFunctionBase<*>>()?.metadata = null
body()
}
}
@@ -1,5 +1,6 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
abstract class GenericBase<T> {
abstract fun foo(x: T): T
@@ -21,4 +22,4 @@ fun box(): String {
if (Derived().foo("OK") != "OK") throw AssertionError()
return "OK"
}
}