JVM_IR. Generate additional checkcast for when/try epressions to avoid frame map problems on reification
This commit is contained in:
@@ -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);
|
||||
|
||||
+7
-7
@@ -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)
|
||||
}
|
||||
|
||||
+29
-10
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user