JVM IR: Optimize equality on class literals

This commit is contained in:
Steven Schäfer
2022-07-27 20:55:45 +02:00
committed by Alexander Udalov
parent 3a1d83e7c5
commit a90c4d5dd5
15 changed files with 199 additions and 31 deletions
@@ -1390,21 +1390,22 @@ class ExpressionCodegen(
return MaterialValue(this@ExpressionCodegen, JAVA_STRING_TYPE, context.irBuiltIns.stringType)
}
override fun visitGetClass(expression: IrGetClass, data: BlockInfo) =
generateClassLiteralReference(expression, true, data)
override fun visitGetClass(expression: IrGetClass, data: BlockInfo): PromisedValue =
generateClassLiteralReference(expression, wrapIntoKClass = true, wrapPrimitives = false, data = data)
override fun visitClassReference(expression: IrClassReference, data: BlockInfo) =
generateClassLiteralReference(expression, true, data)
override fun visitClassReference(expression: IrClassReference, data: BlockInfo): PromisedValue =
generateClassLiteralReference(expression, wrapIntoKClass = true, wrapPrimitives = false, data = data)
fun generateClassLiteralReference(
classReference: IrExpression,
wrapIntoKClass: Boolean,
wrapPrimitives: Boolean,
data: BlockInfo
): PromisedValue {
): MaterialValue {
when (classReference) {
is IrGetClass -> {
// TODO transform one sort of access into the other?
JavaClassProperty.invokeWith(classReference.argument.accept(this, data))
JavaClassProperty.invokeWith(classReference.argument.accept(this, data), wrapPrimitives)
}
is IrClassReference -> {
val classType = classReference.classType
@@ -1416,7 +1417,7 @@ class ExpressionCodegen(
}
}
generateClassInstance(mv, classType, typeMapper)
generateClassInstance(mv, classType, typeMapper, wrapPrimitives)
}
else -> {
throw AssertionError("not an IrGetClass or IrClassReference: ${classReference.dump()}")
@@ -1521,9 +1522,9 @@ class ExpressionCodegen(
get() = this.classifierOrNull?.safeAs<IrTypeParameterSymbol>()?.owner?.isReified == true
companion object {
internal fun generateClassInstance(v: InstructionAdapter, classType: IrType, typeMapper: IrTypeMapper) {
internal fun generateClassInstance(v: InstructionAdapter, classType: IrType, typeMapper: IrTypeMapper, wrapPrimitives: Boolean) {
val asmType = typeMapper.mapType(classType)
if (classType.getClass()?.isSingleFieldValueClass == true || !isPrimitive(asmType)) {
if (wrapPrimitives || classType.getClass()?.isSingleFieldValueClass == true || !isPrimitive(asmType)) {
v.aconst(typeMapper.boxType(classType))
} else {
v.getstatic(boxType(asmType).internalName, "TYPE", "Ljava/lang/Class;")
@@ -44,7 +44,7 @@ class IrInlineIntrinsicsSupport(
get() = context.state
override fun putClassInstance(v: InstructionAdapter, type: IrType) {
ExpressionCodegen.generateClassInstance(v, type, typeMapper)
ExpressionCodegen.generateClassInstance(v, type, typeMapper, wrapPrimitives = false)
}
override fun generateTypeParameterContainer(v: InstructionAdapter, typeParameter: TypeParameterMarker) {
@@ -18,15 +18,10 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.ir.declarations.isSingleFieldValueClass
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.util.isEnumClass
import org.jetbrains.kotlin.ir.util.isEnumEntry
import org.jetbrains.kotlin.ir.util.isIntegerConst
import org.jetbrains.kotlin.ir.util.isNullConst
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -110,15 +105,21 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
}
// We can use reference equality for enums, otherwise we fall back to boxed equality.
val aIsEnum = a.type.classOrNull?.owner?.run { isEnumClass || isEnumEntry } == true
val bIsEnum = b.type.classOrNull?.owner?.run { isEnumClass || isEnumEntry } == true
return if (aIsEnum || bIsEnum) {
referenceEquals(a, b, leftType, codegen, data)
} else {
a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
genAreEqualCall(codegen.mv)
MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)
return when {
a.isEnumValue || b.isEnumValue ->
referenceEquals(a, b, leftType, codegen, data)
a.isClassValue && b.isClassValue -> {
val leftValue = codegen.generateClassLiteralReference(a, wrapIntoKClass = false, wrapPrimitives = true, data = data)
val rightValue = codegen.generateClassLiteralReference(b, wrapIntoKClass = false, wrapPrimitives = true, data = data)
BooleanComparison(operator, leftValue, rightValue)
}
else -> {
a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
genAreEqualCall(codegen.mv)
MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)
}
}
}
@@ -139,6 +140,12 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
BooleanComparison(operator, leftValue, rightValue)
}
}
private val IrExpression.isEnumValue: Boolean
get() = type.classOrNull?.owner?.run { isEnumClass || isEnumEntry } == true
private val IrExpression.isClassValue: Boolean
get() = this is IrGetClass || this is IrClassReference
}
@@ -30,7 +30,7 @@ object JavaClassProperty : IntrinsicMethod() {
value.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
}
fun invokeWith(value: PromisedValue) =
fun invokeWith(value: PromisedValue, wrapPrimitives: Boolean) =
when {
value.type == Type.VOID_TYPE ->
invokeGetClass(value.materializedAt(AsmTypes.UNIT_TYPE, value.codegen.context.irBuiltIns.unitType))
@@ -38,14 +38,18 @@ object JavaClassProperty : IntrinsicMethod() {
invokeGetClass(value.materializedAtBoxed(value.irType))
isPrimitive(value.type) -> {
value.discard()
value.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;")
if (wrapPrimitives) {
value.mv.aconst(boxType(value.type))
} else {
value.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;")
}
}
else ->
invokeGetClass(value.materialized())
}
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
invokeWith(expression.extensionReceiver!!.accept(codegen, data))
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
invokeWith(expression.extensionReceiver!!.accept(codegen, data), wrapPrimitives = false)
return with(codegen) { expression.onStack }
}
}
@@ -28,6 +28,6 @@ object KClassJavaProperty : IntrinsicMethod() {
val extensionReceiver = expression.extensionReceiver
if (extensionReceiver !is IrClassReference && extensionReceiver !is IrGetClass)
return null
return codegen.generateClassLiteralReference(extensionReceiver, false, data)
return codegen.generateClassLiteralReference(extensionReceiver, wrapIntoKClass = false, wrapPrimitives = false, data = data)
}
}