Generate better code for branches based on comparisons.
For comparison intrinsics and for instanceof checks, make
it possible to get the the stack value produced and branch
on that directly instead of materializing a boolean to
branch on from it.
That reduces code such as
```
IF_CMPEQ L1
CONST_0
GOTO L2
L1: CONST_1
L2: IFEQ L3
```
to just one IF_CMP instruction.
This commit is contained in:
+42
-8
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.ComparisonIntrinsic
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicFunction
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
|
||||
@@ -717,16 +718,18 @@ class ExpressionCodegen(
|
||||
private fun genConditionWithOptimizationsIfPossible(branch: IrBranch, data: BlockInfo, elseLabel: Label) {
|
||||
var condition = branch.condition
|
||||
var jumpIfFalse = true
|
||||
|
||||
// Instead of materializing a negated value when used for control flow, flip the branch
|
||||
// targets instead. This significantly cuts down the amount of branches and loads of
|
||||
// const_0 and const_1 in the generated java bytecode.
|
||||
if (isNegation(condition, classCodegen.context)) {
|
||||
// Instead of materializing a negated value when used for control flow, flip the branch
|
||||
// targets instead. This significantly cuts down the amount of branches and loads of
|
||||
// const_0 and const_1 in the generated java bytecode.
|
||||
condition = negationArgument(condition as IrCall)
|
||||
jumpIfFalse = false
|
||||
}
|
||||
|
||||
// Do not materialize null constants to check for null. Instead use the JVM bytecode
|
||||
// ifnull and ifnonnull instructions.
|
||||
if (isNullCheck(condition)) {
|
||||
// Do not materialize null constants to check for null. Instead use the JVM bytecode
|
||||
// ifnull and ifnonnull instructions.
|
||||
val call = condition as IrCall
|
||||
val left = call.getValueArgument(0)!!
|
||||
val right = call.getValueArgument(1)!!
|
||||
@@ -737,10 +740,41 @@ class ExpressionCodegen(
|
||||
} else {
|
||||
mv.ifnull(elseLabel)
|
||||
}
|
||||
} else {
|
||||
gen(condition, data).put(condition.asmType, mv)
|
||||
BranchedValue.condJump(onStack(condition.asmType), elseLabel, jumpIfFalse, mv)
|
||||
return
|
||||
}
|
||||
|
||||
// For comparison intrinsics, branch directly based on the comparison instead of
|
||||
// materializing a boolean and performing and extra jump.
|
||||
if (condition is IrCall) {
|
||||
val intrinsic = intrinsics.getIntrinsic(condition.descriptor.original as CallableMemberDescriptor)
|
||||
if (intrinsic is ComparisonIntrinsic) {
|
||||
val callable = resolveToCallable(condition, false)
|
||||
(callable as IrIntrinsicFunction).loadArguments(this, data)
|
||||
val stackValue = intrinsic.genStackValue(condition, classCodegen.context)
|
||||
BranchedValue.condJump(stackValue, elseLabel, jumpIfFalse, mv)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// For instance of type operators, branch directly on the instanceof result instead
|
||||
// of materializing a boolean and performing an extra jump.
|
||||
if (condition is IrTypeOperatorCall &&
|
||||
(condition.operator == IrTypeOperator.NOT_INSTANCEOF || condition.operator == IrTypeOperator.INSTANCEOF)) {
|
||||
val asmType = condition.typeOperand.toKotlinType().asmType
|
||||
gen(condition.argument, OBJECT_TYPE, data)
|
||||
val type = boxType(asmType)
|
||||
generateIsCheck(mv, condition.typeOperand.toKotlinType(), type, state.languageVersionSettings.isReleaseCoroutines())
|
||||
val stackValue =
|
||||
if (condition.operator == IrTypeOperator.INSTANCEOF)
|
||||
onStack(Type.BOOLEAN_TYPE)
|
||||
else
|
||||
StackValue.not(onStack(Type.BOOLEAN_TYPE))
|
||||
BranchedValue.condJump(stackValue, elseLabel, jumpIfFalse, mv)
|
||||
return
|
||||
}
|
||||
|
||||
gen(condition, data).put(condition.asmType, mv)
|
||||
BranchedValue.condJump(onStack(condition.asmType), elseLabel, jumpIfFalse, mv)
|
||||
}
|
||||
|
||||
private fun isNullCheck(expression: IrExpression): Boolean {
|
||||
|
||||
+13
-9
@@ -63,7 +63,18 @@ class CompareTo : IntrinsicMethod() {
|
||||
class PrimitiveComparison(
|
||||
private val primitiveNumberType: KotlinType,
|
||||
private val operatorToken: KtSingleValueToken
|
||||
) : IntrinsicMethod() {
|
||||
) : IntrinsicMethod(), ComparisonIntrinsic {
|
||||
|
||||
override fun genStackValue(expression: IrMemberAccessExpression, context: JvmBackendContext): StackValue {
|
||||
val parameterType = context.state.typeMapper.mapType(primitiveNumberType)
|
||||
|
||||
return StackValue.cmp(
|
||||
operatorToken,
|
||||
parameterType,
|
||||
StackValue.onStack(parameterType, primitiveNumberType),
|
||||
StackValue.onStack(parameterType, primitiveNumberType)
|
||||
)
|
||||
}
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrMemberAccessExpression,
|
||||
@@ -74,14 +85,7 @@ class PrimitiveComparison(
|
||||
|
||||
return object : IrIntrinsicFunction(expression, signature, context, listOf(parameterType, parameterType)) {
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
StackValue
|
||||
.cmp(
|
||||
operatorToken,
|
||||
parameterType,
|
||||
StackValue.onStack(parameterType, primitiveNumberType),
|
||||
StackValue.onStack(parameterType, primitiveNumberType)
|
||||
)
|
||||
.put(Type.BOOLEAN_TYPE, v)
|
||||
genStackValue(expression, context).put(Type.BOOLEAN_TYPE, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,12 @@ import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||
class Equals(val operator: IElementType) : IntrinsicMethod(), ComparisonIntrinsic {
|
||||
|
||||
override fun toCallable(
|
||||
private fun argumentTypes(
|
||||
expression: IrMemberAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
): Pair<Type, Type> {
|
||||
val receiverAndArgs = expression.receiverAndArgs().apply {
|
||||
assert(size == 2) { "Equals expects 2 arguments, but ${joinToString()}" }
|
||||
}
|
||||
@@ -40,18 +39,34 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||
leftType = boxType(leftType)
|
||||
rightType = boxType(rightType)
|
||||
}
|
||||
return Pair(leftType, rightType)
|
||||
}
|
||||
|
||||
override fun genStackValue(
|
||||
expression: IrMemberAccessExpression,
|
||||
context: JvmBackendContext
|
||||
): StackValue {
|
||||
val (leftType, rightType) = argumentTypes(expression, context)
|
||||
val opToken = expression.origin
|
||||
return if (opToken === IrStatementOrigin.EQEQEQ || opToken === IrStatementOrigin.EXCLEQEQ) {
|
||||
// TODO: always casting to the type of the left operand in case of primitives looks wrong
|
||||
val operandType = if (isPrimitive(leftType)) leftType else OBJECT_TYPE
|
||||
StackValue.cmp(operator, operandType, StackValue.onStack(leftType), StackValue.onStack(rightType))
|
||||
} else {
|
||||
genEqualsForExpressionsOnStack(operator, StackValue.onStack(leftType), StackValue.onStack(rightType))
|
||||
}
|
||||
}
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrMemberAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
var (leftType, rightType) = argumentTypes(expression, context)
|
||||
|
||||
return object : IrIntrinsicFunction(expression, signature, context, listOf(leftType, rightType)) {
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
val opToken = expression.origin
|
||||
|
||||
val value = if (opToken === IrStatementOrigin.EQEQEQ || opToken === IrStatementOrigin.EXCLEQEQ) {
|
||||
// TODO: always casting to the type of the left operand in case of primitives looks wrong
|
||||
val operandType = if (isPrimitive(leftType)) leftType else OBJECT_TYPE
|
||||
StackValue.cmp(operator, operandType, StackValue.onStack(leftType), StackValue.onStack(rightType))
|
||||
} else {
|
||||
genEqualsForExpressionsOnStack(operator, StackValue.onStack(leftType), StackValue.onStack(rightType))
|
||||
}
|
||||
val value = genStackValue(expression, context)
|
||||
value.put(Type.BOOLEAN_TYPE, v)
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.Callable
|
||||
import org.jetbrains.kotlin.codegen.CallableMethod
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
@@ -15,6 +16,10 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
|
||||
interface ComparisonIntrinsic {
|
||||
fun genStackValue(expression: IrMemberAccessExpression, context: JvmBackendContext): StackValue
|
||||
}
|
||||
|
||||
abstract class IntrinsicMethod {
|
||||
|
||||
open fun toCallable(
|
||||
|
||||
+8
-1
@@ -86,6 +86,14 @@ open class IrIntrinsicFunction(
|
||||
}
|
||||
|
||||
open fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||
loadArguments(codegen, data)
|
||||
return StackValue.onStack(genInvokeInstructionWithResult(v))
|
||||
}
|
||||
|
||||
fun loadArguments(
|
||||
codegen: ExpressionCodegen,
|
||||
data: BlockInfo
|
||||
) {
|
||||
val args = listOfNotNull(expression.dispatchReceiver, expression.extensionReceiver) +
|
||||
expression.descriptor.valueParameters.mapIndexed { i, descriptor ->
|
||||
expression.getValueArgument(i) ?: if (descriptor.isVararg)
|
||||
@@ -104,7 +112,6 @@ open class IrIntrinsicFunction(
|
||||
genArg(irExpression, codegen, i, data)
|
||||
}
|
||||
}
|
||||
return StackValue.onStack(genInvokeInstructionWithResult(v))
|
||||
}
|
||||
|
||||
open fun genArg(expression: IrExpression, codegen: ExpressionCodegen, index: Int, data: BlockInfo) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
val a = 1
|
||||
|
||||
fun main() {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
val a = 1
|
||||
|
||||
fun main() {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
val p: Int? = 1;
|
||||
val z: Int? = 2;
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
val p: Int? = 1;
|
||||
val z: Int? = 2;
|
||||
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun test(x: String?) {
|
||||
if (x !is String) return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user