JVM_IR: move ArrayConstructor below function reference phases

This allows taking function references to inline array constructors.

Also, redundant classes are no longer generated when function references
are passed as arguments to the array constructors.

 #KT-46426 Fixed
This commit is contained in:
pyos
2021-05-04 16:13:39 +02:00
committed by TeamCityServer
parent 776920f77d
commit 9f53d70109
15 changed files with 168 additions and 45 deletions
@@ -2440,6 +2440,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/callableReference/genericLocalClassConstructorReference.kt");
}
@Test
@TestMetadata("inlineArrayConstructors.kt")
public void testInlineArrayConstructors() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/inlineArrayConstructors.kt");
}
@Test
@TestMetadata("javaField.kt")
public void testJavaField() throws Exception {
@@ -2835,6 +2841,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/callableReference/bound/array.kt");
}
@Test
@TestMetadata("arrayConstructorArgument.kt")
public void testArrayConstructorArgument() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayConstructorArgument.kt");
}
@Test
@TestMetadata("arrayGetIntrinsic.kt")
public void testArrayGetIntrinsic() throws Exception {
@@ -7,38 +7,57 @@ package org.jetbrains.kotlin.backend.common.ir
import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.IrStatementsBuilder
import org.jetbrains.kotlin.ir.builders.irTemporary
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnableBlockImpl
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.explicitParameters
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.isVararg
import org.jetbrains.kotlin.ir.util.statements
import org.jetbrains.kotlin.util.OperatorNameConventions
sealed class IrInlinable
class IrInvokable(val invokable: IrValueDeclaration) : IrInlinable()
class IrInlinableLambda(val function: IrSimpleFunction, val boundReceiver: IrValueDeclaration?) : IrInlinable()
// Return the underlying function for a lambda argument without bound or default parameters or varargs.
fun IrExpression.asSimpleLambda(): IrSimpleFunction? {
private fun IrExpression.asInlinableLambda(builder: IrStatementsBuilder<*>): IrInlinableLambda? {
if (this is IrFunctionExpression) {
if (function.valueParameters.any { it.isVararg || it.defaultValue != null })
return null
return function
return IrInlinableLambda(function, null)
}
// A lambda is represented as a block with a function declaration and a reference to it.
// Inlinable function references are also a kind of lambda; bound receivers are represented as extension receivers.
if (this !is IrBlock || statements.size != 2)
return null
val (function, reference) = statements
if (function !is IrSimpleFunction || reference !is IrFunctionReference || function.symbol != reference.symbol)
return null
if (function.dispatchReceiverParameter != null)
return null
if ((0 until reference.valueArgumentsCount).any { reference.getValueArgument(it) != null })
return null
if (function.valueParameters.any { it.isVararg || it.defaultValue != null })
return null
return function
return IrInlinableLambda(function, reference.extensionReceiver?.let { builder.irTemporary(it) })
}
fun IrExpression.asInlinable(builder: IrStatementsBuilder<*>): IrInlinable =
asInlinableLambda(builder) ?: IrInvokable(builder.irTemporary(this))
private fun createParameterMapping(source: IrFunction, target: IrFunction): Map<IrValueParameter, IrValueParameter> {
val sourceParameters = listOfNotNull(source.dispatchReceiverParameter, source.extensionReceiverParameter) + source.valueParameters
val targetParameters = listOfNotNull(target.dispatchReceiverParameter, target.extensionReceiverParameter) + target.valueParameters
val sourceParameters = source.explicitParameters
val targetParameters = target.explicitParameters
assert(sourceParameters.size == targetParameters.size)
return sourceParameters.zip(targetParameters).toMap()
}
@@ -79,7 +98,26 @@ private fun IrBody.move(
// TODO use a generic inliner (e.g. JS/Native's FunctionInlining.Inliner)
// Inline simple function calls without type parameters, default parameters, or varargs.
fun IrFunction.inline(target: IrDeclarationParent, arguments: List<IrValueDeclaration> = listOf()): IrReturnableBlock =
private fun IrFunction.inline(target: IrDeclarationParent, arguments: List<IrValueDeclaration> = listOf()): IrReturnableBlock =
IrReturnableBlockImpl(startOffset, endOffset, returnType, IrReturnableBlockSymbolImpl(), null, symbol).apply {
statements += body!!.move(this@inline, target, symbol, valueParameters.zip(arguments).toMap()).statements
statements += body!!.move(this@inline, target, symbol, explicitParameters.zip(arguments).toMap()).statements
}
fun IrInlinable.inline(target: IrDeclarationParent, arguments: List<IrValueDeclaration> = listOf()): IrExpression =
when (this) {
is IrInlinableLambda ->
function.inline(target, listOfNotNull(boundReceiver) + arguments)
is IrInvokable -> {
val invoke = invokable.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INVOKE }
IrCallImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, invoke.returnType, invoke.symbol,
typeArgumentsCount = 0, valueArgumentsCount = arguments.size,
).apply {
dispatchReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, invokable.symbol)
for ((index, argument) in arguments.withIndex()) {
putValueArgument(index, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, argument.symbol))
}
}
}
}
@@ -8,12 +8,11 @@ package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.asSimpleLambda
import org.jetbrains.kotlin.backend.common.ir.asInlinable
import org.jetbrains.kotlin.backend.common.ir.inline
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
@@ -49,15 +48,6 @@ open class ArrayConstructorTransformer(
}
}
private fun IrExpression.asSingleArgumentLambda(): IrSimpleFunction? {
val function = asSimpleLambda() ?: return null
// Only match the one that has exactly one non-vararg argument, as the code below
// does not handle defaults or varargs.
if (function.valueParameters.size != 1)
return null
return function
}
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
val sizeConstructor = arrayInlineToSizeConstructor(expression.symbol.owner)
?: return super.visitConstructorCall(expression)
@@ -82,9 +72,7 @@ open class ArrayConstructorTransformer(
putValueArgument(0, irGet(sizeVar))
})
val lambda = invokable.asSingleArgumentLambda()
val invoke = invokable.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INVOKE }
val invokableVar = if (lambda == null) createTmpVariable(invokable) else null
val generator = invokable.asInlinable(this)
+irWhile().apply {
condition = irCall(context.irBuiltIns.lessFunByOperandType[index.type.classifierOrFail]!!).apply {
putValueArgument(0, irGet(index))
@@ -92,13 +80,10 @@ open class ArrayConstructorTransformer(
}
body = irBlock {
val tempIndex = createTmpVariable(irGet(index))
val value =
lambda?.run { inline(parent, listOf(tempIndex)).patchDeclarationParents(scope.getLocalDeclarationParent()) }
?: irCallOp(invoke.symbol, invoke.returnType, irGet(invokableVar!!), irGet(tempIndex))
+irCall(result.type.getClass()!!.functions.single { it.name == OperatorNameConventions.SET }).apply {
dispatchReceiver = irGet(result)
putValueArgument(0, irGet(tempIndex))
putValueArgument(1, value)
putValueArgument(1, generator.inline(parent, listOf(tempIndex)).patchDeclarationParents(scope.getLocalDeclarationParent()))
}
val inc = index.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INC }
+irSet(index.symbol, irCallOp(inc.symbol, index.type, irGet(index)))
@@ -326,7 +326,6 @@ private val jvmFilePhases = listOf(
annotationPhase,
polymorphicSignaturePhase,
varargPhase,
arrayConstructorPhase,
lateinitNullableFieldsPhase,
lateinitDeclarationLoweringPhase,
@@ -336,6 +335,7 @@ private val jvmFilePhases = listOf(
functionReferencePhase,
suspendLambdaPhase,
propertyReferencePhase,
arrayConstructorPhase,
constPhase1,
// TODO: merge the next three phases together, as visitors behave incorrectly between them
// (backing fields moved out of companion objects are reachable by two paths):
@@ -263,4 +263,10 @@ fun IrStatementOrigin?.isInlineIrExpression() =
isLambda || this == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE || this == IrStatementOrigin.SUSPEND_CONVERSION
fun IrFunction.isInlineFunctionCall(context: JvmBackendContext) =
(!context.state.isInlineDisabled || typeParameters.any { it.isReified }) && isInline
(!context.state.isInlineDisabled || typeParameters.any { it.isReified }) && (isInline || isInlineArrayConstructor(context))
// Constructors can't be marked as inline in metadata, hence this hack.
private fun IrFunction.isInlineArrayConstructor(context: JvmBackendContext): Boolean =
this is IrConstructor && valueParameters.size == 2 && constructedClass.symbol.let {
it == context.irBuiltIns.arrayClass || it in context.irBuiltIns.primitiveArrays
}
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.asSimpleLambda
import org.jetbrains.kotlin.backend.common.ir.asInlinable
import org.jetbrains.kotlin.backend.common.ir.inline
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
@@ -27,12 +27,10 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
internal val assertionPhase = makeIrFilePhase(
::AssertionLowering,
@@ -102,23 +100,12 @@ private class AssertionLowering(private val context: JvmBackendContext) :
private fun IrBuilderWithScope.checkAssertion(assertCondition: IrExpression, lambdaArgument: IrExpression?) =
irBlock {
val lambda = lambdaArgument?.asSimpleLambda()
val invokeVar = if (lambda == null && lambdaArgument != null) irTemporary(lambdaArgument) else null
val generator = lambdaArgument?.asInlinable(this)
val constructor = this@AssertionLowering.context.ir.symbols.assertionErrorConstructor
val throwError = irThrow(irCall(constructor).apply {
putValueArgument(
0,
when {
lambda != null -> lambda.inline(parent)
lambdaArgument != null -> {
val invoke =
lambdaArgument.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INVOKE }
irCallOp(invoke.symbol, invoke.returnType, irGet(invokeVar!!))
}
else -> irString("Assertion failed")
}
)
val message = generator?.inline(parent)?.patchDeclarationParents(scope.getLocalDeclarationParent())
?: irString("Assertion failed")
putValueArgument(0, message)
})
+irIfThen(irNot(assertCondition), throwError)
}
@@ -0,0 +1,10 @@
// WITH_RUNTIME
class C(val x: String) {
fun foo(i: Int): Char = x[i]
}
fun box(): String {
val array = CharArray(2, C("OK")::foo)
return array[0].toString() + array[1].toString()
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
// IGNORE_BACKEND: JS_IR
fun createArray(ctor: (Int, (Int) -> Char) -> CharArray) =
ctor(1) { 'O' }
inline fun createArrayInline(ctor: (Int, (Int) -> Char) -> CharArray) =
ctor(1) { 'K' }
fun box(): String =
createArray(::CharArray)[0].toString() + createArrayInline(::CharArray)[0].toString()
@@ -2440,6 +2440,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/callableReference/genericLocalClassConstructorReference.kt");
}
@Test
@TestMetadata("inlineArrayConstructors.kt")
public void testInlineArrayConstructors() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/inlineArrayConstructors.kt");
}
@Test
@TestMetadata("javaField.kt")
public void testJavaField() throws Exception {
@@ -2835,6 +2841,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/callableReference/bound/array.kt");
}
@Test
@TestMetadata("arrayConstructorArgument.kt")
public void testArrayConstructorArgument() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayConstructorArgument.kt");
}
@Test
@TestMetadata("arrayGetIntrinsic.kt")
public void testArrayGetIntrinsic() throws Exception {
@@ -2440,6 +2440,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/callableReference/genericLocalClassConstructorReference.kt");
}
@Test
@TestMetadata("inlineArrayConstructors.kt")
public void testInlineArrayConstructors() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/inlineArrayConstructors.kt");
}
@Test
@TestMetadata("javaField.kt")
public void testJavaField() throws Exception {
@@ -2835,6 +2841,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/callableReference/bound/array.kt");
}
@Test
@TestMetadata("arrayConstructorArgument.kt")
public void testArrayConstructorArgument() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayConstructorArgument.kt");
}
@Test
@TestMetadata("arrayGetIntrinsic.kt")
public void testArrayGetIntrinsic() throws Exception {
@@ -2165,6 +2165,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/callableReference/genericLocalClassConstructorReference.kt");
}
@TestMetadata("inlineArrayConstructors.kt")
public void testInlineArrayConstructors() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/inlineArrayConstructors.kt");
}
@TestMetadata("javaField.kt")
public void testJavaField() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/javaField.kt");
@@ -2493,6 +2498,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/callableReference/bound/array.kt");
}
@TestMetadata("arrayConstructorArgument.kt")
public void testArrayConstructorArgument() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayConstructorArgument.kt");
}
@TestMetadata("arrayGetIntrinsic.kt")
public void testArrayGetIntrinsic() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayGetIntrinsic.kt");
@@ -1465,6 +1465,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/callableReference/genericLocalClassConstructorReference.kt");
}
@TestMetadata("inlineArrayConstructors.kt")
public void testInlineArrayConstructors() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/inlineArrayConstructors.kt");
}
@TestMetadata("kt21014.kt")
public void testKt21014() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/kt21014.kt");
@@ -1733,6 +1738,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/callableReference/bound/array.kt");
}
@TestMetadata("arrayConstructorArgument.kt")
public void testArrayConstructorArgument() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayConstructorArgument.kt");
}
@TestMetadata("arrayGetIntrinsic.kt")
public void testArrayGetIntrinsic() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayGetIntrinsic.kt");
@@ -1465,6 +1465,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/callableReference/genericLocalClassConstructorReference.kt");
}
@TestMetadata("inlineArrayConstructors.kt")
public void testInlineArrayConstructors() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/inlineArrayConstructors.kt");
}
@TestMetadata("kt21014.kt")
public void testKt21014() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/kt21014.kt");
@@ -1733,6 +1738,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/callableReference/bound/array.kt");
}
@TestMetadata("arrayConstructorArgument.kt")
public void testArrayConstructorArgument() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayConstructorArgument.kt");
}
@TestMetadata("arrayGetIntrinsic.kt")
public void testArrayGetIntrinsic() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayGetIntrinsic.kt");
@@ -1465,6 +1465,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/callableReference/genericLocalClassConstructorReference.kt");
}
@TestMetadata("inlineArrayConstructors.kt")
public void testInlineArrayConstructors() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/inlineArrayConstructors.kt");
}
@TestMetadata("kt21014.kt")
public void testKt21014() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/kt21014.kt");
@@ -1733,6 +1738,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/callableReference/bound/array.kt");
}
@TestMetadata("arrayConstructorArgument.kt")
public void testArrayConstructorArgument() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayConstructorArgument.kt");
}
@TestMetadata("arrayGetIntrinsic.kt")
public void testArrayGetIntrinsic() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayGetIntrinsic.kt");
@@ -1265,6 +1265,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@TestMetadata("inlineArrayConstructors.kt")
public void testInlineArrayConstructors() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/inlineArrayConstructors.kt");
}
@TestMetadata("kt37604.kt")
public void testKt37604() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/kt37604.kt");
@@ -1358,6 +1363,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@TestMetadata("arrayConstructorArgument.kt")
public void testArrayConstructorArgument() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/arrayConstructorArgument.kt");
}
@TestMetadata("captureVarInInitBlock.kt")
public void testCaptureVarInInitBlock() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/captureVarInInitBlock.kt");