diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 0f7acd23ac2..ac6386d1b30 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -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 } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt index 4078b63dc23..818aa67af42 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt @@ -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)) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArrayGet.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArrayGet.kt index a51cd3e17fc..850c6c1e7d9 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArrayGet.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArrayGet.kt @@ -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) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArraySet.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArraySet.kt index 6b59311a5f0..80ad3c68707 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArraySet.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArraySet.kt @@ -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() diff --git a/compiler/testData/codegen/boxInline/anonymousObject/kt13133.kt b/compiler/testData/codegen/boxInline/anonymousObject/kt13133.kt index 046527878bb..02aa42afd34 100644 --- a/compiler/testData/codegen/boxInline/anonymousObject/kt13133.kt +++ b/compiler/testData/codegen/boxInline/anonymousObject/kt13133.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // TARGET_BACKEND: JVM // WITH_REFLECT // FILE: 1.kt diff --git a/compiler/testData/codegen/bytecodeText/forLoop/loopVarInterval.kt b/compiler/testData/codegen/bytecodeText/forLoop/loopVarInterval.kt index ce743722168..a5ca5946e9c 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/loopVarInterval.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/loopVarInterval.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 \ No newline at end of file +// 1 LOCALVARIABLE c C L4 L\d+ 2 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInlineClassFromMethodReturnType.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInlineClassFromMethodReturnType.kt index aaa328e5090..8232d9d41d1 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInlineClassFromMethodReturnType.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInlineClassFromMethodReturnType.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +InlineClasses -// IGNORE_BACKEND: JVM_IR // FILE: utils.kt diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxOnInlinedParameters.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxOnInlinedParameters.kt index 8cf42ad92c9..705bd9a3b47 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxOnInlinedParameters.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxOnInlinedParameters.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +InlineClasses -// IGNORE_BACKEND: JVM_IR // FILE: utils.kt diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/passInlineClassesWithSpreadOperatorToVarargs.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/passInlineClassesWithSpreadOperatorToVarargs.kt index 6ce0d6ec3f4..d57711a9433 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/passInlineClassesWithSpreadOperatorToVarargs.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/passInlineClassesWithSpreadOperatorToVarargs.kt @@ -10,7 +10,7 @@ fun takeVarargs(vararg e: T) {} fun test(u1: UInt, u2: UInt, us: Array) { 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) { // 2 CHECKCAST \[LUInt // JVM_IR_TEMPLATES -// 1 CHECKCAST \[LUInt +// 0 CHECKCAST \[LUInt diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/unboxInlineClassFromParameterizedType.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/unboxInlineClassFromParameterizedType.kt index 014fb963ddb..366a68b1a8b 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/unboxInlineClassFromParameterizedType.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/unboxInlineClassFromParameterizedType.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +InlineClasses -// IGNORE_BACKEND: JVM_IR // FILE: utils.kt