diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt index 3a5d7af71d0..eb4b01c9a82 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt @@ -7,8 +7,6 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.CommonBackendContext -import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass -import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.ir.isElseBranch import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.ir.IrElement @@ -20,7 +18,6 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -80,49 +77,6 @@ abstract class AbstractBlockDecomposerLowering( } } } - -// irDeclarationContainer.transformChildrenVoid(CodeCleaner()) TODO - } - - private inner class CodeCleaner : IrElementTransformerVoid() { - - private fun IrStatementContainer.cleanUpStatements() { - var unreachable = false - - val newStatements = statements.filter { - when { - unreachable -> false - it.isPure(true) -> false - it is IrGetField -> it.receiver?.let { !it.isPure(true) } ?: false - else -> { - unreachable = it is IrExpression && it.type.isNothing() - true - } - } - } - - statements.clear() - - statements += newStatements - } - - override fun visitBlock(expression: IrBlock): IrExpression { - expression.transformChildrenVoid(this) - expression.cleanUpStatements() - return expression - } - - override fun visitBlockBody(body: IrBlockBody): IrBody { - body.transformChildrenVoid(this) - body.cleanUpStatements() - return body - } - - override fun visitComposite(expression: IrComposite): IrExpression { - expression.transformChildrenVoid(this) - expression.cleanUpStatements() - return expression - } } private fun IrExpressionBody.toBlockBody(containingFunction: IrFunction): IrBlockBody { @@ -146,7 +100,6 @@ class BlockDecomposerTransformer( private val constTrue get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true) private val constFalse get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false) - private val nothingType = context.irBuiltIns.nothingNType private val unitType = context.irBuiltIns.unitType private val unitValue get() = JsIrBuilder.buildGetObjectValue(unitType, context.irBuiltIns.unitClass) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt index 8df332dbc8a..9afcb1b9c9a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt @@ -197,7 +197,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass { newStatements: MutableList, declaration: IrDeclarationParent ): () -> IrExpressionWithCopy { - return if (value.isPure(true)) { + return if (value.isPure(anyVariable = true, checkFields = false)) { { value.deepCopyWithSymbols() as IrExpressionWithCopy } } else { val varDeclaration = JsIrBuilder.buildVar(value.type, declaration, initializer = value) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/cleanup/CleanupLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/cleanup/CleanupLowering.kt index 185038b4121..385d3ae7f4e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/cleanup/CleanupLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/cleanup/CleanupLowering.kt @@ -7,20 +7,28 @@ package org.jetbrains.kotlin.ir.backend.js.lower.cleanup import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.backend.js.utils.isPure import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.util.transformFlat import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid class CleanupLowering : BodyLoweringPass { + + private val blockRemover = BlockRemover() + private val codeCleaner = CodeCleaner() + override fun lower(irBody: IrBody, container: IrDeclaration) { - irBody.acceptVoid(BlockRemover()) + // TODO: merge passes together + irBody.acceptVoid(blockRemover) + irBody.acceptVoid(codeCleaner) } } -private class BlockRemover: IrElementVisitorVoid { +private class BlockRemover : IrElementVisitorVoid { override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) } @@ -42,4 +50,40 @@ private class BlockRemover: IrElementVisitorVoid { process(expression) } +} + +private class CodeCleaner : IrElementVisitorVoid { + + private fun IrStatementContainer.cleanUpStatements() { + var unreachable = false + + val newStatements = statements.filter { + when { + unreachable -> false + it is IrExpression && it.isPure(true) -> false + else -> { + unreachable = it is IrExpression && it.type.isNothing() + true + } + } + } + + statements.clear() + + statements += newStatements + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitBlockBody(body: IrBlockBody) { + super.visitBlockBody(body) + body.cleanUpStatements() + } + + override fun visitContainerExpression(expression: IrContainerExpression) { + super.visitContainerExpression(expression) + expression.cleanUpStatements() + } } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt index 855a7ba7005..099eac3d7d9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.backend.js.utils.isPure import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* @@ -483,16 +484,19 @@ class StateMachineBuilder( val newArguments = arrayOfNulls(arguments.size) for ((i, arg) in arguments.withIndex()) { - newArguments[i] = if (arg != null && suspendableCount > 0) { - if (arg in suspendableNodes) suspendableCount-- - arg.acceptVoid(this) - val irVar = tempVar(arg.type, "ARGUMENT") - transformLastExpression { - irVar.apply { initializer = it } + newArguments[i] = if (arg.isPure(false)) arg else { + require(arg != null) + if (suspendableCount > 0) { + if (arg in suspendableNodes) suspendableCount-- + arg.acceptVoid(this) + val irVar = tempVar(arg.type, "ARGUMENT") + transformLastExpression { + irVar.apply { initializer = it } + } + JsIrBuilder.buildGetValue(irVar.symbol) + } else { + arg.deepCopyWithSymbols(function.owner) } - JsIrBuilder.buildGetValue(irVar.symbol) - } else { - arg?.deepCopyWithSymbols(function.owner) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt index fa9cde588a4..6d13fec0ddb 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt @@ -10,10 +10,7 @@ import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrConst -import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue -import org.jetbrains.kotlin.ir.expressions.IrGetValue -import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.types.IrType @@ -45,17 +42,37 @@ fun List.toJsArrayLiteral(context: JsIrBackendContext, arrayType: } } -// TODO: support more cases like built-in operator call, getField and so on -fun IrStatement.isPure(anyVariable: Boolean): Boolean { - return when (this) { - is IrConst<*> -> true - is IrGetValue -> { - if (anyVariable) return true - val valueDeclaration = symbol.owner - if (valueDeclaration is IrVariable) !valueDeclaration.isVar - else true +// TODO: support more cases like built-in operator call and so on + +fun IrExpression?.isPure(anyVariable: Boolean, checkFields: Boolean = true): Boolean { + if (this == null) return true + + fun IrExpression.isPureImpl(): Boolean { + return when (this) { + is IrConst<*> -> true + is IrGetValue -> { + if (anyVariable) return true + val valueDeclaration = symbol.owner + if (valueDeclaration is IrVariable) !valueDeclaration.isVar + else true + } + is IrGetObjectValue -> type.isUnit() + else -> false } - is IrGetObjectValue -> type.isUnit() - else -> false } + + if (isPureImpl()) return true + + if (!checkFields) return false + + if (this is IrGetField) { + if (!symbol.owner.isFinal) { + if (!anyVariable) { + return false + } + } + return receiver.isPure(anyVariable) + } + + return false } \ No newline at end of file