JVM KT-47851 fix redundant checkcast elimination
This commit is contained in:
committed by
teamcityserver
parent
8b066fd345
commit
e525e25518
+40
-1
@@ -17,13 +17,17 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InstructionLivenessAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
|
||||
class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
@@ -31,9 +35,21 @@ class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
|
||||
if (!insns.any { it.opcode == Opcodes.CHECKCAST }) return
|
||||
if (insns.any { ReifiedTypeInliner.isOperationReifiedMarker(it) }) return
|
||||
|
||||
val typeAdjustmentForALoads = getTypeAdjustmentForALoadInstructions(insns, methodNode)
|
||||
val interpreter = object : OptimizationBasicInterpreter() {
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue {
|
||||
val adjustedType = typeAdjustmentForALoads[insn]
|
||||
return if (adjustedType != null)
|
||||
newValue(adjustedType)
|
||||
?: throw AssertionError("Local variable type can't be VOID: $adjustedType")
|
||||
else
|
||||
super.copyOperation(insn, value)
|
||||
}
|
||||
}
|
||||
|
||||
val redundantCheckCasts = ArrayList<TypeInsnNode>()
|
||||
|
||||
val frames = analyze(internalClassName, methodNode, OptimizationBasicInterpreter())
|
||||
val frames = analyze(internalClassName, methodNode, interpreter)
|
||||
for (i in insns.indices) {
|
||||
val valueType = frames[i]?.top()?.type ?: continue
|
||||
val insn = insns[i]
|
||||
@@ -57,6 +73,29 @@ class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTypeAdjustmentForALoadInstructions(
|
||||
insns: Array<AbstractInsnNode>,
|
||||
methodNode: MethodNode
|
||||
): Map<AbstractInsnNode, Type> {
|
||||
val isNonHandler = InstructionLivenessAnalyzer(methodNode, visitExceptionHandlers = false).analyze()
|
||||
|
||||
val result = HashMap<AbstractInsnNode, Type>()
|
||||
for (lv in methodNode.localVariables) {
|
||||
val startIndex = methodNode.instructions.indexOf(lv.start)
|
||||
val endIndex = methodNode.instructions.indexOf(lv.end)
|
||||
for (i in startIndex until endIndex) {
|
||||
val insn = insns[i]
|
||||
// If we are in exception handler (or in dead code, but it really doesn't matter here, since dead code should not be seen
|
||||
// by data flow analyzer), treat ALOAD instructions as producing a value of declared local variable type.
|
||||
// Otherwise, resulting bytecode might fail verification on JDK 1.8+ because of inexact frames (see KT-47851).
|
||||
if (insn.opcode == Opcodes.ALOAD && (insn as VarInsnNode).`var` == lv.index && !isNonHandler[i]) {
|
||||
result[insn] = Type.getType(lv.desc)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun isTrivialSubtype(superType: Type, subType: Type) =
|
||||
superType == subType
|
||||
|
||||
|
||||
+9
-3
@@ -37,7 +37,10 @@ package org.jetbrains.kotlin.codegen.optimization.common
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
class InstructionLivenessAnalyzer(val method: MethodNode) {
|
||||
class InstructionLivenessAnalyzer(
|
||||
val method: MethodNode,
|
||||
val visitExceptionHandlers: Boolean = true
|
||||
) {
|
||||
private val instructions = method.instructions
|
||||
private val nInsns = instructions.size()
|
||||
|
||||
@@ -92,8 +95,10 @@ class InstructionLivenessAnalyzer(val method: MethodNode) {
|
||||
}
|
||||
}
|
||||
|
||||
handlers[insn]?.forEach { tcb ->
|
||||
visitControlFlowEdge(tcb.handler.indexOf)
|
||||
if (visitExceptionHandlers) {
|
||||
handlers[insn]?.forEach { tcb ->
|
||||
visitControlFlowEdge(tcb.handler.indexOf)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,6 +156,7 @@ class InstructionLivenessAnalyzer(val method: MethodNode) {
|
||||
}
|
||||
|
||||
private fun computeExceptionHandlersForEachInsn(m: MethodNode) {
|
||||
if (!visitExceptionHandlers) return
|
||||
for (tcb in m.tryCatchBlocks) {
|
||||
val begin = tcb.start.indexOf
|
||||
val end = tcb.end.indexOf
|
||||
|
||||
Reference in New Issue
Block a user