Make LocalDeclarationsLowering a BodyLoweringPass

This commit is contained in:
Anton Bannykh
2019-12-19 19:22:37 +03:00
parent 1eec2acf43
commit fe236507a0
2 changed files with 140 additions and 102 deletions
@@ -17,9 +17,6 @@
package org.jetbrains.kotlin.backend.common.lower package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.ir.ir2string import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
@@ -29,19 +26,21 @@ import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.isLocal import org.jetbrains.kotlin.ir.util.isLocal
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import kotlin.collections.set
data class Closure(val capturedValues: List<IrValueSymbol>, val capturedTypeParameters: List<IrTypeParameter>) data class Closure(val capturedValues: List<IrValueSymbol>, val capturedTypeParameters: List<IrTypeParameter>)
class ClosureAnnotator(irFile: IrFile) { class ClosureAnnotator(body: IrBody, declaration: IrDeclaration) {
private val closureBuilders = mutableMapOf<IrDeclaration, ClosureBuilder>() private val closureBuilders = mutableMapOf<IrDeclaration, ClosureBuilder>()
init { init {
// Collect all closures for classes and functions. Collect call graph // Collect all closures for classes and functions. Collect call graph
irFile.acceptChildrenVoid(ClosureCollectorVisitor()) if (declaration is IrSimpleFunction && declaration.name.asString() == "arrayIterator") {
1
}
body.accept(ClosureCollectorVisitor(), declaration.closureBuilderOrNull ?: declaration.parentClosureBuilder)
} }
fun getFunctionClosure(declaration: IrFunction) = getClosure(declaration) fun getFunctionClosure(declaration: IrFunction) = getClosure(declaration)
@@ -126,129 +125,150 @@ class ClosureAnnotator(irFile: IrFile) {
} }
} }
private inner class ClosureCollectorVisitor : IrElementVisitorVoid { private fun includeInParent(builder: ClosureBuilder) {
// We don't include functions or classes in a parent function when they are declared.
val closuresStack = mutableListOf<ClosureBuilder>() // Instead we will include them when are is used (use = call for a function or constructor call for a class).
val parentBuilder = builder.owner.parentClosureBuilder
fun includeInParent(builder: ClosureBuilder) { if (parentBuilder != null && parentBuilder.owner !is IrFunction) {
// We don't include functions or classes in a parent function when they are declared. parentBuilder.include(builder)
// 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 IrFunction) {
parentBuilder.include(builder)
}
} }
}
override fun visitElement(element: IrElement) { private val IrClass.closureBuilder: ClosureBuilder
element.acceptChildrenVoid(this) get() = closureBuilders.getOrPut(this) {
} val closureBuilder = ClosureBuilder(this)
override fun visitClass(declaration: IrClass) {
val closureBuilder = ClosureBuilder(declaration)
closureBuilders[declaration] = closureBuilder
collectPotentiallyCapturedTypeParameters(closureBuilder) collectPotentiallyCapturedTypeParameters(closureBuilder)
closureBuilder.declareVariable(declaration.thisReceiver) closureBuilder.declareVariable(this.thisReceiver)
if (declaration.isInner) { if (this.isInner) {
closureBuilder.declareVariable((declaration.parent as IrClass).thisReceiver) closureBuilder.declareVariable((this.parent as IrClass).thisReceiver)
includeInParent(closureBuilder) includeInParent(closureBuilder)
} }
declaration.declarations.firstOrNull { it is IrConstructor && it.isPrimary }?.let { this.declarations.firstOrNull { it is IrConstructor && it.isPrimary }?.let {
val constructor = it as IrConstructor val constructor = it as IrConstructor
constructor.valueParameters.forEach { v -> closureBuilder.declareVariable(v) } constructor.valueParameters.forEach { v -> closureBuilder.declareVariable(v) }
} }
closuresStack.push(closureBuilder) closureBuilder
declaration.acceptChildrenVoid(this)
closuresStack.pop()
} }
override fun visitFunction(declaration: IrFunction) { private val IrFunction.closureBuilder: ClosureBuilder
val closureBuilder = ClosureBuilder(declaration) get() = closureBuilders.getOrPut(this) {
closureBuilders[declaration] = closureBuilder val closureBuilder = ClosureBuilder(this)
collectPotentiallyCapturedTypeParameters(closureBuilder) collectPotentiallyCapturedTypeParameters(closureBuilder)
declaration.valueParameters.forEach { closureBuilder.declareVariable(it) } this.valueParameters.forEach { closureBuilder.declareVariable(it) }
closureBuilder.declareVariable(declaration.dispatchReceiverParameter) closureBuilder.declareVariable(this.dispatchReceiverParameter)
closureBuilder.declareVariable(declaration.extensionReceiverParameter) closureBuilder.declareVariable(this.extensionReceiverParameter)
if (declaration is IrConstructor) { if (this is IrConstructor) {
val constructedClass = (declaration.parent as IrClass) val constructedClass = (this.parent as IrClass)
closureBuilder.declareVariable(constructedClass.thisReceiver) closureBuilder.declareVariable(constructedClass.thisReceiver)
// Include closure of the class in the constructor closure. // Include closure of the class in the constructor closure.
val classBuilder = closuresStack.peek() val classBuilder = constructedClass.closureBuilder
classBuilder?.let { closureBuilder.include(classBuilder)
assert(classBuilder.owner == constructedClass)
closureBuilder.include(classBuilder)
}
} }
closuresStack.push(closureBuilder) closureBuilder
declaration.acceptChildrenVoid(this) }
closuresStack.pop()
private fun collectPotentiallyCapturedTypeParameters(closureBuilder: ClosureBuilder) {
fun ClosureBuilder.doCollect() {
if (owner !is IrClass) {
(owner as? IrTypeParametersContainer)?.let { container ->
for (tp in container.typeParameters) {
closureBuilder.addPotentiallyCapturedTypeParameter(tp)
}
}
owner.parentClosureBuilder?.doCollect()
}
}
closureBuilder.owner.parentClosureBuilder?.doCollect()
}
private val IrDeclaration.parentClosureBuilder: ClosureBuilder?
get() = when (val p = parent) {
is IrClass -> p.closureBuilder
is IrFunction -> p.closureBuilder
is IrDeclaration -> p.parentClosureBuilder
else -> null
}
private val IrDeclaration.closureBuilderOrNull: ClosureBuilder?
get() = when (this) {
is IrClass -> closureBuilder
is IrFunction -> closureBuilder
else -> null
}
private inner class ClosureCollectorVisitor() : IrElementVisitor<Unit, ClosureBuilder?> {
override fun visitElement(element: IrElement, data: ClosureBuilder?) {
element.acceptChildren(this, data)
}
override fun visitClass(declaration: IrClass, data: ClosureBuilder?) {
declaration.acceptChildren(this, declaration.closureBuilder)
}
override fun visitFunction(declaration: IrFunction, data: ClosureBuilder?) {
val closureBuilder = declaration.closureBuilder
declaration.acceptChildren(this, closureBuilder)
includeInParent(closureBuilder) includeInParent(closureBuilder)
} }
override fun visitVariableAccess(expression: IrValueAccessExpression) { override fun visitValueAccess(expression: IrValueAccessExpression, data: ClosureBuilder?) {
closuresStack.peek()?.seeVariable(expression.symbol) data?.seeVariable(expression.symbol)
super.visitVariableAccess(expression) super.visitValueAccess(expression, data)
} }
override fun visitVariable(declaration: IrVariable) { override fun visitVariable(declaration: IrVariable, data: ClosureBuilder?) {
closuresStack.peek()?.declareVariable(declaration) data?.declareVariable(declaration)
super.visitVariable(declaration) super.visitVariable(declaration, data)
} }
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) { override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: ClosureBuilder?) {
super.visitFunctionAccess(expression) super.visitFunctionAccess(expression, data)
processMemberAccess(expression.symbol.owner) processMemberAccess(expression.symbol.owner, data)
} }
override fun visitFunctionReference(expression: IrFunctionReference) { override fun visitFunctionReference(expression: IrFunctionReference, data: ClosureBuilder?) {
super.visitFunctionReference(expression) super.visitFunctionReference(expression, data)
processMemberAccess(expression.symbol.owner) processMemberAccess(expression.symbol.owner, data)
} }
override fun visitPropertyReference(expression: IrPropertyReference) { override fun visitPropertyReference(expression: IrPropertyReference, data: ClosureBuilder?) {
super.visitPropertyReference(expression) super.visitPropertyReference(expression, data)
expression.getter?.let { processMemberAccess(it.owner) } expression.getter?.let { processMemberAccess(it.owner, data) }
expression.setter?.let { processMemberAccess(it.owner) } expression.setter?.let { processMemberAccess(it.owner, data) }
} }
override fun visitExpression(expression: IrExpression) { override fun visitExpression(expression: IrExpression, data: ClosureBuilder?) {
super.visitExpression(expression) super.visitExpression(expression, data)
val typeParameterContainerScopeBuilder = closuresStack.peek()?.let { val typeParameterContainerScopeBuilder = data?.let {
if (it.owner is IrConstructor) { (it.owner as? IrConstructor)?.closureBuilder ?: it
closuresStack[closuresStack.size - 2]
} else it
} }
typeParameterContainerScopeBuilder?.seeType(expression.type) typeParameterContainerScopeBuilder?.seeType(expression.type)
} }
private fun processMemberAccess(declaration: IrDeclaration) { private fun processMemberAccess(declaration: IrDeclaration, parentClosure: ClosureBuilder?) {
if (declaration.isLocal) { if (declaration.isLocal) {
if (declaration is IrSimpleFunction && declaration.visibility != Visibilities.LOCAL) { if (declaration is IrSimpleFunction && declaration.visibility != Visibilities.LOCAL) {
return return
} }
val builder = closureBuilders[declaration] val builder = declaration.closureBuilderOrNull
builder?.let { builder?.let {
closuresStack.peek()?.include(builder) parentClosure?.include(builder)
}
}
}
private fun collectPotentiallyCapturedTypeParameters(closureBuilder: ClosureBuilder) {
closuresStack.takeLastWhile { it.owner !is IrClass }.forEach {
(it.owner as? IrTypeParametersContainer)?.let { container ->
for (tp in container.typeParameters) {
closureBuilder.addPotentiallyCapturedTypeParameter(tp)
}
} }
} }
} }
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.backend.common.lower package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementVisitorVoidWithContext
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.ir.* import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
@@ -43,6 +40,7 @@ import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -95,7 +93,7 @@ class LocalDeclarationsLowering(
val localNameProvider: LocalNameProvider = LocalNameProvider.DEFAULT, val localNameProvider: LocalNameProvider = LocalNameProvider.DEFAULT,
val visibilityPolicy: VisibilityPolicy = VisibilityPolicy.DEFAULT val visibilityPolicy: VisibilityPolicy = VisibilityPolicy.DEFAULT
) : ) :
FileLoweringPass { BodyLoweringPass {
object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE : object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE :
IrDeclarationOriginImpl("FIELD_FOR_CAPTURED_VALUE", isSynthetic = true) IrDeclarationOriginImpl("FIELD_FOR_CAPTURED_VALUE", isSynthetic = true)
@@ -106,10 +104,23 @@ class LocalDeclarationsLowering(
private object STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE : private object STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE :
IrStatementOriginImpl("INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE") IrStatementOriginImpl("INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE")
override fun lower(irFile: IrFile) { override fun lower(irBody: IrBody, container: IrDeclaration) {
LocalDeclarationsTransformer(irFile).lowerLocalDeclarations() LocalDeclarationsTransformer(irBody, container).lowerLocalDeclarations()
} }
private class ScopeWithCounter(scope: Scope, irElement: IrElement) : ScopeWithIr(scope, irElement) {
// Continuous numbering across all declarations in the container.
var counter: Int = 0
}
private val scopeMap: MutableMap<IrSymbolOwner, ScopeWithCounter> = mutableMapOf()
// Need to keep LocalFunctionContext.index
private val IrSymbolOwner.scopeWithCounter: ScopeWithCounter
get() = scopeMap.getOrPut(this) {
ScopeWithCounter(Scope(symbol), this)
}
private abstract class LocalContext { private abstract class LocalContext {
val capturedTypeParameterToTypeParameter: MutableMap<IrTypeParameter, IrTypeParameter> = mutableMapOf() val capturedTypeParameterToTypeParameter: MutableMap<IrTypeParameter, IrTypeParameter> = mutableMapOf()
@@ -203,7 +214,7 @@ class LocalDeclarationsLowering(
abbreviation.annotations abbreviation.annotations
) )
private inner class LocalDeclarationsTransformer(val irFile: IrFile) { private inner class LocalDeclarationsTransformer(val irBody: IrBody, val container: IrDeclaration) {
val localFunctions: MutableMap<IrFunction, LocalFunctionContext> = LinkedHashMap() val localFunctions: MutableMap<IrFunction, LocalFunctionContext> = LinkedHashMap()
val localClasses: MutableMap<IrClass, LocalClassContext> = LinkedHashMap() val localClasses: MutableMap<IrClass, LocalClassContext> = LinkedHashMap()
val localClassConstructors: MutableMap<IrConstructor, LocalClassConstructorContext> = LinkedHashMap() val localClassConstructors: MutableMap<IrConstructor, LocalClassConstructorContext> = LinkedHashMap()
@@ -481,7 +492,7 @@ class LocalDeclarationsLowering(
rewriteClassMembers(it.declaration, it) rewriteClassMembers(it.declaration, it)
} }
rewriteFunctionBody(irFile, null) rewriteFunctionBody(container, null)
} }
private fun createNewCall(oldCall: IrCall, newCallee: IrFunction) = private fun createNewCall(oldCall: IrCall, newCallee: IrFunction) =
@@ -794,7 +805,7 @@ class LocalDeclarationsLowering(
private fun collectClosureForLocalDeclarations() { private fun collectClosureForLocalDeclarations() {
//TODO: maybe use for granular declarations //TODO: maybe use for granular declarations
val annotator = ClosureAnnotator(irFile) val annotator = ClosureAnnotator(irBody, container)
localFunctions.forEach { (declaration, context) -> localFunctions.forEach { (declaration, context) ->
context.closure = annotator.getFunctionClosure(declaration) context.closure = annotator.getFunctionClosure(declaration)
@@ -806,19 +817,25 @@ class LocalDeclarationsLowering(
} }
private fun collectLocalDeclarations() { private fun collectLocalDeclarations() {
class ScopeWithCounter(scope: Scope, irElement: IrElement) : ScopeWithIr(scope, irElement) { val enclosingFileScope = container.file.scopeWithCounter
// Continuous numbering across all declarations in the container.
var counter: Int = 0
}
irFile.acceptVoid(object : IrElementVisitorVoidWithContext() { val enclosingClassScope = run {
var currentParent = container as? IrClass ?: container.parent
while (currentParent is IrDeclaration && currentParent !is IrClass) {
currentParent = currentParent.parent
}
currentParent as? IrClass
}?.scopeWithCounter
irBody.acceptVoid(object : IrElementVisitorVoidWithContext() {
override fun visitElement(element: IrElement) { override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this) element.acceptChildrenVoid(this)
} }
override fun createScope(declaration: IrSymbolOwner): ScopeWithIr { override fun createScope(declaration: IrSymbolOwner): ScopeWithIr {
return ScopeWithCounter(Scope(declaration.symbol), declaration) return ScopeWithCounter(Scope(declaration.symbol), declaration) // Don't cache local declarations
} }
override fun visitSimpleFunction(declaration: IrSimpleFunction) { override fun visitSimpleFunction(declaration: IrSimpleFunction) {
@@ -826,7 +843,7 @@ class LocalDeclarationsLowering(
if (declaration.visibility == Visibilities.LOCAL) { if (declaration.visibility == Visibilities.LOCAL) {
val scopeWithIr = val scopeWithIr =
(currentClass ?: currentFile /*file is required for K/N cause file declarations are not split by classes*/ (currentClass ?: enclosingClassScope ?: enclosingFileScope /*file is required for K/N cause file declarations are not split by classes*/
?: error("No scope for ${declaration.dump()}")) ?: error("No scope for ${declaration.dump()}"))
localFunctions[declaration] = localFunctions[declaration] =
LocalFunctionContext( LocalFunctionContext(
@@ -858,7 +875,8 @@ class LocalDeclarationsLowering(
} }
private val inInlineFunctionScope: Boolean private val inInlineFunctionScope: Boolean
get() = allScopes.any { scope -> (scope.irElement as? IrFunction)?.isInline ?: false } get() = allScopes.any { scope -> (scope.irElement as? IrFunction)?.isInline ?: false } ||
generateSequence(container) { it.parent as? IrDeclaration }.any { it is IrFunction && it.isInline }
}) })
} }
} }