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:
committed by
max-kammerer
parent
53493657ff
commit
4b99d85322
@@ -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)
|
||||
}
|
||||
}
|
||||
+10
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
+22
-4
@@ -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
@@ -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
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
fun cond() = false
|
||||
|
||||
fun foo() {
|
||||
if (cond())
|
||||
cond()
|
||||
else
|
||||
false
|
||||
}
|
||||
|
||||
// 1 4 5 7 8
|
||||
+5
@@ -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");
|
||||
|
||||
+13
-3
@@ -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")
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+13
-3
@@ -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")
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
Generated
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user