From 6e40117116eeaf43893dae54fbe1312a4b13bb95 Mon Sep 17 00:00:00 2001 From: Kristoffer Andersen Date: Thu, 13 Feb 2020 14:18:45 +0100 Subject: [PATCH] [JVM IR] Refactor PromisedValue... ... for an _even leaner_ codegen! - move discard from extention method to abstract method. This will at length avoid some calls to materialize. - use newly abstract method to specialize discard in cases where it would introduce _and_ coerce values only to be popped by discard. - move coerce to member method, introduce materializeAt with a view to eliminate calls to coerce outside of PromisedValue. This will allow us to specilaize materialization at particular types in some instances, at length avoiding casts etc. - no calls to coerce outside of PromisedValue.kt! Progress. - inlined coerce into materializeAt, anticipating specializing materializeAt across implementations of PromisedValue - made materializeAt abstract, copied implementation everywhere! - made materialize an extention function! Ready to specialize materializeAt - coerce is no more! - simplified and specialized all inlinings of materializeAt. - adjust docs to match refactoring --- .../backend/jvm/codegen/ExpressionCodegen.kt | 81 +++---- .../backend/jvm/codegen/PromisedValue.kt | 203 +++++++++--------- .../backend/jvm/codegen/SwitchGenerator.kt | 25 ++- .../kotlin/backend/jvm/intrinsics/AndAnd.kt | 10 +- .../kotlin/backend/jvm/intrinsics/ArrayGet.kt | 5 +- .../kotlin/backend/jvm/intrinsics/ArraySet.kt | 8 +- .../kotlin/backend/jvm/intrinsics/Clone.kt | 2 +- .../backend/jvm/intrinsics/CompareTo.kt | 14 +- .../backend/jvm/intrinsics/EnumIntrinsics.kt | 2 +- .../kotlin/backend/jvm/intrinsics/Equals.kt | 27 +-- .../kotlin/backend/jvm/intrinsics/HashCode.kt | 4 +- .../backend/jvm/intrinsics/IntrinsicMethod.kt | 12 +- .../jvm/intrinsics/JavaClassProperty.kt | 7 +- .../kotlin/backend/jvm/intrinsics/Not.kt | 3 + .../kotlin/backend/jvm/intrinsics/OrOr.kt | 10 +- .../jvm/intrinsics/ReassignParameter.kt | 2 +- .../backend/jvm/intrinsics/SignatureString.kt | 12 +- .../backend/jvm/intrinsics/UnsafeCoerce.kt | 18 +- 18 files changed, 245 insertions(+), 200 deletions(-) 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 e5769228de0..2cb5c476336 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 @@ -7,15 +7,18 @@ package org.jetbrains.kotlin.backend.jvm.codegen import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin -import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin.* +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty 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 @@ -168,7 +171,7 @@ class ExpressionCodegen( if (expression.attributeOwnerId === context.fakeContinuation) { addFakeContinuationMarker(mv) } else { - expression.accept(this, data).coerce(type, irType).materialize() + expression.accept(this, data).materializeAt(type, irType) } return StackValue.onStack(type, irType.toKotlinType()) } @@ -209,7 +212,7 @@ class ExpressionCodegen( } val returnType = signature.returnType val returnIrType = if (irFunction !is IrConstructor) irFunction.returnType else context.irBuiltIns.unitType - result.coerce(returnType, returnIrType).materialize() + result.materializeAt(returnType, returnIrType) mv.areturn(returnType) } val endLabel = markNewLabel() @@ -315,7 +318,7 @@ class ExpressionCodegen( return super.visitBlock(expression, data) val info = BlockInfo(data) // Force materialization to avoid reading from out-of-scope variables. - return super.visitBlock(expression, info).materialized.also { + return super.visitBlock(expression, info).materialized().also { if (info.variables.isNotEmpty()) { writeLocalVariablesInTable(info, markNewLabel()) } @@ -339,13 +342,15 @@ class ExpressionCodegen( } private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo) = - container.statements.fold(immaterialUnitValue as PromisedValue) { prev, exp -> + container.statements.fold(unitValue) { prev, exp -> prev.discard() exp.accept(this, data).also { (exp as? IrExpression)?.markEndOfStatementIfNeeded() } } - override fun visitBlockBody(body: IrBlockBody, data: BlockInfo) = + override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): PromisedValue { visitStatementContainer(body, data).discard() + return unitValue + } override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) = visitStatementContainer(expression, data) @@ -435,7 +440,7 @@ class ExpressionCodegen( expression.type.isNothing() -> { mv.aconst(null) mv.athrow() - immaterialUnitValue + unitValue } expression is IrConstructorCall -> MaterialValue(this, asmType, expression.type) @@ -448,7 +453,7 @@ class ExpressionCodegen( if (callable.asmMethod.returnType != Type.VOID_TYPE) MaterialValue(this, callable.asmMethod.returnType, callee.returnType).discard() // don't generate redundant UNIT/pop instructions - immaterialUnitValue + unitValue } callee.parentAsClass.isAnnotationClass && callable.asmMethod.returnType == AsmTypes.JAVA_CLASS_TYPE -> { wrapJavaClassIntoKClass(mv) @@ -483,7 +488,7 @@ class ExpressionCodegen( val initializer = declaration.initializer if (initializer != null) { - initializer.accept(this, data).coerce(varType, declaration.type).materialize() + initializer.accept(this, data).materializeAt(varType, declaration.type) initializer.markLineNumber(startOffset = true) mv.store(index, varType) } else if (declaration.isVisibleInLVT) { @@ -492,7 +497,7 @@ class ExpressionCodegen( } data.variables.add(VariableInfo(declaration, index, varType, markNewLabel())) - return immaterialUnitValue + return unitValue } override fun visitGetValue(expression: IrGetValue, data: BlockInfo): PromisedValue { @@ -519,17 +524,17 @@ class ExpressionCodegen( expression.markLineNumber(startOffset = true) val ownerName = expression.receiver?.let { receiver -> val ownerType = typeMapper.mapTypeAsDeclaration(receiver.type) - receiver.accept(this, data).coerce(ownerType, receiver.type).materialize() + receiver.accept(this, data).materializeAt(ownerType, receiver.type) ownerType.internalName } ?: typeMapper.mapClass(callee.parentAsClass).internalName return if (expression is IrSetField) { - expression.value.accept(this, data).coerce(fieldType, callee.type).materialize() + expression.value.accept(this, data).materializeAt(fieldType, callee.type) when { isStatic -> mv.putstatic(ownerName, fieldName, fieldType.descriptor) else -> mv.putfield(ownerName, fieldName, fieldType.descriptor) } assert(expression.type.isUnit()) - immaterialUnitValue + unitValue } else { when { isStatic -> mv.getstatic(ownerName, fieldName, fieldType.descriptor) @@ -547,7 +552,7 @@ class ExpressionCodegen( val isFieldInitializer = expression.origin == IrStatementOrigin.INITIALIZE_FIELD val skip = (inPrimaryConstructor || inClassInit) && isFieldInitializer && expressionValue is IrConst<*> && isDefaultValueForType(expression.symbol.owner.type.asmType, expressionValue.value) - return if (skip) defaultValue(expression.type) else super.visitSetField(expression, data) + return if (skip) unitValue else super.visitSetField(expression, data) } /** @@ -576,22 +581,22 @@ class ExpressionCodegen( override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): PromisedValue { expression.markLineNumber(startOffset = true) setVariable(expression.symbol, expression.value, data) - return immaterialUnitValue + return unitValue } fun setVariable(symbol: IrValueSymbol, value: IrExpression, data: BlockInfo) { value.markLineNumber(startOffset = true) - value.accept(this, data).coerce(symbol.owner.type).materialize() + value.accept(this, data).materializeAt(symbol.owner.type) mv.store(findLocalIndex(symbol), symbol.owner.asmType) } override fun visitConst(expression: IrConst, data: BlockInfo): PromisedValue { expression.markLineNumber(startOffset = true) when (val value = expression.value) { - is Boolean -> return object : BooleanValue(this) { - override fun jumpIfFalse(target: Label) = if (value) Unit else mv.goTo(target) - override fun jumpIfTrue(target: Label) = if (value) mv.goTo(target) else Unit - override fun materialize() = mv.iconst(if (value) 1 else 0) + is Boolean -> { + // BooleanConstants _may not_ be materialized, so we ensure an instruction for the line number. + mv.nop() + return BooleanConstant(this, value) } is Char -> mv.iconst(value.toInt()) is Long -> mv.lconst(value) @@ -618,7 +623,7 @@ class ExpressionCodegen( closureReifiedMarkers[declaration] = it } } - return immaterialUnitValue + return unitValue } override fun visitReturn(expression: IrReturn, data: BlockInfo): PromisedValue { @@ -636,12 +641,12 @@ class ExpressionCodegen( mv, "java/lang/UnsupportedOperationException", "Non-local returns are not allowed with inlining disabled" ) - return immaterialUnitValue + return unitValue } val returnType = if (owner == irFunction) signature.returnType else methodSignatureMapper.mapReturnType(owner) val afterReturnLabel = Label() - expression.value.accept(this, data).coerce(returnType, owner.returnType).materialize() + expression.value.accept(this, data).materializeAt(returnType, owner.returnType) generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data) expression.markLineNumber(startOffset = true) if (isNonLocalReturn) { @@ -650,7 +655,7 @@ class ExpressionCodegen( mv.areturn(returnType) mv.mark(afterReturnLabel) mv.nop()/*TODO check RESTORE_STACK_IN_TRY_CATCH processor*/ - return immaterialUnitValue + return unitValue } override fun visitWhen(expression: IrWhen, data: BlockInfo): PromisedValue { @@ -681,7 +686,7 @@ class ExpressionCodegen( if (!exhaustive) { result.discard() } else { - val materializedResult = result.coerce(expression.type).materialized + val materializedResult = result.materializedAt(expression.type) if (branch.condition.isTrueConst()) { // The rest of the expression is dead code. mv.mark(endLabel) @@ -695,7 +700,7 @@ class ExpressionCodegen( mv.mark(elseLabel) } mv.mark(endLabel) - return immaterialUnitValue + return unitValue } override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): PromisedValue { @@ -708,7 +713,7 @@ class ExpressionCodegen( IrTypeOperator.CAST, IrTypeOperator.SAFE_CAST -> { val result = expression.argument.accept(this, data) val boxedLeftType = typeMapper.boxType(result.irType) - result.coerce(boxedLeftType, expression.argument.type).materialize() + result.materializeAt(boxedLeftType, expression.argument.type) val boxedRightType = typeMapper.boxType(typeOperand) if (typeOperand.isReifiedTypeParameter) { @@ -723,7 +728,7 @@ class ExpressionCodegen( } IrTypeOperator.INSTANCEOF -> { - expression.argument.accept(this, data).coerce(context.irBuiltIns.anyNType).materialize() + expression.argument.accept(this, data).materializeAt(context.irBuiltIns.anyNType) val type = typeMapper.boxType(typeOperand) if (typeOperand.isReifiedTypeParameter) { putReifiedOperationMarkerIfTypeIsReifiedParameter(typeOperand, ReifiedTypeInliner.OperationKind.IS) @@ -763,7 +768,7 @@ class ExpressionCodegen( } mv.goTo(continueLabel) mv.mark(endLabel) - return immaterialUnitValue + return unitValue } override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: BlockInfo): PromisedValue { @@ -780,7 +785,7 @@ class ExpressionCodegen( loop.condition.markLineNumber(true) loop.condition.accept(this, data).coerceToBoolean().jumpIfTrue(entry) mv.mark(endLabel) - return immaterialUnitValue + return unitValue } private fun unwindBlockStack( @@ -807,7 +812,7 @@ class ExpressionCodegen( ?: throw AssertionError("Target label for break/continue not found") mv.fixStackAndJump(if (jump is IrBreak) stackElement.breakLabel else stackElement.continueLabel) mv.mark(endLabel) - return immaterialUnitValue + return unitValue } override fun visitTry(aTry: IrTry, data: BlockInfo): PromisedValue { @@ -826,7 +831,7 @@ class ExpressionCodegen( val isExpression = !aTry.type.isUnit() var savedValue: Int? = null if (isExpression) { - tryResult.coerce(tryAsmType, aTry.type).materialize() + tryResult.materializeAt(tryAsmType, aTry.type) savedValue = frameMap.enterTemp(tryAsmType) mv.store(savedValue, tryAsmType) } else { @@ -855,7 +860,7 @@ class ExpressionCodegen( val catchBody = clause.result val catchResult = catchBody.accept(this, data) if (savedValue != null) { - catchResult.coerce(tryAsmType, aTry.type).materialize() + catchResult.materializeAt(tryAsmType, aTry.type) mv.store(savedValue, tryAsmType) } else { catchResult.discard() @@ -903,7 +908,7 @@ class ExpressionCodegen( frameMap.leaveTemp(tryAsmType) return aTry.onStack } - return immaterialUnitValue + return unitValue } private fun genTryCatchCover(catchStart: Label, tryStart: Label, tryEnd: Label, tryGaps: List>, type: String?) { @@ -964,11 +969,11 @@ class ExpressionCodegen( // 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() + exception.materializeAt(context.irBuiltIns.throwableType) else exception.materialize() mv.athrow() - return immaterialUnitValue + return unitValue } override fun visitGetClass(expression: IrGetClass, data: BlockInfo) = 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 818aa67af42..43d5980b842 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 @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.parentAsClass -import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type @@ -24,7 +23,42 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val irType: IrType) { // If this value is immaterial, construct an object on the top of the stack. This // must always be done before generating other values or emitting raw bytecode. - abstract fun materialize() + open fun materializeAt(target: Type, irTarget: IrType) { + val erasedSourceType = irType.eraseTypeParameters() + val erasedTargetType = irTarget.eraseTypeParameters() + val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline + val isToTypeInlineClass = erasedTargetType.classOrNull!!.owner.isInline + + // Boxing and unboxing kotlin.Result leads to CCE in generated code + val doNotCoerceKotlinResultInContinuation = + (codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS || + codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA) + && (irType.isKotlinResult() || irTarget.isKotlinResult()) + + // Coerce inline classes + if ((isFromTypeInlineClass || isToTypeInlineClass) && !doNotCoerceKotlinResultInContinuation) { + val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(erasedSourceType.unboxed) == type + val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(erasedTargetType.unboxed) == target + + when { + isFromTypeUnboxed && !isToTypeUnboxed -> { + StackValue.boxInlineClass(erasedSourceType.toKotlinType(), mv) + return + } + + !isFromTypeUnboxed && isToTypeUnboxed -> { + StackValue.unboxInlineClass(type, erasedTargetType.toKotlinType(), mv) + return + } + } + } + + if (type != target) { + StackValue.coerce(type, target, mv) + } + } + + abstract fun discard() val mv: InstructionAdapter get() = codegen.mv @@ -38,21 +72,20 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val // A value that *has* been fully constructed. class MaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) { - override fun materialize() {} -} - -// A value that is only materialized through coercion. -class ImmaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) { - override fun materialize() {} + override fun discard() { + if (type !== Type.VOID_TYPE) + AsmUtil.pop(mv, type) + } } // A value that can be branched on. JVM has certain branching instructions which can be used // to optimize these. -abstract class BooleanValue(codegen: ExpressionCodegen) : PromisedValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType) { +abstract class BooleanValue(codegen: ExpressionCodegen) : + PromisedValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType) { abstract fun jumpIfFalse(target: Label) abstract fun jumpIfTrue(target: Label) - override fun materialize() { + override fun materializeAt(target: Type, irTarget: IrType) { val const0 = Label() val end = Label() jumpIfFalse(const0) @@ -61,103 +94,71 @@ abstract class BooleanValue(codegen: ExpressionCodegen) : PromisedValue(codegen, mv.mark(const0) mv.iconst(0) mv.mark(end) + if (Type.BOOLEAN_TYPE != target) { + StackValue.coerce(Type.BOOLEAN_TYPE, target, mv) + } } } -// Same as materialize(), but return a representation of the result. -val PromisedValue.materialized: MaterialValue - get() { - materialize() - return MaterialValue(codegen, type, irType) +class BooleanConstant(codegen: ExpressionCodegen, val value: Boolean) : BooleanValue(codegen) { + override fun jumpIfFalse(target: Label) = if (value) Unit else mv.goTo(target) + override fun jumpIfTrue(target: Label) = if (value) mv.goTo(target) else Unit + override fun materializeAt(target: Type, irTarget: IrType) { + mv.iconst(if (value) 1 else 0) + if (Type.BOOLEAN_TYPE != target) { + StackValue.coerce(Type.BOOLEAN_TYPE, target, mv) + } } -// Materialize and disregard this value. Materialization is forced because, presumably, -// we only wanted the side effects anyway. -fun PromisedValue.discard(): PromisedValue { - materialize() - if (type !== Type.VOID_TYPE) - AsmUtil.pop(mv, type) - return codegen.immaterialUnitValue + override fun discard() {} } -private val IrType.unboxed: IrType +fun PromisedValue.coerceToBoolean(): BooleanValue = + when (this) { + is BooleanValue -> this + else -> object : BooleanValue(codegen) { + override fun jumpIfFalse(target: Label) = + this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType).also { mv.ifeq(target) } + + override fun jumpIfTrue(target: Label) = + this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType).also { mv.ifne(target) } + + override fun discard() { + this@coerceToBoolean.discard() + } + } + } + + +fun PromisedValue.materializedAt(target: Type, irTarget: IrType): MaterialValue { + materializeAt(target, irTarget) + return MaterialValue(codegen, target, irTarget) +} + +fun PromisedValue.materialized(): MaterialValue = + materializedAt(type, irType) + +fun PromisedValue.materializedAt(irTarget: IrType): MaterialValue = + materializedAt(typeMapper.mapType(irTarget), irTarget) + +fun PromisedValue.materializedAtBoxed(irTarget: IrType): MaterialValue = + materializedAt(typeMapper.boxType(irTarget), irTarget) + +fun PromisedValue.materialize() { + materializeAt(type, irType) +} + +fun PromisedValue.materializeAt(irTarget: IrType) { + materializeAt(typeMapper.mapType(irTarget), irTarget) +} + +fun PromisedValue.materializeAtBoxed(irTarget: IrType) { + materializeAt(typeMapper.boxType(irTarget), irTarget) +} + +val IrType.unboxed: IrType get() = InlineClassAbi.getUnderlyingType(erasedUpperBound) -// On materialization, cast the value to a different type. -fun PromisedValue.coerce(target: Type, irTarget: IrType): PromisedValue { - val erasedSourceType = irType.eraseTypeParameters() - val erasedTargetType = irTarget.eraseTypeParameters() - val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline - val isToTypeInlineClass = erasedTargetType.classOrNull!!.owner.isInline - - // Boxing and unboxing kotlin.Result leads to CCE in generated code - val doNotCoerceKotlinResultInContinuation = - (codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS || - codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA) - && (irType.isKotlinResult() || irTarget.isKotlinResult()) - - // Coerce inline classes - if ((isFromTypeInlineClass || isToTypeInlineClass) && !doNotCoerceKotlinResultInContinuation) { - val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(erasedSourceType.unboxed) == type - val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(erasedTargetType.unboxed) == target - - when { - isFromTypeUnboxed && !isToTypeUnboxed -> return object : PromisedValue(codegen, target, irTarget) { - override fun materialize() { - this@coerce.materialize() - StackValue.boxInlineClass(erasedSourceType.toKotlinType(), mv) - } - } - !isFromTypeUnboxed && isToTypeUnboxed -> return object : PromisedValue(codegen, target, irTarget) { - override fun materialize() { - val value = this@coerce.materialized - StackValue.unboxInlineClass(value.type, erasedTargetType.toKotlinType(), 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) - } - } -} - -fun PromisedValue.coerce(irTarget: IrType) = - coerce(typeMapper.mapType(irTarget), irTarget) - -fun PromisedValue.coerceToBoxed(irTarget: IrType) = - coerce(typeMapper.boxType(irTarget), irTarget) - -// 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)) { - is BooleanValue -> coerced - else -> object : BooleanValue(codegen) { - override fun jumpIfFalse(target: Label) = coerced.materialize().also { mv.ifeq(target) } - override fun jumpIfTrue(target: Label) = coerced.materialize().also { mv.ifne(target) } - override fun materialize() = coerced.materialize() - } - } - -// Non-materialized value of Unit type -// This value is only materialized when calling coerce, which is why it is represented -// as a MaterialValue even though there is nothing on the stack. -val ExpressionCodegen.immaterialUnitValue: ImmaterialValue - get() = ImmaterialValue(this, Type.VOID_TYPE, context.irBuiltIns.unitType) - -// Non-materialized default value for the given type -fun ExpressionCodegen.defaultValue(irType: IrType): PromisedValue = - object : PromisedValue(this, typeMapper.mapType(irType), irType) { - override fun materialize() { - StackValue.coerce(Type.VOID_TYPE, type, codegen.mv) - } - } - -private fun IrType.isArrayOfKClass(): Boolean = - isArray() && this is IrSimpleType && arguments.singleOrNull().let { argument -> - argument is IrTypeProjection && argument.type.isKClass() - } +// A Non-materialized value of Unit type that is only materialized through coercion. +val ExpressionCodegen.unitValue: PromisedValue + get() = MaterialValue(this, Type.VOID_TYPE, context.irBuiltIns.unitType) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt index 849c5296102..7ee9378cdf7 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt @@ -160,13 +160,13 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf // // Namely, the structure which returns true if any one of the condition is true. for (branch in condition.branches) { - if (branch is IrElseBranch) { + candidates += if (branch is IrElseBranch) { assert(branch.condition.isTrueConst()) { "IrElseBranch.condition should be const true: ${branch.condition.dump()}" } - candidates += matchConditions(branch.result) ?: return null + matchConditions(branch.result) ?: return null } else { if (!branch.result.isTrueConst()) return null - candidates += matchConditions(branch.condition) ?: return null + matchConditions(branch.condition) ?: return null } } @@ -220,14 +220,21 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf private fun genBranchTargets(): PromisedValue { with(codegen) { val endLabel = Label() + for ((thenExpression, label) in expressionToLabels) { mv.visitLabel(label) - thenExpression.accept(codegen, data).coerce(expression.type).materialized + thenExpression.accept(codegen, data).also { + if (elseExpression != null) { + it.materializedAt(expression.type) + } else { + it.discard() + } + } mv.goTo(endLabel) } + mv.visitLabel(defaultLabel) - val stackValue = elseExpression?.accept(codegen, data)?.coerce(expression.type) ?: defaultValue(expression.type) - val result = stackValue.materialized + val result = elseExpression?.accept(codegen, data)?.materializedAt(expression.type) ?: unitValue mv.mark(endLabel) return result } @@ -245,10 +252,8 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf override fun shouldOptimize() = cases.size > 1 override fun genSwitch() { - with(codegen) { - subject.accept(codegen, data).materialize() - genIntSwitch(cases) - } + subject.accept(codegen, data).materialize() + genIntSwitch(cases) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/AndAnd.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/AndAnd.kt index 62ef11261f3..d045cd8f3fb 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/AndAnd.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/AndAnd.kt @@ -12,7 +12,8 @@ import org.jetbrains.org.objectweb.asm.Label object AndAnd : IntrinsicMethod() { - private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) : BooleanValue(codegen) { + private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) : + BooleanValue(codegen) { override fun jumpIfFalse(target: Label) { arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(target) @@ -25,6 +26,13 @@ object AndAnd : IntrinsicMethod() { arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target) mv.visitLabel(stayLabel) } + + override fun discard() { + val end = Label() + arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(end) + arg1.accept(codegen, data).discard() + mv.visitLabel(end) + } } override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { 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 850c6c1e7d9..4020304c36a 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 @@ -24,11 +24,10 @@ import org.jetbrains.org.objectweb.asm.Type object ArrayGet : IntrinsicMethod() { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { val dispatchReceiver = expression.dispatchReceiver!! - val receiver = dispatchReceiver.accept(codegen, data).coerce(dispatchReceiver.type).materialized + val receiver = dispatchReceiver.accept(codegen, data).materializedAt(dispatchReceiver.type) val elementType = AsmUtil.correctElementType(receiver.type) expression.getValueArgument(0)!!.accept(codegen, data) - .coerce(Type.INT_TYPE, codegen.context.irBuiltIns.intType) - .materialize() + .materializeAt(Type.INT_TYPE, codegen.context.irBuiltIns.intType) codegen.mv.aload(elementType) return MaterialValue(codegen, elementType, expression.type) } 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 80ad3c68707..916ee969c7d 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 @@ -25,12 +25,12 @@ import org.jetbrains.org.objectweb.asm.Type object ArraySet : IntrinsicMethod() { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { val dispatchReceiver = expression.dispatchReceiver!! - val receiver = dispatchReceiver.accept(codegen, data).coerce(dispatchReceiver.type).materialized + val receiver = dispatchReceiver.accept(codegen, data).materializedAt(dispatchReceiver.type) 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() - expression.getValueArgument(1)!!.accept(codegen, data).coerce(elementType, elementIrType).materialize() + expression.getValueArgument(0)!!.accept(codegen, data).materializeAt(Type.INT_TYPE, codegen.context.irBuiltIns.intType) + expression.getValueArgument(1)!!.accept(codegen, data).materializeAt(elementType, elementIrType) codegen.mv.astore(elementType) - return codegen.immaterialUnitValue + return codegen.unitValue } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Clone.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Clone.kt index f05be8221b6..58494d2c808 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Clone.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Clone.kt @@ -25,7 +25,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes object Clone : IntrinsicMethod() { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) { - val result = expression.dispatchReceiver!!.accept(this, data).materialized + val result = expression.dispatchReceiver!!.accept(this, data).materialized() assert(!AsmUtil.isPrimitive(result.type)) { "clone() of primitive type" } val opcode = if (expression is IrCall && expression.superQualifierSymbol != null) Opcodes.INVOKESPECIAL else Opcodes.INVOKEVIRTUAL mv.visitMethodInsn(opcode, "java/lang/Object", "clone", "()Ljava/lang/Object;", false) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt index 73ce1144431..c3eeb91ae4c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt @@ -84,6 +84,11 @@ class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: Mater NumberCompare.patchOpcode(BranchedValue.negatedOperations[NumberCompare.getNumberCompareOpcode(op)]!!, mv, op, a.type) mv.visitJumpInsn(opcode, target) } + + override fun discard() { + b.discard() + a.discard() + } } @@ -107,6 +112,11 @@ class NonIEEE754FloatComparison(val op: IElementType, val a: MaterialValue, val invokeStaticComparison(a.type) mv.visitJumpInsn(BranchedValue.negatedOperations[numberCompareOpcode]!!, target) } + + override fun discard() { + b.discard() + a.discard() + } } class PrimitiveComparison( @@ -116,8 +126,8 @@ class PrimitiveComparison( override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { val parameterType = Type.getType(JvmPrimitiveType.get(KotlinBuiltIns.getPrimitiveType(primitiveNumberType)!!).desc) val (left, right) = expression.receiverAndArgs() - val a = left.accept(codegen, data).coerce(parameterType, left.type).materialized - val b = right.accept(codegen, data).coerce(parameterType, right.type).materialized + val a = left.accept(codegen, data).materializedAt(parameterType, left.type) + val b = right.accept(codegen, data).materializedAt(parameterType, right.type) val useNonIEEE754Comparison = !codegen.context.state.languageVersionSettings.supportsFeature(LanguageFeature.ProperIeee754Comparisons) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/EnumIntrinsics.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/EnumIntrinsics.kt index 9ccd3a07f22..262c8bb3a39 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/EnumIntrinsics.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/EnumIntrinsics.kt @@ -18,7 +18,7 @@ import org.jetbrains.org.objectweb.asm.Type object EnumValueOf : IntrinsicMethod() { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) { val type = expression.getTypeArgument(0)!! - val result = expression.getValueArgument(0)!!.accept(this, data).materialized + val result = expression.getValueArgument(0)!!.accept(this, data).materialized() if (type.isReifiedTypeParameter) { // Note that the inliner expects exactly the following sequence of instructions. // diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt index dbf08c79efb..6f67a784d10 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.ir.types.toKotlinType @@ -39,8 +40,8 @@ class ExplicitEquals : IntrinsicMethod() { val (a, b) = expression.receiverAndArgs() // TODO use specialized boxed type - this might require types like 'java.lang.Integer' in IR - a.accept(codegen, data).coerce(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType).materialize() - b.accept(codegen, data).coerce(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType).materialize() + a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType) + b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType) codegen.mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, AsmTypes.OBJECT_TYPE.internalName, @@ -54,15 +55,13 @@ class ExplicitEquals : IntrinsicMethod() { } class Equals(val operator: IElementType) : IntrinsicMethod() { - private class BooleanConstantFalseCheck(val value: PromisedValue) : BooleanValue(value.codegen) { - override fun materialize() = value.discard().let { mv.iconst(0) } - override fun jumpIfFalse(target: Label) = value.discard().let { mv.fakeAlwaysTrueIfeq(target) } - override fun jumpIfTrue(target: Label) = value.discard().let { mv.fakeAlwaysFalseIfeq(target) } - } private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.codegen) { override fun jumpIfFalse(target: Label) = value.materialize().also { mv.ifnonnull(target) } override fun jumpIfTrue(target: Label) = value.materialize().also { mv.ifnull(target) } + override fun discard() { + value.discard() + } } override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { @@ -72,8 +71,10 @@ class Equals(val operator: IElementType) : IntrinsicMethod() { val value = irValue.accept(codegen, data) return if (!isPrimitive(value.type) && (irValue.type.classOrNull?.owner?.isInline != true || irValue.type.isNullable())) BooleanNullCheck(value) - else - BooleanConstantFalseCheck(value) + else { + value.discard() + BooleanConstant(codegen, false) + } } val leftType = with(codegen) { a.asmType } @@ -84,14 +85,14 @@ class Equals(val operator: IElementType) : IntrinsicMethod() { // is that `equals` is a total order (-0 < +0 and NaN == NaN) and `===` is IEEE754-compliant. (!isPrimitive(leftType) || leftType != rightType || leftType == Type.FLOAT_TYPE || leftType == Type.DOUBLE_TYPE) return if (useEquals) { - a.accept(codegen, data).coerce(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType).materialize() - b.accept(codegen, data).coerce(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType).materialize() + 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) } else { val operandType = if (!isPrimitive(leftType)) AsmTypes.OBJECT_TYPE else leftType - val aValue = a.accept(codegen, data).coerce(operandType, a.type).materialized - val bValue = b.accept(codegen, data).coerce(operandType, b.type).materialized + val aValue = a.accept(codegen, data).materializedAt(operandType, a.type) + val bValue = b.accept(codegen, data).materializedAt(operandType, b.type) BooleanComparison(operator, aValue, bValue) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/HashCode.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/HashCode.kt index c29948a0260..751882f4ca8 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/HashCode.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/HashCode.kt @@ -30,13 +30,13 @@ import org.jetbrains.org.objectweb.asm.Type object HashCode : IntrinsicMethod() { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) { val receiver = expression.dispatchReceiver ?: error("No receiver for hashCode: ${expression.render()}") - val result = receiver.accept(this, data).materialized + val result = receiver.accept(this, data).materialized() val target = context.state.target when { irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD || irFunction.origin == IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER -> AsmUtil.genHashCode(mv, mv, result.type, target) target == JvmTarget.JVM_1_6 -> { - result.coerceToBoxed(receiver.type).materialize() + result.materializeAtBoxed(receiver.type) mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false) } else -> { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethod.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethod.kt index 6321074d0cc..a70f3243ae8 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethod.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethod.kt @@ -6,12 +6,12 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo -import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen -import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue +import org.jetbrains.kotlin.backend.jvm.codegen.* +import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.Method @@ -27,11 +27,11 @@ abstract class IntrinsicMethod { with(codegen) { val descriptor = methodSignatureMapper.mapSignatureSkipGeneric(expression.symbol.owner) val stackValue = toCallable(expression, descriptor, context).invoke(mv, codegen, data) - return object : PromisedValue(this, stackValue.type, expression.type) { - override fun materialize() = stackValue.put(mv) - } + stackValue.put(mv) + return MaterialValue(this, stackValue.type, expression.type) } + companion object { fun calcReceiverType(call: IrMemberAccessExpression, context: JvmBackendContext): Type { return context.typeMapper.mapType(call.dispatchReceiver?.type ?: call.extensionReceiver!!.type) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JavaClassProperty.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JavaClassProperty.kt index 7ccbd5f65f1..055fda1f47b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JavaClassProperty.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JavaClassProperty.kt @@ -26,22 +26,21 @@ import org.jetbrains.org.objectweb.asm.Type object JavaClassProperty : IntrinsicMethod() { private fun invokeGetClass(value: PromisedValue) { - value.materialize() value.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false) } fun invokeWith(value: PromisedValue) = when { value.type == Type.VOID_TYPE -> - invokeGetClass(value.coerce(AsmTypes.UNIT_TYPE, value.codegen.context.irBuiltIns.unitType)) + invokeGetClass(value.materializedAt(AsmTypes.UNIT_TYPE, value.codegen.context.irBuiltIns.unitType)) value.irType.classOrNull?.owner?.isInline == true -> - invokeGetClass(value.coerceToBoxed(value.irType)) + invokeGetClass(value.materializedAtBoxed(value.irType)) isPrimitive(value.type) -> { value.discard() value.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;") } else -> - invokeGetClass(value) + invokeGetClass(value.materialized()) } override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Not.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Not.kt index 90ae463d504..49a316fccda 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Not.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Not.kt @@ -27,6 +27,9 @@ object Not : IntrinsicMethod() { class BooleanNegation(val value: BooleanValue) : BooleanValue(value.codegen) { override fun jumpIfFalse(target: Label) = value.jumpIfTrue(target) override fun jumpIfTrue(target: Label) = value.jumpIfFalse(target) + override fun discard() { + value.discard() + } } override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/OrOr.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/OrOr.kt index 0912ae4e95a..fb1df3b2c2c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/OrOr.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/OrOr.kt @@ -13,7 +13,8 @@ import org.jetbrains.org.objectweb.asm.Label object OrOr : IntrinsicMethod() { private class BooleanDisjunction( - val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) : BooleanValue(codegen) { + val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo + ) : BooleanValue(codegen) { override fun jumpIfFalse(target: Label) { val stayLabel = Label() @@ -26,6 +27,13 @@ object OrOr : IntrinsicMethod() { arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(target) arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target) } + + override fun discard() { + val end = Label() + arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(end) + arg1.accept(codegen, data).discard() + mv.visitLabel(end) + } } override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ReassignParameter.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ReassignParameter.kt index 61a1be501c2..39bd1638616 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ReassignParameter.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ReassignParameter.kt @@ -28,6 +28,6 @@ object ReassignParameter : IntrinsicMethod() { val parameter = parameterGet?.symbol as? IrValueParameterSymbol ?: throw AssertionError("${expression.getValueArgument(0)} is not a get of a parameter") codegen.setVariable(parameter, expression.getValueArgument(1)!!, data) - return codegen.immaterialUnitValue + return codegen.unitValue } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/SignatureString.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/SignatureString.kt index e54caf33493..28eae435167 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/SignatureString.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/SignatureString.kt @@ -7,20 +7,23 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen +import org.jetbrains.kotlin.backend.jvm.codegen.MaterialValue import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionReference +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.collectRealOverrides import org.jetbrains.kotlin.ir.util.isSuspend import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.org.objectweb.asm.Type /** * Computes the JVM signature of a given IrFunction. The function is passed as an IrFunctionReference * to the single argument of the intrinsic. */ object SignatureString : IntrinsicMethod() { - override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { + override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue { val function = (expression.getValueArgument(0) as IrFunctionReference).symbol.owner var resolved = if (function is IrSimpleFunction) function.collectRealOverrides().first() else function if (resolved.isSuspend) { @@ -28,10 +31,7 @@ object SignatureString : IntrinsicMethod() { } val method = codegen.context.methodSignatureMapper.mapAsmMethod(resolved) val descriptor = method.name + method.descriptor - return object : PromisedValue(codegen, AsmTypes.JAVA_STRING_TYPE, codegen.context.irBuiltIns.stringType) { - override fun materialize() { - codegen.mv.aconst(descriptor) - } - } + codegen.mv.aconst(descriptor) + return MaterialValue(codegen, AsmTypes.JAVA_STRING_TYPE, codegen.context.irBuiltIns.stringType) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/UnsafeCoerce.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/UnsafeCoerce.kt index 356cfc1c22e..c5f095febf2 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/UnsafeCoerce.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/UnsafeCoerce.kt @@ -5,11 +5,10 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics -import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo -import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen -import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue -import org.jetbrains.kotlin.backend.jvm.codegen.coerce +import org.jetbrains.kotlin.backend.jvm.codegen.* import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.org.objectweb.asm.Type /** * Implicit coercion between IrTypes with the same underlying representation. @@ -19,7 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression * addition to the underlying asmType. */ object UnsafeCoerce : IntrinsicMethod() { - override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { + override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue { val from = expression.getTypeArgument(0)!! val to = expression.getTypeArgument(1)!! val fromType = codegen.typeMapper.mapType(from) @@ -30,7 +29,14 @@ object UnsafeCoerce : IntrinsicMethod() { val arg = expression.getValueArgument(0)!! val result = arg.accept(codegen, data) return object : PromisedValue(codegen, toType, to) { - override fun materialize() = result.coerce(from).materialize() + override fun materializeAt(target: Type, irTarget: IrType) { + result.materializeAt(fromType, from) + super.materializeAt(target, irTarget) + } + + override fun discard() { + result.discard() + } } } }