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
This commit is contained in:
Ilya Matveev
2017-02-10 17:37:03 +03:00
committed by ilmat192
parent 95a9f65afd
commit c68c4d3b6d
6 changed files with 127 additions and 75 deletions
@@ -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<ValueDescriptor>)
// TODO: rename the file.
class Closure(val capturedValues: List<ValueDescriptor> = emptyList())
private fun <E> MutableList<E>.push(element: E) = this.add(element)
@@ -20,37 +20,51 @@ private fun <E> MutableList<E>.pop() = this.removeAt(size - 1)
private fun <E> MutableList<E>.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<DeclarationDescriptor, ClosureBuilder>()
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<ValueDescriptor>()
private val nestedBuilders = mutableSetOf<ClosureBuilder>()
private val declaredValues = mutableSetOf<ValueDescriptor>()
private val includes = mutableSetOf<ClosureBuilder>()
fun buildClosure() : Closure {
val processed = mutableSetOf<ClosureBuilder>(this)
val builderStack = mutableListOf<ClosureBuilder>().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<ValueDescriptor>().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<ValueDescriptor>, nested: Collection<ValueDescriptor>) {
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<DeclarationDescriptor, ClosureBuilder>()
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<ClosureBuilder>()
protected val classClosures = mutableMapOf<ClassDescriptor, ClosureBuilder>()
val closuresStack = mutableListOf<ClosureBuilder>()
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)
}
}
}
@@ -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() {
@@ -7,5 +7,17 @@ fun main(args : Array<String>) {
}
local0()
}
fun l1() {
var x = 1
fun l2() {
fun l3() {
l1()
x = 5
}
l3()
}
}
println("OK")
}
@@ -0,0 +1,16 @@
fun main(args: Array<String>) {
var a = 0
fun local() {
class A {
val b = 0
fun f() {
a = b
}
}
fun local2() : A {
return A()
}
}
println("OK")
}
@@ -0,0 +1,21 @@
fun main(args : Array<String>) {
var x = 0
class A {
fun bar() {
fun local() {
class B {
fun baz() {
fun local2() {
x++
}
local2()
}
}
B().baz()
}
local()
}
}
A().bar()
println("OK")
}