JVM: remove TypedNullValue, hack interpreter instead
This commit is contained in:
+1
-1
@@ -669,7 +669,7 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
val value = frame.getLocal(slot)
|
val value = frame.getLocal(slot)
|
||||||
if (value.type == null || !livenessFrame.isAlive(slot)) continue
|
if (value.type == null || !livenessFrame.isAlive(slot)) continue
|
||||||
|
|
||||||
if (value == StrictBasicValue.NULL_VALUE || value is TypedNullValue) {
|
if (value == StrictBasicValue.NULL_VALUE) {
|
||||||
referencesToSpill += slot to null
|
referencesToSpill += slot to null
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-23
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.codegen.coroutines
|
|||||||
import org.jetbrains.kotlin.codegen.StackValue
|
import org.jetbrains.kotlin.codegen.StackValue
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
|
import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
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.Label
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -135,7 +134,7 @@ internal fun performSpilledVariableFieldTypesAnalysis(
|
|||||||
for ((insn, type) in interpreter.needsToBeCoerced) {
|
for ((insn, type) in interpreter.needsToBeCoerced) {
|
||||||
methodNode.instructions.insert(insn, withInstructionAdapter { coerceInt(type, this) })
|
methodNode.instructions.insert(insn, withInstructionAdapter { coerceInt(type, this) })
|
||||||
}
|
}
|
||||||
return FastMethodAnalyzer(thisName, methodNode, NullCheckcastAwareOptimizationBasicInterpreter()).analyze()
|
return FastMethodAnalyzer(thisName, methodNode, OptimizationBasicInterpreter()).analyze()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun coerceInt(to: Type, v: InstructionAdapter) {
|
private fun coerceInt(to: Type, v: InstructionAdapter) {
|
||||||
@@ -159,24 +158,3 @@ private fun Type.isIntLike(): Boolean = when (sort) {
|
|||||||
Type.BOOLEAN, Type.BYTE, Type.CHAR, Type.SHORT -> true
|
Type.BOOLEAN, Type.BYTE, Type.CHAR, Type.SHORT -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents [ACONST_NULL, CHECKCAST Type] sequence result.
|
|
||||||
internal class TypedNullValue(type: Type) : StrictBasicValue(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)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
|
||||||
when {
|
|
||||||
v is TypedNullValue && w is TypedNullValue -> if (v.type == w.type) v else StrictBasicValue.NULL_VALUE
|
|
||||||
v is TypedNullValue -> super.merge(StrictBasicValue.NULL_VALUE, w)
|
|
||||||
w is TypedNullValue -> super.merge(v, StrictBasicValue.NULL_VALUE)
|
|
||||||
else -> super.merge(v, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+4
-1
@@ -204,7 +204,7 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
|
|||||||
case DREM:
|
case DREM:
|
||||||
return StrictBasicValue.DOUBLE_VALUE;
|
return StrictBasicValue.DOUBLE_VALUE;
|
||||||
case AALOAD:
|
case AALOAD:
|
||||||
return StrictBasicValue.REFERENCE_VALUE;
|
return StrictBasicValue.NULL_VALUE;
|
||||||
case LCMP:
|
case LCMP:
|
||||||
case FCMPL:
|
case FCMPL:
|
||||||
case FCMPG:
|
case FCMPG:
|
||||||
@@ -331,6 +331,9 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
|
|||||||
case ATHROW:
|
case ATHROW:
|
||||||
return null;
|
return null;
|
||||||
case CHECKCAST:
|
case CHECKCAST:
|
||||||
|
if (value == StrictBasicValue.NULL_VALUE) {
|
||||||
|
return StrictBasicValue.NULL_VALUE;
|
||||||
|
}
|
||||||
desc = ((TypeInsnNode) insn).desc;
|
desc = ((TypeInsnNode) insn).desc;
|
||||||
return newValue(Type.getObjectType(desc));
|
return newValue(Type.getObjectType(desc));
|
||||||
case INSTANCEOF:
|
case INSTANCEOF:
|
||||||
|
|||||||
+7
-2
@@ -68,7 +68,12 @@ open class StrictBasicValue(type: Type?) : BasicValue(type) {
|
|||||||
override fun hashCode() = (type?.hashCode() ?: 0)
|
override fun hashCode() = (type?.hashCode() ?: 0)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
if (this === UNINITIALIZED_VALUE) return "."
|
val inner = when {
|
||||||
return super.toString()
|
this === REFERENCE_VALUE -> "R"
|
||||||
|
this === NULL_VALUE -> "null"
|
||||||
|
this === UNINITIALIZED_VALUE -> "."
|
||||||
|
else -> super.toString()
|
||||||
|
}
|
||||||
|
return "${this::class.java.simpleName}($inner)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-12
@@ -657,18 +657,6 @@ class ExpressionCodegen(
|
|||||||
val value = initializer.accept(this, data)
|
val value = initializer.accept(this, data)
|
||||||
initializer.markLineNumber(startOffset = true)
|
initializer.markLineNumber(startOffset = true)
|
||||||
value.materializeAt(varType, declaration.type)
|
value.materializeAt(varType, declaration.type)
|
||||||
// We need to generate CHECKCAST from ACONST_NULL here for coroutines,
|
|
||||||
// since otherwise, upon spilling and then unspilling, we will get VerifyError,
|
|
||||||
// because state-machine builder does not know the type of the ACONST_NULL
|
|
||||||
// and assumes it to be Ljava/lang/Object;, which is incorrect.
|
|
||||||
// Generating CHECKCAST hints the state-machine builder the type of the variable
|
|
||||||
// avoiding the issue of VerifyError. See KT-51718
|
|
||||||
// Exception is Ljava/lang/Object;, since CHECKCAST Ljava/lang/Object; is effectively no-op.
|
|
||||||
if (initializer.isNullConst() && varType != OBJECT_TYPE &&
|
|
||||||
(irFunction.isSuspend || irFunction.isInvokeSuspendOfLambda())
|
|
||||||
) {
|
|
||||||
mv.checkcast(varType)
|
|
||||||
}
|
|
||||||
declaration.markLineNumber(startOffset = true)
|
declaration.markLineNumber(startOffset = true)
|
||||||
mv.store(index, varType)
|
mv.store(index, varType)
|
||||||
} else if (declaration.isVisibleInLVT) {
|
} else if (declaration.isVisibleInLVT) {
|
||||||
|
|||||||
Reference in New Issue
Block a user