Generate CHECKCAST after ACONST_NULL in coroutines
If we do not do this, the state-machine builder will not know the type of the ACONST_NULL, defaulting to Object, leading to VerifyError. Alternatively, we could use LVT to deduce the type, but getting types from LVT is something I got rid of long time ago, and I have no desire to return it back. Generating CHECKCAST hints the state-machine builder the type of the variable avoiding the issue of VerifyError. However, this CHECKCAST replaces StrictBasicValue.NULL_VALUE with BasicValue in OptimizationBasicInterpreter. To preserve optimization on not-spilling known nulls, introduce BasicValues, which represent typed nulls and create BasicInterpreter, which is aware of them. This way we have the best of two worlds - we do not spill known nulls, and we know the type of ACONST_NULL. #KT-51718 Fixed
This commit is contained in:
+1
-1
@@ -643,7 +643,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
val value = frame.getLocal(slot)
|
||||
if (value.type == null || !livenessFrame.isAlive(slot)) continue
|
||||
|
||||
if (value == StrictBasicValue.NULL_VALUE) {
|
||||
if (value == StrictBasicValue.NULL_VALUE || value is TypedNullValue) {
|
||||
referencesToSpill += slot to null
|
||||
continue
|
||||
}
|
||||
|
||||
+15
-1
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.codegen.coroutines
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -134,7 +135,7 @@ internal fun performSpilledVariableFieldTypesAnalysis(
|
||||
for ((insn, type) in interpreter.needsToBeCoerced) {
|
||||
methodNode.instructions.insert(insn, withInstructionAdapter { coerceInt(type, this) })
|
||||
}
|
||||
return FastMethodAnalyzer(thisName, methodNode, OptimizationBasicInterpreter()).analyze()
|
||||
return FastMethodAnalyzer(thisName, methodNode, NullCheckcastAwareOptimizationBasicInterpreter()).analyze()
|
||||
}
|
||||
|
||||
private fun coerceInt(to: Type, v: InstructionAdapter) {
|
||||
@@ -157,4 +158,17 @@ private fun coerceInt(to: Type, v: InstructionAdapter) {
|
||||
private fun Type.isIntLike(): Boolean = when (sort) {
|
||||
Type.BOOLEAN, Type.BYTE, Type.CHAR, Type.SHORT -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
// Represents [ACONST_NULL, CHECKCAST Type] sequence result.
|
||||
internal class TypedNullValue(type: Type) : BasicValue(type)
|
||||
|
||||
// Preserves nulls through CHECKCASTS.
|
||||
private class NullCheckcastAwareOptimizationBasicInterpreter : OptimizationBasicInterpreter() {
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
||||
if (insn.opcode == Opcodes.CHECKCAST && (value == StrictBasicValue.NULL_VALUE || value is TypedNullValue)) {
|
||||
return TypedNullValue(Type.getObjectType((insn as TypeInsnNode).desc))
|
||||
}
|
||||
return super.unaryOperation(insn, value)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user