JVM IR: Avoid redundant coercions in ExpressionCodegen
This commit is contained in:
committed by
Dmitry Petrov
parent
2b2ae8a5f1
commit
5760c0be9c
+38
-22
@@ -15,11 +15,8 @@ import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.constantValue
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.*
|
||||
import org.jetbrains.kotlin.codegen.BaseExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.CallGenerator
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.extractReificationArgument
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putNeedClassReificationMarker
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.AS
|
||||
@@ -361,7 +358,7 @@ class ExpressionCodegen(
|
||||
visitStatementContainer(body, data).discard()
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) =
|
||||
visitStatementContainer(expression, data).coerce(expression.type)
|
||||
visitStatementContainer(expression, data)
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue {
|
||||
classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol)
|
||||
@@ -459,14 +456,23 @@ class ExpressionCodegen(
|
||||
}
|
||||
expression is IrConstructorCall ->
|
||||
MaterialValue(this, asmType, expression.type)
|
||||
expression.type.isUnit() && callable.asmMethod.returnType == Type.VOID_TYPE ->
|
||||
//don't generate redundant UNIT/pop instructions
|
||||
immaterialUnitValue
|
||||
expression.type.isUnit() ->
|
||||
expression.type.isUnit() -> {
|
||||
// NewInference allows casting `() -> T` to `() -> Unit`. A CHECKCAST here will fail.
|
||||
MaterialValue(this, callable.asmMethod.returnType, callee.returnType).discard().coerce(expression.type)
|
||||
if (callable.asmMethod.returnType != Type.VOID_TYPE)
|
||||
MaterialValue(this, callable.asmMethod.returnType, callee.returnType).discard()
|
||||
// don't generate redundant UNIT/pop instructions
|
||||
immaterialUnitValue
|
||||
}
|
||||
callee.parentAsClass.isAnnotationClass && callable.asmMethod.returnType == AsmTypes.JAVA_CLASS_TYPE -> {
|
||||
wrapJavaClassIntoKClass(mv)
|
||||
MaterialValue(this, AsmTypes.K_CLASS_TYPE, expression.type)
|
||||
}
|
||||
callee.parentAsClass.isAnnotationClass && callable.asmMethod.returnType == AsmTypes.JAVA_CLASS_ARRAY_TYPE -> {
|
||||
wrapJavaClassesIntoKClasses(mv)
|
||||
MaterialValue(this, AsmTypes.K_CLASS_ARRAY_TYPE, expression.type)
|
||||
}
|
||||
else ->
|
||||
MaterialValue(this, callable.asmMethod.returnType, callee.returnType).coerce(expression.type)
|
||||
MaterialValue(this, callable.asmMethod.returnType, callee.returnType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,24 +535,28 @@ class ExpressionCodegen(
|
||||
|
||||
val realField = callee.resolveFakeOverride()!!
|
||||
val fieldType = typeMapper.mapType(realField.type)
|
||||
val ownerType = typeMapper.mapClass(callee.parentAsClass).internalName
|
||||
val fieldName = realField.name.asString()
|
||||
val isStatic = expression.receiver == null
|
||||
expression.markLineNumber(startOffset = true)
|
||||
expression.receiver?.accept(this, data)?.materialize()
|
||||
val ownerName = expression.receiver?.let { receiver ->
|
||||
val ownerType = typeMapper.mapTypeAsDeclaration(receiver.type)
|
||||
receiver.accept(this, data).coerce(ownerType, receiver.type).materialize()
|
||||
ownerType.internalName
|
||||
} ?: typeMapper.mapClass(callee.parentAsClass).internalName
|
||||
return if (expression is IrSetField) {
|
||||
expression.value.accept(this, data).coerce(fieldType, callee.type).materialize()
|
||||
when {
|
||||
isStatic -> mv.putstatic(ownerType, fieldName, fieldType.descriptor)
|
||||
else -> mv.putfield(ownerType, fieldName, fieldType.descriptor)
|
||||
isStatic -> mv.putstatic(ownerName, fieldName, fieldType.descriptor)
|
||||
else -> mv.putfield(ownerName, fieldName, fieldType.descriptor)
|
||||
}
|
||||
defaultValue(expression.type)
|
||||
assert(expression.type.isUnit())
|
||||
immaterialUnitValue
|
||||
} else {
|
||||
when {
|
||||
isStatic -> mv.getstatic(ownerType, fieldName, fieldType.descriptor)
|
||||
else -> mv.getfield(ownerType, fieldName, fieldType.descriptor)
|
||||
isStatic -> mv.getstatic(ownerName, fieldName, fieldType.descriptor)
|
||||
else -> mv.getfield(ownerName, fieldName, fieldType.descriptor)
|
||||
}
|
||||
MaterialValue(this, fieldType, callee.type).coerce(expression.type)
|
||||
MaterialValue(this, fieldType, callee.type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,7 +731,7 @@ class ExpressionCodegen(
|
||||
val kotlinType = typeOperand.toKotlinType()
|
||||
return when (expression.operator) {
|
||||
IrTypeOperator.IMPLICIT_CAST ->
|
||||
expression.argument.accept(this, data).coerce(expression.type)
|
||||
expression.argument.accept(this, data)
|
||||
|
||||
IrTypeOperator.CAST, IrTypeOperator.SAFE_CAST -> {
|
||||
val result = expression.argument.accept(this, data)
|
||||
@@ -737,7 +747,7 @@ class ExpressionCodegen(
|
||||
assert(expression.operator == IrTypeOperator.CAST) { "IrTypeOperator.SAFE_CAST should have been lowered." }
|
||||
TypeIntrinsics.checkcast(mv, kotlinType, boxedRightType, false)
|
||||
}
|
||||
MaterialValue(this, boxedRightType, expression.type).coerce(expression.type)
|
||||
MaterialValue(this, boxedRightType, expression.type)
|
||||
}
|
||||
|
||||
IrTypeOperator.INSTANCEOF -> {
|
||||
@@ -978,7 +988,13 @@ class ExpressionCodegen(
|
||||
|
||||
override fun visitThrow(expression: IrThrow, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
expression.value.accept(this, data).materialize()
|
||||
val exception = expression.value.accept(this, data)
|
||||
// Avoid unecessary CHECKCASTs to java/lang/Throwable. If the exception is not of type Object
|
||||
// then it must be some subtype of throwable and we don't need to coerce it.
|
||||
if (exception.type == OBJECT_TYPE)
|
||||
exception.coerce(context.irBuiltIns.throwableType).materialize()
|
||||
else
|
||||
exception.materialize()
|
||||
mv.athrow()
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
+5
-26
@@ -117,28 +117,11 @@ fun PromisedValue.coerce(target: Type, irTarget: IrType): PromisedValue {
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
// All unsafe coercions between irTypes should use the UnsafeCoerce intrinsic
|
||||
type == target -> this
|
||||
type == AsmTypes.JAVA_CLASS_TYPE && target == AsmTypes.K_CLASS_TYPE && irType.isKClass() ->
|
||||
object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
this@coerce.materialize()
|
||||
AsmUtil.wrapJavaClassIntoKClass(mv)
|
||||
}
|
||||
}
|
||||
type == AsmTypes.JAVA_CLASS_ARRAY_TYPE && target == AsmTypes.K_CLASS_ARRAY_TYPE && irType.isArrayOfKClass() ->
|
||||
object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
this@coerce.materialize()
|
||||
AsmUtil.wrapJavaClassesIntoKClasses(mv)
|
||||
}
|
||||
}
|
||||
else -> object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
val value = this@coerce.materialized
|
||||
StackValue.coerce(value.type, type, mv)
|
||||
}
|
||||
// All unsafe coercions between irTypes should use the UnsafeCoerce intrinsic
|
||||
return if (type == target) this else object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
val value = this@coerce.materialized
|
||||
StackValue.coerce(value.type, type, mv)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,10 +132,6 @@ fun PromisedValue.coerce(irTarget: IrType) =
|
||||
fun PromisedValue.coerceToBoxed(irTarget: IrType) =
|
||||
coerce(typeMapper.boxType(irTarget), irTarget)
|
||||
|
||||
fun PromisedValue.boxInlineClasses(irTarget: IrType) =
|
||||
if (irTarget.classOrNull?.owner?.isInline == true)
|
||||
coerceToBoxed(irTarget) else this
|
||||
|
||||
// Same as above, but with a return type that allows conditional jumping.
|
||||
fun PromisedValue.coerceToBoolean() =
|
||||
when (val coerced = coerce(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)) {
|
||||
|
||||
@@ -23,7 +23,8 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object ArrayGet : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val receiver = expression.dispatchReceiver!!.accept(codegen, data).materialized
|
||||
val dispatchReceiver = expression.dispatchReceiver!!
|
||||
val receiver = dispatchReceiver.accept(codegen, data).coerce(dispatchReceiver.type).materialized
|
||||
val elementType = AsmUtil.correctElementType(receiver.type)
|
||||
expression.getValueArgument(0)!!.accept(codegen, data)
|
||||
.coerce(Type.INT_TYPE, codegen.context.irBuiltIns.intType)
|
||||
|
||||
@@ -24,7 +24,8 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object ArraySet : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val receiver = expression.dispatchReceiver!!.accept(codegen, data).materialized
|
||||
val dispatchReceiver = expression.dispatchReceiver!!
|
||||
val receiver = dispatchReceiver.accept(codegen, data).coerce(dispatchReceiver.type).materialized
|
||||
val elementType = AsmUtil.correctElementType(receiver.type)
|
||||
val elementIrType = receiver.irType.getArrayElementType(codegen.context.irBuiltIns)
|
||||
expression.getValueArgument(0)!!.accept(codegen, data).coerce(Type.INT_TYPE, codegen.context.irBuiltIns.intType).materialize()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// WITH_REFLECT
|
||||
// FILE: 1.kt
|
||||
|
||||
@@ -12,6 +12,6 @@ fun f() {
|
||||
// 1 LOCALVARIABLE c C L3 L\d+ 0
|
||||
|
||||
// JVM_IR_TEMPLATES
|
||||
// 1 ISTORE 2\s+L5
|
||||
// 1 ISTORE 2\s+L4
|
||||
// 1 ILOAD 2\s+INVOKEVIRTUAL java/io/PrintStream.print \(C\)V
|
||||
// 1 LOCALVARIABLE c C L5 L\d+ 2
|
||||
// 1 LOCALVARIABLE c C L4 L\d+ 2
|
||||
Vendored
-1
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
// FILE: utils.kt
|
||||
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
// FILE: utils.kt
|
||||
|
||||
|
||||
compiler/testData/codegen/bytecodeText/inlineClasses/passInlineClassesWithSpreadOperatorToVarargs.kt
Vendored
+2
-2
@@ -10,7 +10,7 @@ fun <T> takeVarargs(vararg e: T) {}
|
||||
|
||||
fun test(u1: UInt, u2: UInt, us: Array<UInt>) {
|
||||
takeVarargs(*us) // copy + checkcast (on non-ir backend)
|
||||
takeVarargs(u1, u2, *us) // 2 box + ...
|
||||
takeVarargs(u1, u2, *us) // 2 box + checkcast (on non-ir backend)
|
||||
}
|
||||
|
||||
// @TestKt.class:
|
||||
@@ -26,4 +26,4 @@ fun test(u1: UInt, u2: UInt, us: Array<UInt>) {
|
||||
// 2 CHECKCAST \[LUInt
|
||||
|
||||
// JVM_IR_TEMPLATES
|
||||
// 1 CHECKCAST \[LUInt
|
||||
// 0 CHECKCAST \[LUInt
|
||||
|
||||
Vendored
-1
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
// FILE: utils.kt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user