Optimize JVM byte code generation for conditional conjunction

Implement an intrinsic method for boolean.and operation, and replace
ANDAND condition with a call to such intrinsic method.
This commit is contained in:
Jiaxiang Chen
2019-04-02 13:47:27 -07:00
committed by max-kammerer
parent 53493657ff
commit 4b99d85322
18 changed files with 187 additions and 12 deletions
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.intrinsics
import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.org.objectweb.asm.Label
object AndAnd : IntrinsicMethod() {
private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, val codegen: ExpressionCodegen, val data: BlockInfo) : BooleanValue(codegen.mv) {
override fun jumpIfFalse(target: Label) {
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
arg1.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
}
override fun jumpIfTrue(target: Label) {
val stayLabel = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(stayLabel)
arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
mv.visitLabel(stayLabel)
}
}
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
val (left, right) = expression.receiverAndArgs()
return BooleanConjunction(left, right, codegen, data)
}
}
@@ -31,6 +31,10 @@ class IrIntrinsicMethods(irBuiltIns: IrBuiltIns) {
val intrinsics = IntrinsicMethods()
val andandSymbol = irBuiltIns.run { defineOperator(OperatorNames.ANDAND, bool, listOf(bool, bool)) }
private val andand = andandSymbol.descriptor
private val irMapping = hashMapOf<CallableMemberDescriptor, IntrinsicMethod>()
private fun createPrimitiveComparisonIntrinsics(typeToIrFun: Map<SimpleType, IrSimpleFunctionSymbol>, operator: KtSingleValueToken) {
@@ -55,6 +59,8 @@ class IrIntrinsicMethods(irBuiltIns: IrBuiltIns) {
irMapping[irBuiltIns.noWhenBranchMatchedException] = IrNoWhenBranchMatchedException()
irMapping[irBuiltIns.illegalArgumentException] = IrIllegalArgumentException()
irMapping[irBuiltIns.throwNpe] = ThrowNPE()
irMapping[andand] = AndAnd
}
fun getIntrinsic(descriptor: CallableMemberDescriptor): IntrinsicMethod? {
@@ -64,4 +70,8 @@ class IrIntrinsicMethods(irBuiltIns: IrBuiltIns) {
}
return irMapping[descriptor.original]
}
private object OperatorNames {
const val ANDAND = "ANDAND"
}
}
@@ -15,13 +15,12 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.types.isBoolean
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded
import org.jetbrains.kotlin.ir.util.isFalseConst
import org.jetbrains.kotlin.ir.util.isNullConst
import org.jetbrains.kotlin.ir.util.isTrueConst
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -102,6 +101,25 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower
expression.branches.removeIf() {
it.condition.isFalseConst() && isCompilerGenerated
}
if (expression.origin == IrStatementOrigin.ANDAND) {
assert(expression.type.isBoolean()
&& expression.branches.size == 2
&& expression.branches[1].condition.isTrueConst()
&& expression.branches[1].result.isFalseConst()) {
"ANDAND condition should have an 'if true then false' body on its second branch. " +
"Failing expression: ${expression.dump()}"
}
// Replace conjunction condition with intrinsic "and" function call
return IrCallImpl(
expression.startOffset,
expression.endOffset,
context.irBuiltIns.booleanType,
context.irIntrinsics.andandSymbol
).apply {
dispatchReceiver = expression.branches[0].condition
putValueArgument(0, expression.branches[0].result)
}
}
// 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
// entirely and replace it with an empty block.