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.
@@ -0,0 +1,18 @@
var s = ""
fun o(): Boolean {
s += "O"
return false
}
fun k(): Boolean {
s += "K"
return true
}
fun box(): String {
val b = o() and k()
if (b)
return "fail: b should be false"
return s
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
val a = false
val b = false
val c = false
@@ -0,0 +1,16 @@
val a = false
val b = false
val c = false
fun main() {
do {
"loop"
} while (a && b && c)
}
// 0 ICONST_0
// 0 ICONST_1
// 2 IFEQ
// 1 IFNE
// 3 IF
// 0 GOTO
@@ -0,0 +1,16 @@
val a = false
val b = false
val c = false
fun main() {
while (a && b && c) {
"loop"
}
}
// 0 ICONST_0
// 0 ICONST_1
// 3 IFEQ
// 0 IFNE
// 3 IF
// 1 GOTO
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
val a = false
val b = false
val c = false
+10
View File
@@ -0,0 +1,10 @@
fun cond() = false
fun foo() {
if (cond())
cond()
else
false
}
// 1 4 5 7 8
@@ -13534,6 +13534,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/intrinsics/longRangeWithExplicitDot.kt");
}
@TestMetadata("nonShortCircuitAnd.kt")
public void testNonShortCircuitAnd() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/nonShortCircuitAnd.kt");
}
@TestMetadata("prefixIncDec.kt")
public void testPrefixIncDec() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
@@ -894,9 +894,19 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/conditions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("conjuction.kt")
public void testConjuction() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/conditions/conjuction.kt");
@TestMetadata("conjunction.kt")
public void testConjunction() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/conditions/conjunction.kt");
}
@TestMetadata("conjunctionInDoWhile.kt")
public void testConjunctionInDoWhile() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/conditions/conjunctionInDoWhile.kt");
}
@TestMetadata("conjunctionInWhile.kt")
public void testConjunctionInWhile() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/conditions/conjunctionInWhile.kt");
}
@TestMetadata("disjunction.kt")
@@ -13534,6 +13534,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/intrinsics/longRangeWithExplicitDot.kt");
}
@TestMetadata("nonShortCircuitAnd.kt")
public void testNonShortCircuitAnd() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/nonShortCircuitAnd.kt");
}
@TestMetadata("prefixIncDec.kt")
public void testPrefixIncDec() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
@@ -181,6 +181,11 @@ public class LineNumberTestGenerated extends AbstractLineNumberTest {
runTest("compiler/testData/lineNumber/custom/ifThenElse.kt");
}
@TestMetadata("ifThenElseFalse.kt")
public void testIfThenElseFalse() throws Exception {
runTest("compiler/testData/lineNumber/custom/ifThenElseFalse.kt");
}
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
runTest("compiler/testData/lineNumber/custom/inTheEndOfLambdaArgumentOfInlineCall.kt");
@@ -13539,6 +13539,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/intrinsics/longRangeWithExplicitDot.kt");
}
@TestMetadata("nonShortCircuitAnd.kt")
public void testNonShortCircuitAnd() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/nonShortCircuitAnd.kt");
}
@TestMetadata("prefixIncDec.kt")
public void testPrefixIncDec() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
@@ -894,9 +894,19 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/conditions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
}
@TestMetadata("conjuction.kt")
public void testConjuction() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/conditions/conjuction.kt");
@TestMetadata("conjunction.kt")
public void testConjunction() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/conditions/conjunction.kt");
}
@TestMetadata("conjunctionInDoWhile.kt")
public void testConjunctionInDoWhile() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/conditions/conjunctionInDoWhile.kt");
}
@TestMetadata("conjunctionInWhile.kt")
public void testConjunctionInWhile() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/conditions/conjunctionInWhile.kt");
}
@TestMetadata("disjunction.kt")
@@ -181,6 +181,11 @@ public class IrLineNumberTestGenerated extends AbstractIrLineNumberTest {
runTest("compiler/testData/lineNumber/custom/ifThenElse.kt");
}
@TestMetadata("ifThenElseFalse.kt")
public void testIfThenElseFalse() throws Exception {
runTest("compiler/testData/lineNumber/custom/ifThenElseFalse.kt");
}
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
runTest("compiler/testData/lineNumber/custom/inTheEndOfLambdaArgumentOfInlineCall.kt");
@@ -10854,6 +10854,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/intrinsics/longRangeWithExplicitDot.kt");
}
@TestMetadata("nonShortCircuitAnd.kt")
public void testNonShortCircuitAnd() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/nonShortCircuitAnd.kt");
}
@TestMetadata("prefixIncDec.kt")
public void testPrefixIncDec() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
@@ -11999,6 +11999,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/intrinsics/longRangeWithExplicitDot.kt");
}
@TestMetadata("nonShortCircuitAnd.kt")
public void testNonShortCircuitAnd() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/nonShortCircuitAnd.kt");
}
@TestMetadata("prefixIncDec.kt")
public void testPrefixIncDec() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");