From 39f002db81cee27dca165ef82a8cd4baab4ce385 Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Tue, 14 Mar 2017 17:18:01 +0700 Subject: [PATCH] Combine parameter evaluation with parameter transformer --- .../backend/konan/lower/FunctionInlining.kt | 366 +++++++++--------- 1 file changed, 181 insertions(+), 185 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index 31295e1b6e7..9f0c6f97f9e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -3,7 +3,9 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.descriptors.isFunctionInvoke import org.jetbrains.kotlin.backend.konan.ir.IrInlineFunctionBody -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.ValueDescriptor import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.Scope @@ -11,7 +13,10 @@ import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.expressions.impl.IrContainerExpressionBase +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrMemberAccessExpressionBase +import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.util.DeepCopyIrTree import org.jetbrains.kotlin.ir.util.getArguments import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -31,6 +36,10 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid( //-------------------------------------------------------------------------// + override fun visitElement(element: IrElement) = element.accept(this, null) + + //-------------------------------------------------------------------------// + override fun visitFile(declaration: IrFile): IrFile { currentFile = declaration return super.visitFile(declaration) @@ -41,37 +50,31 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid( override fun visitFunction(declaration: IrFunction): IrStatement { currentFunction = declaration functionScope = Scope(declaration.descriptor) + // println("visitFunction ${declaration.descriptor.name}") return super.visitFunction(declaration) } //-------------------------------------------------------------------------// - fun isLambdaExpression(expression: IrExpression) : Boolean { - if (expression !is IrContainerExpressionBase) return false - if (expression.origin != IrStatementOrigin.LAMBDA) return false - return true - } + override fun visitCall(expression: IrCall): IrExpression { - //-------------------------------------------------------------------------// + val fqName = currentFile!!.packageFragmentDescriptor.fqName.asString() // TODO to be removed after stdlib compilation + if(fqName.contains("kotlin")) return super.visitCall(expression) // TODO to be removed after stdlib compilation + val fileName = currentFile!!.fileEntry.name + if (fileName.contains("cinterop")) return super.visitCall(expression) - fun evaluateParameters(irCall: IrCall, - statements: MutableList): MutableList> { + if (currentFunction == null) return super.visitCall(expression) + if (currentFunction!!.descriptor.isInline) return super.visitCall(expression) // TODO workaround - val parametersOld = irCall.getArguments() // Create map call_site_argument -> inline_function_parameter. - val parametersNew = parametersOld.map { - val argument = it.first.original - val parameter = it.second - if (parameter is IrGetValue) return@map argument to it.second // Parameter is already GetValue - nothing to evaluate. - if (parameter is IrConst<*>) return@map argument to it.second // Parameter is constant - nothing to evaluate. - if (isLambdaExpression(parameter)) return@map argument to it.second // Parameter is lambda - will be inlined. - - val newVar = functionScope!!.createTemporaryVariable(parameter, "inline", false) // Create new variable and init it with the parameter expression. - statements.add(0, newVar) // Add initialization of the new variable in statement list. - - val getVal = IrGetValueImpl(0, 0, newVar.descriptor) // Create new IR element representing access the new variable. - argument to getVal // Parameter will be replaced with the new variable. + // println("visitCall ${expression.descriptor.name}") + val functionDescriptor = expression.descriptor as FunctionDescriptor + if (functionDescriptor.isInline) { + val inlineFunctionBody = inlineFunction(expression) +// inlineFunctionBody.accept(this, null) // + return inlineFunctionBody // Return newly created IrInlineBody instead of IrCall. } - return parametersNew.toMutableList() + + return super.visitCall(expression) } //-------------------------------------------------------------------------// @@ -83,7 +86,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid( .functions[functionDescriptor.original] // Get FunctionDeclaration by FunctionDescriptor. if (functionDeclaration == null) return irCall // Function is declared in another module. - print("file: ${currentFile!!.fileEntry.name} ") // TODO debug output + print(" inline file: ${currentFile!!.fileEntry.name} ") // TODO debug output print("function: ${currentFunction!!.descriptor.name} ") // TODO debug output println("call: ${functionDescriptor.name} ${irCall.startOffset}") // TODO debug output @@ -99,71 +102,154 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid( val statements = blockBody.statements val inlineBody = IrInlineFunctionBody(startOffset, endOffset, returnType, null, statements) - val newBody = super.visitBlock(inlineBody) as IrBlock - val parameters = evaluateParameters(irCall, newBody.statements) // Evaluate parameters representing expression. - val lambdaInliner = LambdaInliner(parameters, functionScope!!) - newBody.accept(lambdaInliner, null) // TODO - val transformer = ParametersTransformer(parameters, irCall, functionScope!!) - newBody.accept(transformer, null) // TODO // Replace parameters with expression. + val parameterToArgument: MutableList > = irCall.getArguments().toMutableList() + val typeArgsMap = (irCall as IrMemberAccessExpressionBase).typeArguments // If there are no type args - do nothing. + val statementsBuf = mutableListOf() + val transformer = ParametersTransformer(parameterToArgument, typeArgsMap, statementsBuf) + inlineBody.accept(transformer, null) // Replace parameters with expression. + inlineBody.statements.addAll(0, statementsBuf) - return newBody + val lambdaInliner = LambdaInliner(parameterToArgument) + inlineBody.accept(lambdaInliner, null) + + return inlineBody } //-------------------------------------------------------------------------// - override fun visitCall(expression: IrCall): IrExpression { + inner class ParametersTransformer(val substituteMap: MutableList >, + val typeArgsMap: Map ?, + val statements: MutableList): IrElementTransformerVoid() { - val fqName = currentFile!!.packageFragmentDescriptor.fqName.asString() // TODO to be removed after stdlib compilation - //if(fqName.contains("kotlin")) return super.visitCall(expression) // TODO to be removed after stdlib compilation - val fileName = currentFile!!.fileEntry.name - if (fileName.contains("cinterop")) return super.visitCall(expression) - if (currentFunction!!.descriptor.isInline) return super.visitCall(expression) // TODO workaround + override fun visitElement(element: IrElement) = element.accept(this, null) - val functionDescriptor = expression.descriptor as FunctionDescriptor - if (functionDescriptor.isInline) return inlineFunction(expression) // Return newly created IrInlineBody instead of IrCall. + //---------------------------------------------------------------------// - return super.visitCall(expression) + override fun visitTypeOperator(oldExpression: IrTypeOperatorCall): IrExpression { + + val expression = super.visitTypeOperator(oldExpression) as IrTypeOperatorCall + if (expression.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) { // Nothing to do for IMPLICIT_COERCION_TO_UNIT + return expression + } + + if (typeArgsMap == null) return expression + + val operandTypeDescriptor = expression.typeOperand.constructor.declarationDescriptor + if (operandTypeDescriptor !is TypeParameterDescriptor) return expression // It is not TypeParameter - do nothing + + val typeNew = typeArgsMap[operandTypeDescriptor]!! + val startOffset = expression.startOffset + val endOffset = expression.endOffset + val type = expression.type // TODO + val operator = expression.operator + val argument = expression.argument + + return IrTypeOperatorCallImpl(startOffset, endOffset, type, operator, typeNew, argument) + } + + //---------------------------------------------------------------------// + + fun isLambdaExpression(expression: IrExpression) : Boolean { + if (expression !is IrContainerExpressionBase) return false + if (expression.origin != IrStatementOrigin.LAMBDA) return false + return true + } + + //---------------------------------------------------------------------// + + fun needsEvaluation(expression: IrExpression): Boolean { + if (expression is IrGetValue) return false // Parameter is already GetValue - nothing to evaluate. + if (expression is IrConst<*>) return false // Parameter is constant - nothing to evaluate. + if (isLambdaExpression(expression)) return false // Parameter is lambda - will be inlined. + return true + } + + //---------------------------------------------------------------------// + + fun evaluateExpression(expression: IrExpression): IrGetValue { + // println(" evaluateExpression $expression") + val newVar = functionScope!!.createTemporaryVariable(expression, "inline", false) // Create new variable and init it with the parameter expression. + statements.add(0, newVar) // Add initialization of the new variable in statement list. + + val getVal = IrGetValueImpl(0, 0, newVar.descriptor) // Create new IR element representing access the new variable. + return getVal // Parameter will be replaced with the new variable. + } + + //---------------------------------------------------------------------// + + override fun visitGetValue(expression: IrGetValue): IrExpression { + // println(" visitGetValue ${expression.descriptor.name}") + val newExpression = super.visitGetValue(expression) as IrGetValue + val descriptor = newExpression.descriptor + val substitute = substituteMap.find { it.first.original == descriptor } // Find expression to replace this parameter. + if (substitute == null) return newExpression // If there is no such expression - do nothing + + val parameter = substitute.first + val argument = substitute.second + if (needsEvaluation(argument)) { + val newArgument = evaluateExpression(argument) + val index = substituteMap.indexOf(substitute) + val newSubstitute = parameter to newArgument + substituteMap[index] = newSubstitute + return newArgument + } + + return argument + } + + //---------------------------------------------------------------------// + + fun newVariable(oldVariable: IrVariable): IrVariable { + val initializer = oldVariable.initializer!! + return functionScope!!.createTemporaryVariable(initializer, "inline", false) // Create new variable and init it with the parameter expression. + } + + //---------------------------------------------------------------------// + + override fun visitVariable(declaration: IrVariable): IrStatement { + + // println(" visitVariable ${declaration.descriptor.name}") + val newDeclaration = super.visitVariable(declaration) as IrVariable // Process variable initializer. + val newVariable = newVariable(newDeclaration) // Create new local variable. + val getVal = IrGetValueImpl(0, 0, newVariable.descriptor) // Create new IR element representing access the new variable. + substituteMap.add(declaration.descriptor to getVal) + return newVariable + } } //-------------------------------------------------------------------------// - override fun visitElement(element: IrElement) = element.accept(this, null) -} + inner class LambdaInliner(val substituteMap: MutableList >): IrElementTransformerVoid() { -//-----------------------------------------------------------------------------// + override fun visitElement(element: IrElement) = element.accept(this, null) -internal class LambdaInliner(val parameterToArgument: - MutableList>, val scope: Scope): IrElementTransformerVoid() { + //---------------------------------------------------------------------// - override fun visitElement(element: IrElement) = element.accept(this, null) + fun isLambdaCall(irCall: IrCall) : Boolean { + if (!(irCall.descriptor as FunctionDescriptor).isFunctionInvoke) return false // If it is lambda call. + if (irCall.dispatchReceiver !is IrGetValue) return false // Do not process such dispatch receiver. + return true + } - //-------------------------------------------------------------------------// + //---------------------------------------------------------------------// - fun isLambdaCall(irCall: IrCall) : Boolean { - if (!(irCall.descriptor as FunctionDescriptor).isFunctionInvoke) return false // If it is lambda call. - if (irCall.dispatchReceiver !is IrGetValue) return false // Do not process such dispatch receiver. - return true - } + fun getLambdaStatements(value: IrExpression) : MutableList { + val statements = (value as IrContainerExpressionBase).statements + val lambdaFunction = statements[0] as IrFunction + val lambdaBody = lambdaFunction.body as IrBlockBody + return lambdaBody.statements + } - //-------------------------------------------------------------------------// + //---------------------------------------------------------------------// - fun getLambdaStatements(value: IrExpression) : MutableList { - val statements = (value as IrContainerExpressionBase).statements - val lambdaFunction = statements[0] as IrFunction - val lambdaBody = lambdaFunction.body as IrBlockBody - return lambdaBody.statements - } + fun getLambdaReturnType(value: IrExpression) : KotlinType { + val statements = (value as IrContainerExpressionBase).statements + val lambdaFunction = statements[0] as IrFunction + return lambdaFunction.descriptor.returnType!! + } - //-------------------------------------------------------------------------// - - fun getLambdaReturnType(value: IrExpression) : KotlinType { - val statements = (value as IrContainerExpressionBase).statements - val lambdaFunction = statements[0] as IrFunction - return lambdaFunction.descriptor.returnType!! - } - - //-------------------------------------------------------------------------// + //---------------------------------------------------------------------// fun getLambdaFunction(lambdaArgument: IrExpression): IrFunction? { if (lambdaArgument !is IrBlock) return null @@ -193,136 +279,46 @@ internal class LambdaInliner(val parameterToArgument: return irFunction } - //-------------------------------------------------------------------------// + //---------------------------------------------------------------------// - fun inlineLambda(irCall: IrCall): IrExpression { + fun inlineLambda(irCall: IrCall): IrExpression { - val dispatchReceiver = irCall.dispatchReceiver as IrGetValue // - val parameterArgument = parameterToArgument.find { // Find expression to replace this parameter. - it.first == dispatchReceiver.descriptor - } - if (parameterArgument == null) return super.visitCall(irCall) // It is not function parameter. + val dispatchReceiver = irCall.dispatchReceiver as IrGetValue // + val substitute = substituteMap.find { // Find expression to replace this parameter. + it.first == dispatchReceiver.descriptor + } + if (substitute == null) return super.visitCall(irCall) // It is not function parameter - nothing to substitute. - val lambdaArgument = parameterArgument.second + val lambdaArgument = substitute.second val lambdaFunction = getLambdaFunction(lambdaArgument) if (lambdaFunction == null) return super.visitCall(irCall) // TODO - val parameters = lambdaFunction.descriptor.valueParameters // Lambda function parameters - val res = parameters.map { - val argument = irCall.getValueArgument(it.index) - val parameter = it as ValueDescriptor - parameter to argument!! - }.toMutableList() + val parameters = lambdaFunction.descriptor.valueParameters // Get lambda function parameters. + val substituteMap = parameters.map { // Iterate parameters. + val parameter = it as ValueDescriptor + val argument = irCall.getValueArgument(it.index) // Get corresponding argument. + parameter to argument!! // Create (parameter -> argument) pair. + }.toMutableList() - val lambdaStatements = getLambdaStatements(lambdaArgument) - val lambdaReturnType = getLambdaReturnType(lambdaArgument) - val inlineBody = IrInlineFunctionBody(0, 0, lambdaReturnType, null, lambdaStatements) + val lambdaStatements = getLambdaStatements(lambdaArgument) + val lambdaReturnType = getLambdaReturnType(lambdaArgument) + val inlineBody = IrInlineFunctionBody(0, 0, lambdaReturnType, null, lambdaStatements) - val transformer = ParametersTransformer(res, irCall, scope) - val aa = inlineBody.accept(transformer, null) // TODO // Replace parameters with expression. + val transformer = ParametersTransformer(substituteMap, null, lambdaStatements) + inlineBody.accept(transformer, null) // Replace parameters with expression. - return inlineBody // Replace call site with InlineFunctionBody. - } + return inlineBody // Replace call site with InlineFunctionBody. + } - //-------------------------------------------------------------------------// + //---------------------------------------------------------------------// - override fun visitCall(expression: IrCall): IrExpression { - if (!isLambdaCall(expression)) return super.visitCall(expression) // If call it is not lambda call - do nothing - return inlineLambda(expression) + override fun visitCall(expression: IrCall): IrExpression { + if (!isLambdaCall(expression)) return super.visitCall(expression) // If call it is not lambda call - do nothing. + return inlineLambda(expression) + } } } -//-----------------------------------------------------------------------------// -internal class ParametersTransformer(val parameterToArgument: MutableList>, - val callSite: IrCall, val scope: Scope): IrElementTransformerVoid() { - - override fun visitElement(element: IrElement) = element.accept(this, null) - - //-------------------------------------------------------------------------// - - override fun visitTypeOperator(oldExpression: IrTypeOperatorCall): IrExpression { - - val expression = super.visitTypeOperator(oldExpression) as IrTypeOperatorCall - if (expression.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) { // Nothing to do for IMPLICIT_COERCION_TO_UNIT - return expression - } - - val typeArgsMap = (callSite as IrMemberAccessExpressionBase).typeArguments // If there are no type args - do nothing. - if (typeArgsMap == null) return expression - - val operandTypeDescriptor = expression.typeOperand.constructor.declarationDescriptor - if (operandTypeDescriptor !is TypeParameterDescriptor) return expression // It is not TypeParameter - do nothing - - println("typeArgsMap: $typeArgsMap") - println("operandTypeDescriptor: $operandTypeDescriptor") - val typeNew = typeArgsMap[operandTypeDescriptor]!! - val startOffset = expression.startOffset - val endOffset = expression.endOffset - val type = expression.type // TODO - val operator = expression.operator - val argument = expression.argument - - return IrTypeOperatorCallImpl(startOffset, endOffset, type, operator, typeNew, argument) - } - - //-------------------------------------------------------------------------// - - override fun visitGetValue(expression: IrGetValue): IrExpression { - val descriptor = expression.descriptor - val result = super.visitGetValue(expression) - val parameterArgument = parameterToArgument.find { it.first == descriptor } // Find expression to replace this parameter. - if (parameterArgument == null) { - return result - } - - return parameterArgument.second // TODO should we proceed with IR iteration here? - } - - //-------------------------------------------------------------------------// - - fun newVariable(oldVariable: IrVariable, newType: KotlinType): IrVariable { - - val initializer = oldVariable.initializer!! - val newVariable = scope.createTemporaryVariable(initializer, "inline", false) // Create new variable and init it with the parameter expression. - - return newVariable - } - - //-------------------------------------------------------------------------// - - fun substituteType(variable: IrVariable): KotlinType? { - - val typeArgsMap = (callSite as IrMemberAccessExpressionBase).typeArguments - if (typeArgsMap == null) return variable.descriptor.type - val oldType = variable.descriptor.type - val typeDescriptor = oldType.constructor.declarationDescriptor - val newType = typeArgsMap[typeDescriptor] - if (newType == null) return variable.descriptor.type - return newType - } - - //-------------------------------------------------------------------------// - - override fun visitVariable(declaration: IrVariable): IrStatement { - -// println("visitVariableAkm ${declaration.descriptor.name}") - - val replacement = parameterToArgument.find { - if (it.second !is IrGetValue) return super.visitVariable(declaration) - val aa = it.second as IrGetValue - aa.descriptor == declaration.descriptor - } // Try to find - if (replacement != null) return super.visitVariable(declaration) - - val nn = super.visitVariable(declaration) as IrVariable - val newType = substituteType(nn)!! // If variable type should be replaced with type arg. - val newVariable = newVariable(nn, newType) // Create new local variable. - val getVal = IrGetValueImpl(0, 0, newVariable.descriptor) // Create new IR element representing access the new variable. - parameterToArgument.add(nn.descriptor to getVal) - return newVariable - } - -}