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) {
|
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) {
|
if (toType.getSort() == Type.VOID) {
|
||||||
pop(v, fromType);
|
pop(v, fromType);
|
||||||
|
|||||||
+7
-7
@@ -504,10 +504,10 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
unboxedInlineClassIrType != null && !irFunction.isInvokeSuspendOfContinuation() ->
|
unboxedInlineClassIrType != null && !irFunction.isInvokeSuspendOfContinuation() ->
|
||||||
object : PromisedValue(this, unboxedInlineClassIrType.asmType, unboxedInlineClassIrType) {
|
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)
|
mv.checkcast(unboxedInlineClassIrType.asmType)
|
||||||
MaterialValue(this@ExpressionCodegen, unboxedInlineClassIrType.asmType, unboxedInlineClassIrType)
|
MaterialValue(this@ExpressionCodegen, unboxedInlineClassIrType.asmType, unboxedInlineClassIrType)
|
||||||
.materializeAt(target, irTarget)
|
.materializeAt(target, irTarget, castForReified)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun discard() {
|
override fun discard() {
|
||||||
@@ -951,7 +951,7 @@ class ExpressionCodegen(
|
|||||||
if (!exhaustive) {
|
if (!exhaustive) {
|
||||||
result.discard()
|
result.discard()
|
||||||
} else {
|
} else {
|
||||||
val materializedResult = result.materializedAt(expression.type)
|
val materializedResult = result.materializedAt(typeMapper.mapType(expression.type), expression.type, true)
|
||||||
if (branch.condition.isTrueConst()) {
|
if (branch.condition.isTrueConst()) {
|
||||||
// The rest of the expression is dead code.
|
// The rest of the expression is dead code.
|
||||||
mv.mark(endLabel)
|
mv.mark(endLabel)
|
||||||
@@ -1098,7 +1098,7 @@ class ExpressionCodegen(
|
|||||||
val isExpression = !aTry.type.isUnit()
|
val isExpression = !aTry.type.isUnit()
|
||||||
var savedValue: Int? = null
|
var savedValue: Int? = null
|
||||||
if (isExpression) {
|
if (isExpression) {
|
||||||
tryResult.materializeAt(tryAsmType, aTry.type)
|
tryResult.materializeAt(tryAsmType, aTry.type, true)
|
||||||
savedValue = frameMap.enterTemp(tryAsmType)
|
savedValue = frameMap.enterTemp(tryAsmType)
|
||||||
mv.store(savedValue, tryAsmType)
|
mv.store(savedValue, tryAsmType)
|
||||||
} else {
|
} else {
|
||||||
@@ -1127,7 +1127,7 @@ class ExpressionCodegen(
|
|||||||
val catchBody = clause.result
|
val catchBody = clause.result
|
||||||
val catchResult = catchBody.accept(this, data)
|
val catchResult = catchBody.accept(this, data)
|
||||||
if (savedValue != null) {
|
if (savedValue != null) {
|
||||||
catchResult.materializeAt(tryAsmType, aTry.type)
|
catchResult.materializeAt(tryAsmType, aTry.type, true)
|
||||||
mv.store(savedValue, tryAsmType)
|
mv.store(savedValue, tryAsmType)
|
||||||
} else {
|
} else {
|
||||||
catchResult.discard()
|
catchResult.discard()
|
||||||
@@ -1170,11 +1170,11 @@ class ExpressionCodegen(
|
|||||||
mv.mark(tryCatchBlockEnd)
|
mv.mark(tryCatchBlockEnd)
|
||||||
// TODO: generate a common `finally` for try & catch blocks here? Right now this breaks the inliner.
|
// TODO: generate a common `finally` for try & catch blocks here? Right now this breaks the inliner.
|
||||||
return object : PromisedValue(this, tryAsmType, aTry.type) {
|
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) {
|
if (savedValue != null) {
|
||||||
mv.load(savedValue, tryAsmType)
|
mv.load(savedValue, tryAsmType)
|
||||||
frameMap.leaveTemp(tryAsmType)
|
frameMap.leaveTemp(tryAsmType)
|
||||||
super.materializeAt(target, irTarget)
|
super.materializeAt(target, irTarget, castForReified)
|
||||||
} else {
|
} else {
|
||||||
unitValue.materializeAt(target, irTarget)
|
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.backend.jvm.lower.inlineclasses.InlineClassAbi
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
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.descriptors.toIrBasedKotlinType
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
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.parentAsClass
|
||||||
|
import org.jetbrains.kotlin.ir.util.substitute
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
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.Label
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
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) {
|
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
|
// 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.
|
// 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 erasedSourceType = irType.eraseTypeParameters()
|
||||||
val erasedTargetType = irTarget.eraseTypeParameters()
|
val erasedTargetType = irTarget.eraseTypeParameters()
|
||||||
val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline
|
val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline
|
||||||
@@ -54,9 +60,10 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != target) {
|
if (type != target || (castForReified && irType.anyTypeArgument { it.isReified })) {
|
||||||
StackValue.coerce(type, target, mv)
|
StackValue.coerce(type, target, mv, type == target)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun discard()
|
abstract fun discard()
|
||||||
@@ -68,6 +75,15 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val
|
|||||||
get() = codegen.typeMapper
|
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.
|
// A value that *has* been fully constructed.
|
||||||
class MaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) {
|
class MaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) {
|
||||||
override fun discard() {
|
override fun discard() {
|
||||||
@@ -83,7 +99,7 @@ abstract class BooleanValue(codegen: ExpressionCodegen) :
|
|||||||
abstract fun jumpIfFalse(target: Label)
|
abstract fun jumpIfFalse(target: Label)
|
||||||
abstract fun jumpIfTrue(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 const0 = Label()
|
||||||
val end = Label()
|
val end = Label()
|
||||||
jumpIfFalse(const0)
|
jumpIfFalse(const0)
|
||||||
@@ -101,7 +117,7 @@ abstract class BooleanValue(codegen: ExpressionCodegen) :
|
|||||||
class BooleanConstant(codegen: ExpressionCodegen, val value: Boolean) : BooleanValue(codegen) {
|
class BooleanConstant(codegen: ExpressionCodegen, val value: Boolean) : BooleanValue(codegen) {
|
||||||
override fun jumpIfFalse(target: Label) = if (value) Unit else mv.goTo(target)
|
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 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)
|
mv.iconst(if (value) 1 else 0)
|
||||||
if (Type.BOOLEAN_TYPE != target) {
|
if (Type.BOOLEAN_TYPE != target) {
|
||||||
StackValue.coerce(Type.BOOLEAN_TYPE, target, mv)
|
StackValue.coerce(Type.BOOLEAN_TYPE, target, mv)
|
||||||
@@ -116,10 +132,10 @@ fun PromisedValue.coerceToBoolean(): BooleanValue =
|
|||||||
is BooleanValue -> this
|
is BooleanValue -> this
|
||||||
else -> object : BooleanValue(codegen) {
|
else -> object : BooleanValue(codegen) {
|
||||||
override fun jumpIfFalse(target: Label) =
|
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) =
|
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() {
|
override fun discard() {
|
||||||
this@coerceToBoolean.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 {
|
fun PromisedValue.materializedAt(target: Type, irTarget: IrType, castForReified: Boolean = false): MaterialValue {
|
||||||
materializeAt(target, irTarget)
|
materializeAt(target, irTarget, castForReified)
|
||||||
return MaterialValue(codegen, target, irTarget)
|
return MaterialValue(codegen, target, irTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +182,7 @@ val ExpressionCodegen.unitValue: PromisedValue
|
|||||||
|
|
||||||
val ExpressionCodegen.nullConstant: PromisedValue
|
val ExpressionCodegen.nullConstant: PromisedValue
|
||||||
get() = object : PromisedValue(this, AsmTypes.OBJECT_TYPE, context.irBuiltIns.nothingNType) {
|
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)
|
mv.aconst(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -29,9 +29,9 @@ object UnsafeCoerce : IntrinsicMethod() {
|
|||||||
val arg = expression.getValueArgument(0)!!
|
val arg = expression.getValueArgument(0)!!
|
||||||
val result = arg.accept(codegen, data)
|
val result = arg.accept(codegen, data)
|
||||||
return object : PromisedValue(codegen, toType, to) {
|
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)
|
result.materializeAt(fromType, from)
|
||||||
super.materializeAt(target, irTarget)
|
super.materializeAt(target, irTarget, castForReified)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun discard() {
|
override fun discard() {
|
||||||
|
|||||||
Reference in New Issue
Block a user