JVM_IR. Generate additional checkcast for when/try epressions to avoid frame map problems on reification

This commit is contained in:
Mikhael Bogdanov
2021-01-26 16:12:54 +01:00
parent 16928d6e3f
commit 5b64ceceb3
4 changed files with 43 additions and 20 deletions
@@ -629,7 +629,11 @@ public abstract class StackValue {
}
public static void coerce(@NotNull Type fromType, @NotNull Type toType, @NotNull InstructionAdapter v) {
if (toType.equals(fromType)) return;
coerce(fromType, toType, v, false);
}
public static void coerce(@NotNull Type fromType, @NotNull Type toType, @NotNull InstructionAdapter v, boolean forceSelfCast) {
if (toType.equals(fromType) && !forceSelfCast) return;
if (toType.getSort() == Type.VOID) {
pop(v, fromType);
@@ -504,10 +504,10 @@ class ExpressionCodegen(
}
unboxedInlineClassIrType != null && !irFunction.isInvokeSuspendOfContinuation() ->
object : PromisedValue(this, unboxedInlineClassIrType.asmType, unboxedInlineClassIrType) {
override fun materializeAt(target: Type, irTarget: IrType) {
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
mv.checkcast(unboxedInlineClassIrType.asmType)
MaterialValue(this@ExpressionCodegen, unboxedInlineClassIrType.asmType, unboxedInlineClassIrType)
.materializeAt(target, irTarget)
.materializeAt(target, irTarget, castForReified)
}
override fun discard() {
@@ -951,7 +951,7 @@ class ExpressionCodegen(
if (!exhaustive) {
result.discard()
} else {
val materializedResult = result.materializedAt(expression.type)
val materializedResult = result.materializedAt(typeMapper.mapType(expression.type), expression.type, true)
if (branch.condition.isTrueConst()) {
// The rest of the expression is dead code.
mv.mark(endLabel)
@@ -1098,7 +1098,7 @@ class ExpressionCodegen(
val isExpression = !aTry.type.isUnit()
var savedValue: Int? = null
if (isExpression) {
tryResult.materializeAt(tryAsmType, aTry.type)
tryResult.materializeAt(tryAsmType, aTry.type, true)
savedValue = frameMap.enterTemp(tryAsmType)
mv.store(savedValue, tryAsmType)
} else {
@@ -1127,7 +1127,7 @@ class ExpressionCodegen(
val catchBody = clause.result
val catchResult = catchBody.accept(this, data)
if (savedValue != null) {
catchResult.materializeAt(tryAsmType, aTry.type)
catchResult.materializeAt(tryAsmType, aTry.type, true)
mv.store(savedValue, tryAsmType)
} else {
catchResult.discard()
@@ -1170,11 +1170,11 @@ class ExpressionCodegen(
mv.mark(tryCatchBlockEnd)
// TODO: generate a common `finally` for try & catch blocks here? Right now this breaks the inliner.
return object : PromisedValue(this, tryAsmType, aTry.type) {
override fun materializeAt(target: Type, irTarget: IrType) {
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
if (savedValue != null) {
mv.load(savedValue, tryAsmType)
frameMap.leaveTemp(tryAsmType)
super.materializeAt(target, irTarget)
super.materializeAt(target, irTarget, castForReified)
} else {
unitValue.materializeAt(target, irTarget)
}
@@ -11,10 +11,16 @@ import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.isTypeParameter
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.substitute
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.typeConstructor
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -24,7 +30,7 @@ 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.
open fun materializeAt(target: Type, irTarget: IrType) {
open fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
val erasedSourceType = irType.eraseTypeParameters()
val erasedTargetType = irTarget.eraseTypeParameters()
val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline
@@ -54,9 +60,10 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val
}
}
if (type != target) {
StackValue.coerce(type, target, mv)
if (type != target || (castForReified && irType.anyTypeArgument { it.isReified })) {
StackValue.coerce(type, target, mv, type == target)
}
}
abstract fun discard()
@@ -68,6 +75,15 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val
get() = codegen.typeMapper
}
fun IrType.anyTypeArgument(check: (IrTypeParameter) -> Boolean): Boolean {
when {
isTypeParameter() -> if (check((classifierOrNull as IrTypeParameterSymbol).owner)) return true
this is IrSimpleType -> return arguments.any { it.typeOrNull?.anyTypeArgument(check) ?: false }
}
return false
}
// A value that *has* been fully constructed.
class MaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) {
override fun discard() {
@@ -83,7 +99,7 @@ abstract class BooleanValue(codegen: ExpressionCodegen) :
abstract fun jumpIfFalse(target: Label)
abstract fun jumpIfTrue(target: Label)
override fun materializeAt(target: Type, irTarget: IrType) {
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
val const0 = Label()
val end = Label()
jumpIfFalse(const0)
@@ -101,7 +117,7 @@ abstract class BooleanValue(codegen: ExpressionCodegen) :
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) {
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
mv.iconst(if (value) 1 else 0)
if (Type.BOOLEAN_TYPE != target) {
StackValue.coerce(Type.BOOLEAN_TYPE, target, mv)
@@ -116,10 +132,10 @@ fun PromisedValue.coerceToBoolean(): BooleanValue =
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) }
this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType, false).also { mv.ifeq(target) }
override fun jumpIfTrue(target: Label) =
this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType).also { mv.ifne(target) }
this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType, false).also { mv.ifne(target) }
override fun discard() {
this@coerceToBoolean.discard()
@@ -127,9 +143,12 @@ fun PromisedValue.coerceToBoolean(): BooleanValue =
}
}
fun PromisedValue.materializeAt(target: Type, irTarget: IrType) {
materializeAt(target, irTarget, false)
}
fun PromisedValue.materializedAt(target: Type, irTarget: IrType): MaterialValue {
materializeAt(target, irTarget)
fun PromisedValue.materializedAt(target: Type, irTarget: IrType, castForReified: Boolean = false): MaterialValue {
materializeAt(target, irTarget, castForReified)
return MaterialValue(codegen, target, irTarget)
}
@@ -163,7 +182,7 @@ val ExpressionCodegen.unitValue: PromisedValue
val ExpressionCodegen.nullConstant: PromisedValue
get() = object : PromisedValue(this, AsmTypes.OBJECT_TYPE, context.irBuiltIns.nothingNType) {
override fun materializeAt(target: Type, irTarget: IrType) {
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
mv.aconst(null)
}
@@ -29,9 +29,9 @@ object UnsafeCoerce : IntrinsicMethod() {
val arg = expression.getValueArgument(0)!!
val result = arg.accept(codegen, data)
return object : PromisedValue(codegen, toType, to) {
override fun materializeAt(target: Type, irTarget: IrType) {
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
result.materializeAt(fromType, from)
super.materializeAt(target, irTarget)
super.materializeAt(target, irTarget, castForReified)
}
override fun discard() {