[JS IR] Fix code clean-up optimizations

- Do not create an extra variable/field for in some lowerings
This commit is contained in:
Roman Artemev
2020-03-02 17:04:50 +03:00
committed by romanart
parent 65011f96cc
commit 98aa73ee6b
5 changed files with 92 additions and 74 deletions
@@ -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.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext 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.backend.common.ir.isElseBranch
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement 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.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.types.IrType 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.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid 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 { 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 constTrue get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val constFalse get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false) private val constFalse get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
private val nothingType = context.irBuiltIns.nothingNType
private val unitType = context.irBuiltIns.unitType private val unitType = context.irBuiltIns.unitType
private val unitValue get() = JsIrBuilder.buildGetObjectValue(unitType, context.irBuiltIns.unitClass) private val unitValue get() = JsIrBuilder.buildGetObjectValue(unitType, context.irBuiltIns.unitClass)
@@ -197,7 +197,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
newStatements: MutableList<IrStatement>, newStatements: MutableList<IrStatement>,
declaration: IrDeclarationParent declaration: IrDeclarationParent
): () -> IrExpressionWithCopy { ): () -> IrExpressionWithCopy {
return if (value.isPure(true)) { return if (value.isPure(anyVariable = true, checkFields = false)) {
{ value.deepCopyWithSymbols() as IrExpressionWithCopy } { value.deepCopyWithSymbols() as IrExpressionWithCopy }
} else { } else {
val varDeclaration = JsIrBuilder.buildVar(value.type, declaration, initializer = value) val varDeclaration = JsIrBuilder.buildVar(value.type, declaration, initializer = value)
@@ -7,16 +7,24 @@ package org.jetbrains.kotlin.ir.backend.js.lower.cleanup
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.IrElement 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.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.* 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.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid
class CleanupLowering : BodyLoweringPass { class CleanupLowering : BodyLoweringPass {
private val blockRemover = BlockRemover()
private val codeCleaner = CodeCleaner()
override fun lower(irBody: IrBody, container: IrDeclaration) { override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.acceptVoid(BlockRemover()) // TODO: merge passes together
irBody.acceptVoid(blockRemover)
irBody.acceptVoid(codeCleaner)
} }
} }
@@ -43,3 +51,39 @@ private class BlockRemover: IrElementVisitorVoid {
process(expression) 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()
}
}
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder 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.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
@@ -483,7 +484,9 @@ class StateMachineBuilder(
val newArguments = arrayOfNulls<IrExpression>(arguments.size) val newArguments = arrayOfNulls<IrExpression>(arguments.size)
for ((i, arg) in arguments.withIndex()) { for ((i, arg) in arguments.withIndex()) {
newArguments[i] = if (arg != null && suspendableCount > 0) { newArguments[i] = if (arg.isPure(false)) arg else {
require(arg != null)
if (suspendableCount > 0) {
if (arg in suspendableNodes) suspendableCount-- if (arg in suspendableNodes) suspendableCount--
arg.acceptVoid(this) arg.acceptVoid(this)
val irVar = tempVar(arg.type, "ARGUMENT") val irVar = tempVar(arg.type, "ARGUMENT")
@@ -492,7 +495,8 @@ class StateMachineBuilder(
} }
JsIrBuilder.buildGetValue(irVar.symbol) JsIrBuilder.buildGetValue(irVar.symbol)
} else { } else {
arg?.deepCopyWithSymbols(function.owner) arg.deepCopyWithSymbols(function.owner)
}
} }
} }
@@ -10,10 +10,7 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.*
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.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
@@ -45,8 +42,12 @@ fun List<IrExpression>.toJsArrayLiteral(context: JsIrBackendContext, arrayType:
} }
} }
// TODO: support more cases like built-in operator call, getField and so on // TODO: support more cases like built-in operator call and so on
fun IrStatement.isPure(anyVariable: Boolean): Boolean {
fun IrExpression?.isPure(anyVariable: Boolean, checkFields: Boolean = true): Boolean {
if (this == null) return true
fun IrExpression.isPureImpl(): Boolean {
return when (this) { return when (this) {
is IrConst<*> -> true is IrConst<*> -> true
is IrGetValue -> { is IrGetValue -> {
@@ -59,3 +60,19 @@ fun IrStatement.isPure(anyVariable: Boolean): Boolean {
else -> false 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
}