From a4c693ec29afe588b22478e4da0563b44f8c2878 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Wed, 13 Mar 2019 18:19:22 +0300 Subject: [PATCH] [JS IR BE] updated inliner to the most recent version FunctionInlining.kt commit 364b7ae72a49c2a1716c83c1bab0866dfe46fa25 : https://github.com/JetBrains/kotlin-native/blob/364b7ae72a49c2a1716c83c1bab0866dfe46fa25/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt --- .../js/lower/inline/FunctionInlining.kt | 227 ++++++++---------- .../inlineSuspendLambdaNonLocalReturn.kt | 2 +- .../callableReference/ordinaryParameter.kt | 2 +- .../callableReference/suspendOfOrdinary.kt | 2 +- 4 files changed, 105 insertions(+), 128 deletions(-) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/FunctionInlining.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/FunctionInlining.kt index b6d151c0d10..4e7134ba436 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/FunctionInlining.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/FunctionInlining.kt @@ -8,18 +8,16 @@ package org.jetbrains.kotlin.ir.backend.js.lower.inline import org.jetbrains.kotlin.backend.common.* -import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters +import org.jetbrains.kotlin.backend.common.ir.createTemporaryVariableWithWrappedDescriptor +import org.jetbrains.kotlin.backend.common.lower.CoroutineIntrinsicLambdaOrigin import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.common.serialization.fqNameSafe +import org.jetbrains.kotlin.backend.common.serialization.hasAnnotation import org.jetbrains.kotlin.config.languageVersionSettings -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.ValueDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.lower.ArrayConstructorTransformer -import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.declarations.* @@ -35,8 +33,7 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.inline.InlineUtil +import org.jetbrains.kotlin.util.OperatorNameConventions typealias Context = JsIrBackendContext @@ -53,9 +50,9 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW override fun visitCall(expression: IrCall): IrExpression { val callSite = arrayConstructorTransformer.transformCall(super.visitCall(expression) as IrCall) - val functionDescriptor = callSite.descriptor - if (!functionDescriptor.needsInlining) return callSite // This call does not need inlining. + if (!callSite.symbol.owner.needsInlining) + return callSite val languageVersionSettings = context.configuration.languageVersionSettings when { @@ -76,19 +73,12 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW val parent = allScopes.map { it.irElement }.filterIsInstance().lastOrNull() val inliner = Inliner(callSite, callee, currentScope!!, parent, context) - // Create inliner for this scope. - return inliner.inline() // Return newly created IrInlineBody instead of IrCall. + return inliner.inline() } - //-------------------------------------------------------------------------// - private fun getFunctionDeclaration(symbol: IrFunctionSymbol): IrFunction { - val descriptor = symbol.descriptor.original -// val originalDescriptor = functionDescriptor.resolveFakeOverride().original - val languageVersionSettings = context.configuration.languageVersionSettings - // TODO: Remove these hacks when coroutine intrinsics are fixed. return when { // descriptor.isBuiltInIntercepted(languageVersionSettings) -> @@ -104,20 +94,17 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW } } - - // TODO: should we keep this at all? private val inlineConstructor = FqName("kotlin.native.internal.InlineConstructor") - private val FunctionDescriptor.isInlineConstructor get() = annotations.hasAnnotation(inlineConstructor) -//-----------------------------------------------------------------------------// + private val IrFunction.isInlineConstructor get() = annotations.hasAnnotation(inlineConstructor) - private inner class Inliner( - val callSite: IrCall, - val callee: IrFunction, - val currentScope: ScopeWithIr, - val parent: IrDeclarationParent?, - val context: Context - ) { + private val IrFunction.needsInlining get() = isInlineConstructor || (this.isInline && !this.isExternal) + + private inner class Inliner(val callSite: IrCall, + val callee: IrFunction, + val currentScope: ScopeWithIr, + val parent: IrDeclarationParent?, + val context: Context) { val copyIrElement = run { val typeParameters = @@ -131,7 +118,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW DeepCopyIrTreeWithSymbolsForInliner(context, typeArguments, parent) } - val substituteMap = mutableMapOf() + val substituteMap = mutableMapOf() fun inline() = inlineFunction(callSite, callee) @@ -146,35 +133,42 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW } } - private fun inlineFunction(callSite: IrCall, callee: IrFunction): IrReturnableBlockImpl { + private fun inlineFunction(callSite: IrCall, callee: IrFunction): IrReturnableBlock { val copiedCallee = copyIrElement.copy(callee) as IrFunction val evaluationStatements = evaluateArguments(callSite, copiedCallee) val statements = (copiedCallee.body as IrBlockBody).statements val irReturnableBlockSymbol = IrReturnableBlockSymbolImpl(copiedCallee.descriptor.original) - val descriptor = callee.descriptor.original val startOffset = callee.startOffset val endOffset = callee.endOffset val irBuilder = context.createIrBuilder(irReturnableBlockSymbol, startOffset, endOffset) - if (descriptor.isInlineConstructor) { + if (callee.isInlineConstructor) { + // Copier sets parent to be the current function but + // constructor's parent cannot be a function. + val constructedClass = callee.parentAsClass + copiedCallee.parent = constructedClass val delegatingConstructorCall = statements[0] as IrDelegatingConstructorCall irBuilder.run { - val constructorDescriptor = delegatingConstructorCall.descriptor.original - val constructorCall = irCall(delegatingConstructorCall.symbol, callSite.type, - constructorDescriptor.typeParameters.map { delegatingConstructorCall.getTypeArgument(it)!! }).apply { - constructorDescriptor.valueParameters.forEach { - putValueArgument( - it, - delegatingConstructorCall.getValueArgument(it) - ) + val constructorCall = IrCallImpl( + startOffset, endOffset, + callSite.type, + delegatingConstructorCall.symbol, delegatingConstructorCall.descriptor, + constructedClass.typeParameters.size, + delegatingConstructorCall.symbol.owner.valueParameters.size + ).apply { + delegatingConstructorCall.symbol.owner.valueParameters.forEach { + putValueArgument(it.index, delegatingConstructorCall.getValueArgument(it.index)) + } + constructedClass.typeParameters.forEach { + putTypeArgument(it.index, delegatingConstructorCall.getTypeArgument(it.index)) } } - val oldThis = delegatingConstructorCall.descriptor.constructedClass.thisAsReceiverParameter - val newThis = currentScope.scope.createTemporaryVariable( + val oldThis = constructedClass.thisReceiver!! + val newThis = currentScope.scope.createTemporaryVariableWithWrappedDescriptor( irExpression = constructorCall, - nameHint = delegatingConstructorCall.descriptor.fqNameSafe.toString() + ".this" + nameHint = constructedClass.fqNameSafe.toString() + ".this" ) statements[0] = newThis substituteMap[oldThis] = irGet(newThis) @@ -182,20 +176,25 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW } } + val sourceFile = callee.file + val transformer = ParameterSubstitutor() statements.transform { it.transform(transformer, data = null) } statements.addAll(0, evaluationStatements) + val isCoroutineIntrinsicCall = callSite.descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn( + context.configuration.languageVersionSettings) + return IrReturnableBlockImpl( - startOffset = startOffset, - endOffset = endOffset, - type = copiedCallee.returnType, - symbol = irReturnableBlockSymbol, - origin = null, - statements = statements, - sourceFileSymbol = callee.file.symbol + startOffset = startOffset, + endOffset = endOffset, + type = callSite.type, + symbol = irReturnableBlockSymbol, + origin = if (isCoroutineIntrinsicCall) CoroutineIntrinsicLambdaOrigin else null, + statements = statements, + sourceFileSymbol = sourceFile.symbol ).apply { - transformChildrenVoid(object: IrElementTransformerVoid() { + transformChildrenVoid(object : IrElementTransformerVoid() { override fun visitReturn(expression: IrReturn): IrExpression { expression.transformChildrenVoid(this) @@ -213,8 +212,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW override fun visitGetValue(expression: IrGetValue): IrExpression { val newExpression = super.visitGetValue(expression) as IrGetValue - val descriptor = newExpression.descriptor - val argument = substituteMap[descriptor] ?: return newExpression + val argument = substituteMap[newExpression.symbol.owner] ?: return newExpression argument.transformChildrenVoid(this) // Default argument can contain subjects for substitution. return copyIrElement.copy(argument) as IrExpression @@ -222,57 +220,46 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW //-----------------------------------------------------------------// - private val IrFunctionReference.isLambda: Boolean - get() { - return symbol.owner.visibility == Visibilities.LOCAL && origin == IrStatementOrigin.LAMBDA - } - override fun visitCall(expression: IrCall): IrExpression { if (!isLambdaCall(expression)) return super.visitCall(expression) val dispatchReceiver = expression.dispatchReceiver as IrGetValue - val functionArgument = substituteMap[dispatchReceiver.descriptor] - if (functionArgument == null) + val functionArgument = substituteMap[dispatchReceiver.symbol.owner] ?: return super.visitCall(expression) + if ((dispatchReceiver.symbol.owner as? IrValueParameter)?.isNoinline == true) return super.visitCall(expression) - val dispatchDescriptor = dispatchReceiver.descriptor - if (dispatchDescriptor is ValueParameterDescriptor && - dispatchDescriptor.isNoinline - ) return super.visitCall(expression) if (functionArgument is IrFunctionReference) { - if (!functionArgument.isLambda) return super.visitCall(expression) - - val functionDescriptor = functionArgument.descriptor - val functionParameters = functionDescriptor.explicitParameters - val boundFunctionParameters = functionArgument.getArguments() + val function = functionArgument.symbol.owner + val functionParameters = function.explicitParameters + val boundFunctionParameters = functionArgument.getArgumentsWithIr() val unboundFunctionParameters = functionParameters - boundFunctionParameters.map { it.first } val boundFunctionParametersMap = boundFunctionParameters.associate { it.first to it.second } var unboundIndex = 0 val unboundArgsSet = unboundFunctionParameters.toSet() - val valueParameters = expression.getArguments().drop(1) // Skip dispatch receiver. + val valueParameters = expression.getArgumentsWithIr().drop(1) // Skip dispatch receiver. val immediateCall = IrCallImpl( - startOffset = expression.startOffset, - endOffset = expression.endOffset, - type = expression.type, - symbol = functionArgument.symbol, - descriptor = functionArgument.descriptor + expression.startOffset, expression.endOffset, + expression.type, + functionArgument.symbol ).apply { functionParameters.forEach { val argument = - if (!unboundArgsSet.contains(it)) + if (it !in unboundArgsSet) boundFunctionParametersMap[it]!! else - valueParameters[unboundIndex++].second + valueParameters.getOrNull(unboundIndex++)?.second when (it) { - functionDescriptor.dispatchReceiverParameter -> this.dispatchReceiver = argument - functionDescriptor.extensionReceiverParameter -> this.extensionReceiver = argument - else -> putValueArgument((it as ValueParameterDescriptor).index, argument) + function.dispatchReceiverParameter -> this.dispatchReceiver = argument + function.extensionReceiverParameter -> this.extensionReceiver = argument + else -> putValueArgument(it.index, argument) } } - assert(unboundIndex == valueParameters.size) { "Not all arguments of are used" } + assert(unboundIndex >= valueParameters.size) { "Not all arguments of are used" } + for (index in 0 until functionArgument.typeArgumentsCount) + putTypeArgument(index, functionArgument.getTypeArgument(index)) } return this@FunctionInlining.visitCall(super.visitCall(immediateCall) as IrCall) } @@ -280,14 +267,8 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW return super.visitCall(expression) val functionDeclaration = functionArgument.statements[0] as IrFunction - val newExpression = inlineFunction( - expression, - functionDeclaration - ) // Inline the lambda. Lambda parameters will be substituted with lambda arguments. - return newExpression.transform( - this, - null - ) // Substitute lambda arguments with target function arguments. + val newExpression = inlineFunction(expression, functionDeclaration) // Inline the lambda. Lambda parameters will be substituted with lambda arguments. + return newExpression.transform(this, null) // Substitute lambda arguments with target function arguments. } //-----------------------------------------------------------------// @@ -295,27 +276,33 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW override fun visitElement(element: IrElement) = element.accept(this, null) } - private fun isLambdaCall(irCall: IrCall) = irCall.symbol.owner.isFunctionInvoke && irCall.dispatchReceiver is IrGetValue + private fun isLambdaCall(irCall: IrCall): Boolean { + val callee = irCall.symbol.owner + val dispatchReceiver = callee.dispatchReceiverParameter ?: return false + assert(!dispatchReceiver.type.isKFunction()) + + return (dispatchReceiver.type.isFunction() || dispatchReceiver.type.isSuspendFunction()) + && callee.name == OperatorNameConventions.INVOKE + && irCall.dispatchReceiver is IrGetValue + } //-------------------------------------------------------------------------// - private inner class ParameterToArgument( - val parameter: IrValueParameter, - val argumentExpression: IrExpression - ) { + private fun IrValueParameter.isInlineParameter() = + !isNoinline && !type.isNullable() && type.isFunctionOrKFunction() + + private inner class ParameterToArgument(val parameter: IrValueParameter, + val argumentExpression: IrExpression) { val isInlinableLambdaArgument: Boolean get() { - if (!InlineUtil.isInlineParameter(parameter.descriptor)) return false - if (argumentExpression is IrFunctionReference - && !argumentExpression.descriptor.isSuspend - ) return true // Skip suspend functions for now since it's not supported by FE anyway. + if (!parameter.isInlineParameter()) return false + if (argumentExpression is IrFunctionReference) return true // Do pattern-matching on IR. if (argumentExpression !is IrBlock) return false if (argumentExpression.origin != IrStatementOrigin.LAMBDA && - argumentExpression.origin != IrStatementOrigin.ANONYMOUS_FUNCTION - ) return false + argumentExpression.origin != IrStatementOrigin.ANONYMOUS_FUNCTION) return false val statements = argumentExpression.statements val irFunction = statements[0] val irCallableReference = statements[1] @@ -326,7 +313,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW val isImmutableVariableLoad: Boolean get() = argumentExpression.let { - it is IrGetValue && !it.symbol.owner.let { v -> v is IrVariable && v.isVar } + it is IrGetValue && !it.symbol.owner.let { it is IrVariable && it.isVar } } } @@ -337,15 +324,14 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW val parameterToArgument = mutableListOf() if (callSite.dispatchReceiver != null && // Only if there are non null dispatch receivers both - callee.dispatchReceiverParameter != null - ) // on call site and in function declaration. + callee.dispatchReceiverParameter != null) // on call site and in function declaration. parameterToArgument += ParameterToArgument( parameter = callee.dispatchReceiverParameter!!, argumentExpression = callSite.dispatchReceiver!! ) val valueArguments = - callSite.descriptor.valueParameters.map { callSite.getValueArgument(it) }.toMutableList() + callSite.symbol.owner.valueParameters.map { callSite.getValueArgument(it.index) }.toMutableList() if (callee.extensionReceiverParameter != null) { parameterToArgument += ParameterToArgument( @@ -414,34 +400,25 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW val evaluationStatements = mutableListOf() val substitutor = ParameterSubstitutor() parameterToArgumentOld.forEach { - val parameterDescriptor = it.parameter.descriptor - /* - * We need to create temporary variable for each argument except inlinable lambda arguments. - * For simplicity and to produce simpler IR we don't create temporaries for every immutable variable, - * not only for those referring to inlinable lambdas. - */ + * We need to create temporary variable for each argument except inlinable lambda arguments. + * For simplicity and to produce simpler IR we don't create temporaries for every immutable variable, + * not only for those referring to inlinable lambdas. + */ if (it.isInlinableLambdaArgument) { - substituteMap[parameterDescriptor] = it.argumentExpression + substituteMap[it.parameter] = it.argumentExpression return@forEach } if (it.isImmutableVariableLoad) { - substituteMap[parameterDescriptor] = it.argumentExpression.transform( - substitutor, - data = null - ) // Arguments may reference the previous ones - substitute them. + substituteMap[it.parameter] = it.argumentExpression.transform(substitutor, data = null) // Arguments may reference the previous ones - substitute them. return@forEach } - val newVariable = currentScope.scope.createTemporaryVariable( - irExpression = it.argumentExpression.transform( - substitutor, - data = null - ), // Arguments may reference the previous ones - substitute them. - nameHint = callee.descriptor.name.toString(), - isMutable = false - ) + val newVariable = currentScope.scope.createTemporaryVariableWithWrappedDescriptor( // Create new variable and init it with the parameter expression. + irExpression = it.argumentExpression.transform(substitutor, data = null), // Arguments may reference the previous ones - substitute them. + nameHint = callee.symbol.owner.name.toString(), + isMutable = false) evaluationStatements.add(newVariable) val getVal = IrGetValueImpl( @@ -450,7 +427,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW type = newVariable.type, symbol = newVariable.symbol ) - substituteMap[parameterDescriptor] = getVal + substituteMap[it.parameter] = getVal } return evaluationStatements } diff --git a/compiler/testData/codegen/box/coroutines/inlineSuspendLambdaNonLocalReturn.kt b/compiler/testData/codegen/box/coroutines/inlineSuspendLambdaNonLocalReturn.kt index ce73162181a..bc22bbc0706 100644 --- a/compiler/testData/codegen/box/coroutines/inlineSuspendLambdaNonLocalReturn.kt +++ b/compiler/testData/codegen/box/coroutines/inlineSuspendLambdaNonLocalReturn.kt @@ -1,4 +1,4 @@ -// IGNORE_BACKEND: JVM_IR +// IGNORE_BACKEND: JVM_IR, JS_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/boxInline/suspend/callableReference/ordinaryParameter.kt b/compiler/testData/codegen/boxInline/suspend/callableReference/ordinaryParameter.kt index bad96cdc9f4..48e1babea57 100644 --- a/compiler/testData/codegen/boxInline/suspend/callableReference/ordinaryParameter.kt +++ b/compiler/testData/codegen/boxInline/suspend/callableReference/ordinaryParameter.kt @@ -1,7 +1,7 @@ // !LANGUAGE: +NewInference // IGNORE_BACKEND: NATIVE // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS, JS_IR +// IGNORE_BACKEND: JS // NO_CHECK_LAMBDA_INLINING // FILE: test.kt // We cannot use COMMON_COROUTINES_TEST here due to !LANGUAGE directive diff --git a/compiler/testData/codegen/boxInline/suspend/callableReference/suspendOfOrdinary.kt b/compiler/testData/codegen/boxInline/suspend/callableReference/suspendOfOrdinary.kt index ed26a580728..599930ca96d 100644 --- a/compiler/testData/codegen/boxInline/suspend/callableReference/suspendOfOrdinary.kt +++ b/compiler/testData/codegen/boxInline/suspend/callableReference/suspendOfOrdinary.kt @@ -1,7 +1,7 @@ // !LANGUAGE: +NewInference // IGNORE_BACKEND: NATIVE // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS, JS_IR +// IGNORE_BACKEND: JS // NO_CHECK_LAMBDA_INLINING // FILE: test.kt