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 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 val irMapping = hashMapOf<CallableMemberDescriptor, IntrinsicMethod>()
|
||||||
|
|
||||||
private fun createPrimitiveComparisonIntrinsics(typeToIrFun: Map<SimpleType, IrSimpleFunctionSymbol>, operator: KtSingleValueToken) {
|
private fun createPrimitiveComparisonIntrinsics(typeToIrFun: Map<SimpleType, IrSimpleFunctionSymbol>, operator: KtSingleValueToken) {
|
||||||
@@ -55,6 +59,8 @@ class IrIntrinsicMethods(irBuiltIns: IrBuiltIns) {
|
|||||||
irMapping[irBuiltIns.noWhenBranchMatchedException] = IrNoWhenBranchMatchedException()
|
irMapping[irBuiltIns.noWhenBranchMatchedException] = IrNoWhenBranchMatchedException()
|
||||||
irMapping[irBuiltIns.illegalArgumentException] = IrIllegalArgumentException()
|
irMapping[irBuiltIns.illegalArgumentException] = IrIllegalArgumentException()
|
||||||
irMapping[irBuiltIns.throwNpe] = ThrowNPE()
|
irMapping[irBuiltIns.throwNpe] = ThrowNPE()
|
||||||
|
|
||||||
|
irMapping[andand] = AndAnd
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIntrinsic(descriptor: CallableMemberDescriptor): IntrinsicMethod? {
|
fun getIntrinsic(descriptor: CallableMemberDescriptor): IntrinsicMethod? {
|
||||||
@@ -64,4 +70,8 @@ class IrIntrinsicMethods(irBuiltIns: IrBuiltIns) {
|
|||||||
}
|
}
|
||||||
return irMapping[descriptor.original]
|
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.declarations.IrVariable
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
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.expressions.impl.IrConstImpl
|
||||||
|
import org.jetbrains.kotlin.ir.types.isBoolean
|
||||||
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
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.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
@@ -102,6 +101,25 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower
|
|||||||
expression.branches.removeIf() {
|
expression.branches.removeIf() {
|
||||||
it.condition.isFalseConst() && isCompilerGenerated
|
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
|
// 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
|
// when in favor of the result. If there are no conditions left, remove the when
|
||||||
// entirely and replace it with an empty block.
|
// 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 a = false
|
||||||
val b = false
|
val b = false
|
||||||
val c = 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 a = false
|
||||||
val b = false
|
val b = false
|
||||||
val c = 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");
|
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")
|
@TestMetadata("prefixIncDec.kt")
|
||||||
public void testPrefixIncDec() throws Exception {
|
public void testPrefixIncDec() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
|
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);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/conditions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("conjuction.kt")
|
@TestMetadata("conjunction.kt")
|
||||||
public void testConjuction() throws Exception {
|
public void testConjunction() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/conditions/conjuction.kt");
|
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")
|
@TestMetadata("disjunction.kt")
|
||||||
|
|||||||
+5
@@ -13534,6 +13534,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/intrinsics/longRangeWithExplicitDot.kt");
|
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")
|
@TestMetadata("prefixIncDec.kt")
|
||||||
public void testPrefixIncDec() throws Exception {
|
public void testPrefixIncDec() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
|
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
|
||||||
|
|||||||
@@ -181,6 +181,11 @@ public class LineNumberTestGenerated extends AbstractLineNumberTest {
|
|||||||
runTest("compiler/testData/lineNumber/custom/ifThenElse.kt");
|
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")
|
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
|
||||||
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
|
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
|
||||||
runTest("compiler/testData/lineNumber/custom/inTheEndOfLambdaArgumentOfInlineCall.kt");
|
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");
|
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")
|
@TestMetadata("prefixIncDec.kt")
|
||||||
public void testPrefixIncDec() throws Exception {
|
public void testPrefixIncDec() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
|
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);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/conditions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("conjuction.kt")
|
@TestMetadata("conjunction.kt")
|
||||||
public void testConjuction() throws Exception {
|
public void testConjunction() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/conditions/conjuction.kt");
|
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")
|
@TestMetadata("disjunction.kt")
|
||||||
|
|||||||
+5
@@ -181,6 +181,11 @@ public class IrLineNumberTestGenerated extends AbstractIrLineNumberTest {
|
|||||||
runTest("compiler/testData/lineNumber/custom/ifThenElse.kt");
|
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")
|
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
|
||||||
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
|
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
|
||||||
runTest("compiler/testData/lineNumber/custom/inTheEndOfLambdaArgumentOfInlineCall.kt");
|
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");
|
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")
|
@TestMetadata("prefixIncDec.kt")
|
||||||
public void testPrefixIncDec() throws Exception {
|
public void testPrefixIncDec() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
|
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");
|
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")
|
@TestMetadata("prefixIncDec.kt")
|
||||||
public void testPrefixIncDec() throws Exception {
|
public void testPrefixIncDec() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
|
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user