From c68c4d3b6d8330d36f1d35be37d8d2e7cf6129d0 Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Fri, 10 Feb 2017 17:37:03 +0300 Subject: [PATCH] backend: Use simple DFS during closure building. Now closure builder can contain includes. Includes are another builders which closures must be added to the building one. Includes are: For function closure: call targets (including constructors) callable reference targets For class: all children declarations call targets (including constructors) callable reference targets For constructor: call and callable reference targets delegating constructor call target constructing class closure --- .../common/lower/AbstractClosureAnnotator.kt | 129 +++++++++--------- .../common/lower/LocalDeclarationsLowering.kt | 24 ++-- .../tests/codegen/function/localFunction.kt | 12 ++ .../tests/codegen/function/localFunction2.kt | 16 +++ .../localFunctionCallFromLocalClass.kt | 0 .../localClass/localFunctionInLocalClass.kt | 21 +++ 6 files changed, 127 insertions(+), 75 deletions(-) create mode 100644 backend.native/tests/codegen/function/localFunction2.kt rename backend.native/tests/codegen/{innerClass => localClass}/localFunctionCallFromLocalClass.kt (100%) create mode 100644 backend.native/tests/codegen/localClass/localFunctionInLocalClass.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt index 123b54af751..c1fa8e82fa1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt @@ -3,16 +3,16 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny // TODO: synchronize with JVM BE -class Closure(val capturedValues: List) +// TODO: rename the file. +class Closure(val capturedValues: List = emptyList()) private fun MutableList.push(element: E) = this.add(element) @@ -20,37 +20,51 @@ private fun MutableList.pop() = this.removeAt(size - 1) private fun MutableList.peek(): E? = if (size == 0) null else this[size - 1] -abstract class AbstractClosureAnnotator { - protected abstract fun recordFunctionClosure(functionDescriptor: FunctionDescriptor, closure: Closure) - protected abstract fun recordClassClosure(classDescriptor: ClassDescriptor, closure: Closure) +class ClosureAnnotator { + private val closureBuilders = mutableMapOf() - private class ClosureBuilder { + constructor(declaration: IrDeclaration) { + // Collect all closures for classes and functions. Collect call graph + declaration.acceptChildrenVoid(ClosureCollectorVisitor()) + } + + fun getFunctionClosure(descriptor: FunctionDescriptor) = getClosure(descriptor) + fun getClassClosure(descriptor: ClassDescriptor) = getClosure(descriptor) + + private fun getClosure(descriptor: DeclarationDescriptor) : Closure { + closureBuilders.values.forEach { it.processed = false } + return closureBuilders + .getOrElse(descriptor) { throw AssertionError("No closure builder for passed descriptor.") } + .buildClosure() + } + + private class ClosureBuilder(val owner: DeclarationDescriptor) { val capturedValues = mutableSetOf() - private val nestedBuilders = mutableSetOf() private val declaredValues = mutableSetOf() + private val includes = mutableSetOf() - fun buildClosure() : Closure { - val processed = mutableSetOf(this) - val builderStack = mutableListOf().apply { addAll(nestedBuilders) } - while (builderStack.isNotEmpty()) { - val builder = builderStack.pop() - if (!processed.contains(builder)) { - processed.add(builder) - builderStack.addAll(builder.nestedBuilders) - fillInNestedClosure(capturedValues, builder.capturedValues) + var processed = false + + /* + * Node's closure = variables captured by the node + + * closure of all included nodes - + * variables declared in the node. + */ + fun buildClosure(): Closure { + val result = mutableSetOf().apply { addAll(capturedValues) } + includes.forEach { + if (!it.processed) { + it.processed = true + it.buildClosure().capturedValues.filterTo(result) { isExternal(it) } } } - // TODO: Save the closure and reuse it. - return Closure(capturedValues.toList()) + // TODO: We can save the closure and reuse it. + return Closure(result.toList()) } - fun addNested(builder: ClosureBuilder) { - nestedBuilders.add(builder) - declaredValues.addAll(builder.declaredValues) - } - private fun fillInNestedClosure(destination: MutableSet, nested: Collection) { - nested.filterTo(destination) { isExternal(it) } + fun include(includingBuilder: ClosureBuilder) { + includes.add(includingBuilder) } fun declareVariable(valueDescriptor: ValueDescriptor?) { @@ -66,27 +80,21 @@ abstract class AbstractClosureAnnotator { fun isExternal(valueDescriptor: ValueDescriptor): Boolean { return !declaredValues.contains(valueDescriptor) } - } - private val closureBuilders = mutableMapOf() - - fun annotate(declaration: IrDeclaration) { - // First pass - collect all closures for classes and functions. Collect call graph - declaration.acceptChildrenVoid(ClosureCollectorVisitor()) - // Second pass - build closures on basis of calls. - closureBuilders.forEach { descriptor, builder -> - when(descriptor) { - is FunctionDescriptor -> recordFunctionClosure(descriptor, builder.buildClosure()) - is ClassDescriptor -> recordClassClosure(descriptor, builder.buildClosure()) - else -> throw AssertionError("Unexpected descriptor type.") - } - } } private inner class ClosureCollectorVisitor : IrElementVisitorVoid { - protected val closuresStack = mutableListOf() - protected val classClosures = mutableMapOf() + val closuresStack = mutableListOf() + + fun includeInParent(builder: ClosureBuilder) { + // We don't include functions or classes in a parent function when they are declared. + // Instead we will include them when are is used (use = call for a function or constructor call for a class). + val parentBuilder = closuresStack.peek() + if (parentBuilder != null && parentBuilder.owner !is FunctionDescriptor) { + parentBuilder.include(builder) + } + } override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) @@ -94,47 +102,43 @@ abstract class AbstractClosureAnnotator { override fun visitClass(declaration: IrClass) { val classDescriptor = declaration.descriptor - val closureBuilder = ClosureBuilder() + val closureBuilder = ClosureBuilder(classDescriptor) closureBuilders[declaration.descriptor] = closureBuilder closureBuilder.declareVariable(classDescriptor.thisAsReceiverParameter) - if (classDescriptor.isInner) + if (classDescriptor.isInner) { closureBuilder.declareVariable((classDescriptor.containingDeclaration as ClassDescriptor).thisAsReceiverParameter) + includeInParent(closureBuilder) + } closuresStack.push(closureBuilder) declaration.acceptChildrenVoid(this) closuresStack.pop() - - val superClassClosure = classClosures[classDescriptor.getSuperClassOrAny()] - if (superClassClosure != null) { - // Capture all values from the super class since we need to call constructor of super class - // with its captured values. - closureBuilder.addNested(superClassClosure) - } - - if (DescriptorUtils.isLocal(classDescriptor)) { - classClosures[classDescriptor] = closureBuilder - } - - closuresStack.peek()?.addNested(closureBuilder) } override fun visitFunction(declaration: IrFunction) { val functionDescriptor = declaration.descriptor - val closureBuilder = ClosureBuilder() - closureBuilders[declaration.descriptor] = closureBuilder + val closureBuilder = ClosureBuilder(functionDescriptor) + closureBuilders[functionDescriptor] = closureBuilder functionDescriptor.valueParameters.forEach { closureBuilder.declareVariable(it) } closureBuilder.declareVariable(functionDescriptor.dispatchReceiverParameter) closureBuilder.declareVariable(functionDescriptor.extensionReceiverParameter) - if (functionDescriptor is ConstructorDescriptor) + if (functionDescriptor is ConstructorDescriptor) { closureBuilder.declareVariable(functionDescriptor.constructedClass.thisAsReceiverParameter) + // Include closure of the class in the constructor closure. + val classBuilder = closuresStack.peek() + classBuilder?.let { + assert(classBuilder.owner == functionDescriptor.constructedClass) + closureBuilder.include(classBuilder) + } + } closuresStack.push(closureBuilder) declaration.acceptChildrenVoid(this) closuresStack.pop() - closuresStack.peek()?.addNested(closureBuilder) + includeInParent(closureBuilder) } override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) { @@ -152,13 +156,14 @@ abstract class AbstractClosureAnnotator { super.visitVariable(declaration) } - override fun visitCall(expression: IrCall) { + // Process delegating constructor calls, enum constructor calls, calls and callable references. + override fun visitMemberAccess(expression: IrMemberAccessExpression) { expression.acceptChildrenVoid(this) val descriptor = expression.descriptor if (DescriptorUtils.isLocal(descriptor)) { val builder = closureBuilders[descriptor] builder?.let { - closuresStack.peek()?.addNested(builder) + closuresStack.peek()?.include(builder) } } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt index 3246d7c24ec..b7401620016 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt @@ -36,7 +36,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.util.transformFlat import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.descriptorUtil.parents import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.types.KotlinType @@ -505,14 +504,14 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai return newValueParameters } - private fun createTransformedConstructorDescriptor(localFunctionContext: LocalClassConstructorContext) { - val oldDescriptor = localFunctionContext.descriptor + private fun createTransformedConstructorDescriptor(constructorContext: LocalClassConstructorContext) { + val oldDescriptor = constructorContext.descriptor val localClassContext = localClasses[oldDescriptor.containingDeclaration]!! val newDescriptor = ClassConstructorDescriptorImpl.create( localClassContext.descriptor, Annotations.EMPTY, oldDescriptor.isPrimary, oldDescriptor.source) - localFunctionContext.transformedDescriptor = newDescriptor + constructorContext.transformedDescriptor = newDescriptor transformedDescriptors[oldDescriptor] = newDescriptor // Do not substitute type parameters for now. @@ -520,7 +519,7 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai val capturedValues = localClasses[oldDescriptor.containingDeclaration]!!.closure.capturedValues - val newValueParameters = createTransformedValueParameters(localFunctionContext, capturedValues) + val newValueParameters = createTransformedValueParameters(constructorContext, capturedValues) newDescriptor.initialize( newValueParameters, @@ -623,15 +622,14 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai private fun collectClosures() { - object : AbstractClosureAnnotator() { - override fun recordFunctionClosure(functionDescriptor: FunctionDescriptor, closure: Closure) { - localFunctions[functionDescriptor]?.closure = closure - } + val annotator = ClosureAnnotator(memberDeclaration) + localFunctions.forEach { descriptor, context -> + context.closure = annotator.getFunctionClosure(descriptor) + } - override fun recordClassClosure(classDescriptor: ClassDescriptor, closure: Closure) { - localClasses[classDescriptor]?.closure = closure - } - }.annotate(memberDeclaration) + localClasses.forEach { descriptor, context -> + context.closure = annotator.getClassClosure(descriptor) + } } private fun collectLocalDeclarations() { diff --git a/backend.native/tests/codegen/function/localFunction.kt b/backend.native/tests/codegen/function/localFunction.kt index d50382ac0c9..41c74aefd04 100644 --- a/backend.native/tests/codegen/function/localFunction.kt +++ b/backend.native/tests/codegen/function/localFunction.kt @@ -7,5 +7,17 @@ fun main(args : Array) { } local0() } + + fun l1() { + var x = 1 + fun l2() { + fun l3() { + l1() + x = 5 + } + l3() + } + } + println("OK") } \ No newline at end of file diff --git a/backend.native/tests/codegen/function/localFunction2.kt b/backend.native/tests/codegen/function/localFunction2.kt new file mode 100644 index 00000000000..3a0b76b5da4 --- /dev/null +++ b/backend.native/tests/codegen/function/localFunction2.kt @@ -0,0 +1,16 @@ +fun main(args: Array) { + var a = 0 + fun local() { + class A { + val b = 0 + fun f() { + a = b + } + + } + fun local2() : A { + return A() + } + } + println("OK") +} \ No newline at end of file diff --git a/backend.native/tests/codegen/innerClass/localFunctionCallFromLocalClass.kt b/backend.native/tests/codegen/localClass/localFunctionCallFromLocalClass.kt similarity index 100% rename from backend.native/tests/codegen/innerClass/localFunctionCallFromLocalClass.kt rename to backend.native/tests/codegen/localClass/localFunctionCallFromLocalClass.kt diff --git a/backend.native/tests/codegen/localClass/localFunctionInLocalClass.kt b/backend.native/tests/codegen/localClass/localFunctionInLocalClass.kt new file mode 100644 index 00000000000..103daedd0f8 --- /dev/null +++ b/backend.native/tests/codegen/localClass/localFunctionInLocalClass.kt @@ -0,0 +1,21 @@ +fun main(args : Array) { + var x = 0 + class A { + fun bar() { + fun local() { + class B { + fun baz() { + fun local2() { + x++ + } + local2() + } + } + B().baz() + } + local() + } + } + A().bar() + println("OK") +} \ No newline at end of file