JVM_IR: defer some branch optimizations to codegen.

Specifically, defer the removal of hand-written "if (true|false)" from
JvmBuiltinOptimizationLowering into codegen so that appropriate debug
info (and a NOP) can be inserted.

Change-Id: Ia11af05ad8b4251946bd3e685fb7c3319f0f433f
This commit is contained in:
Ting-Yuan Huang
2019-03-01 11:30:27 -08:00
committed by max-kammerer
parent 6bbb0269b1
commit 013ad4b8e4
17 changed files with 272 additions and 20 deletions
@@ -39,9 +39,7 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isMarkedNullable
import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.isNullConst
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass
@@ -694,31 +692,52 @@ class ExpressionCodegen(
}
private fun genIfWithBranches(branch: IrBranch, data: BlockInfo, type: KotlinType, otherBranches: List<IrBranch>): StackValue {
// True or false conditions known at compile time need not be generated.
val shouldGenerateCondition = !branch.condition.isFalseConst() && !branch.condition.isTrueConst()
// Body of an always-false-condition need not be generated.
val shouldGenerateBody = !branch.condition.isFalseConst()
// Don't generate the tail if it doesn't exist or isn't reachable.
val shouldGenerateTail = !otherBranches.isEmpty() && !branch.condition.isTrueConst()
val elseLabel = Label()
val thenBranch = branch.result
//TODO don't generate condition for else branch - java verifier fails with empty stack
val elseBranch = branch is IrElseBranch
if (!elseBranch) {
val endLabel = Label()
if (shouldGenerateCondition) {
genConditionWithOptimizationsIfPossible(branch, data, elseLabel)
} else {
// Even when a condition isn't generated, a linenumber and nop is still required so that a debugger can break on the line of the
// condition, except for the explicit "else".
if (branch !is IrElseBranch) {
branch.condition.markLineNumber(startOffset = true)
mv.nop()
}
}
val end = Label()
val result = thenBranch.run {
val stackValue = gen(this, data)
coerceNotToUnit(stackValue.type, stackValue.kotlinType, type)
val resultFromBody = if (shouldGenerateBody) {
val thenBranch = branch.result
val result = thenBranch.run {
val stackValue = gen(this, data)
coerceNotToUnit(stackValue.type, stackValue.kotlinType, type)
}
mv.goTo(endLabel)
mv.mark(elseLabel)
result
} else {
none()
}
mv.goTo(end)
mv.mark(elseLabel)
if (!otherBranches.isEmpty()) {
val resultFromTail = if (shouldGenerateTail) {
val nextBranch = otherBranches.first()
genIfWithBranches(nextBranch, data, type, otherBranches.drop(1))
} else {
none()
}
mv.mark(end)
return result
// endLabel is only used to jump from end-of-then-body to the end of the whole if cascade.
if (shouldGenerateBody)
mv.mark(endLabel)
return if (shouldGenerateBody) resultFromBody else resultFromTail
}
private fun genConditionWithOptimizationsIfPossible(branch: IrBranch, data: BlockInfo, elseLabel: Label) {
@@ -109,10 +109,11 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower
}
override fun visitWhen(expression: IrWhen): IrExpression {
val isCompilerGenerated = expression.origin == null
expression.transformChildrenVoid(this)
// Remove all branches with constant false condition.
expression.branches.removeIf() {
it.condition.isFalseConst()
it.condition.isFalseConst() && isCompilerGenerated
}
// If the only condition that is left has a constant true condition remove the
// when in favor of the result. If there are no conditions left, remove the when
@@ -120,7 +121,7 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower
return if (expression.branches.size == 0) {
IrBlockImpl(expression.startOffset, expression.endOffset, context.irBuiltIns.unitType)
} else {
expression.branches.first().takeIf { it.condition.isTrueConst() }?.result ?: expression
expression.branches.first().takeIf { it.condition.isTrueConst() && isCompilerGenerated }?.result ?: expression
}
}