diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 8c9224fac46..cafe0c14140 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -394,7 +394,7 @@ val jvmPhases = NamedCompilerPhase( generateMultifileFacadesPhase then resolveInlineCallsPhase then // should be last transformation - removeDeclarationsThatWouldBeInlined then + prepareForBytecodeInlining then validateIrAfterLowering ) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt index 14104b2fe10..70f4b80a811 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt @@ -167,5 +167,4 @@ private tailrec fun IrDeclaration.isInlineOrInsideInline(): Boolean { return parent.isInlineOrInsideInline() } -// TODO generate better labels; this is unique (includes the object's address), but not very descriptive -internal fun IrLoop.nonLocalReturnLabel(forBreak: Boolean): String = "$this\$${if (forBreak) "break" else "continue"}" +internal fun IrLoop.nonLocalReturnLabel(forBreak: Boolean): String = "${label!!}\$${if (forBreak) "break" else "continue"}" diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BytecodeInliningPreparationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BytecodeInliningPreparationLowering.kt new file mode 100644 index 00000000000..5f023370765 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BytecodeInliningPreparationLowering.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrFunctionReference +import org.jetbrains.kotlin.ir.expressions.IrLoop +import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor + +internal val prepareForBytecodeInlining = makeIrModulePhase( + ::BytecodeInliningPreparationLowering, + name = "BytecodeInliningPreparation", + description = "Remove inline lambda declarations and label all loops" +) + +private class BytecodeInliningPreparationLowering(val context: JvmBackendContext) : FileLoweringPass { + override fun lower(irFile: IrFile) { + val loweredLambdasToDelete = mutableSetOf() + irFile.accept(object : IrElementVisitor { + // This counter is intentionally not local to every declaration because their names might clash. + private var counter = 0 + + override fun visitElement(element: IrElement, data: String) = + element.acceptChildren(this, if (element is IrDeclarationWithName) "$data${element.name}$" else data) + + override fun visitLoop(loop: IrLoop, data: String) { + // Give all loops unique labels so that we can generate unambiguous instructions for non-local + // `break`/`continue` statements. + loop.label = "$data${++counter}" + super.visitLoop(loop, data) + } + + override fun visitFunctionReference(expression: IrFunctionReference, data: String) { + // Remove inline lambdas from their declaration parents. They should not appear in the output + // bytecode in non-inlined form. + if (expression.origin.isInlineIrExpression()) { + loweredLambdasToDelete.add(expression.symbol.owner) + } + super.visitFunctionReference(expression, data) + } + }, "") + + for (irClass in loweredLambdasToDelete.mapTo(mutableSetOf()) { it.parentAsClass }) { + irClass.declarations.removeAll(loweredLambdasToDelete) + } + } +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RemoveDeclarationsThatWouldBeInlinedLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RemoveDeclarationsThatWouldBeInlinedLowering.kt deleted file mode 100644 index c3637e0a17f..00000000000 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RemoveDeclarationsThatWouldBeInlinedLowering.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.backend.jvm.lower - -import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase -import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.expressions.IrFunctionReference -import org.jetbrains.kotlin.ir.visitors.* - -internal val removeDeclarationsThatWouldBeInlined = makeIrModulePhase( - ::RemoveDeclarationsThatWouldBeInlinedLowering, - name = "RemoveInlinedDeclarations", - description = "Rename declaration that should be inlined" -) - -// Removes all functions which are only used as arguments to inline functions. It's -// important that this phase runs right before codegen, since we need the bodies of lambdas to -// be lowered for inline codegen. Conversely, since this phase runs right before codegen we can -// assume that all remaining function references are only used as arguments to inline functions - -// otherwise they would have been lowered. -private class RemoveDeclarationsThatWouldBeInlinedLowering(val context: JvmBackendContext) : FileLoweringPass { - override fun lower(irFile: IrFile) { - val loweredLambdasToDelete = mutableSetOf() - - irFile.acceptVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) = element.acceptChildrenVoid(this) - - override fun visitFunctionReference(expression: IrFunctionReference) { - if (expression.origin.isInlineIrExpression()) { - loweredLambdasToDelete.add(expression.symbol.owner) - } - - expression.acceptChildrenVoid(this) - } - }) - - irFile.transformChildrenVoid(object : IrElementTransformerVoid() { - override fun visitClass(declaration: IrClass): IrStatement { - return super.visitClass(declaration).also { - declaration.declarations.removeAll(loweredLambdasToDelete) - } - } - }) - } -}