JVM KT-47851 fix redundant checkcast elimination

This commit is contained in:
Dmitry Petrov
2021-11-18 20:12:38 +03:00
committed by teamcityserver
parent 8b066fd345
commit e525e25518
12 changed files with 187 additions and 4 deletions
@@ -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
@@ -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
@@ -4797,6 +4797,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
public void testKt19246() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
}
@Test
@TestMetadata("kt47851.kt")
public void testKt47851() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851.kt");
}
@Test
@TestMetadata("kt47851a.kt")
public void testKt47851a() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851a.kt");
}
}
@Nested
@@ -0,0 +1,21 @@
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: result.getMethod OK in FE1.0, unresolved in FIR
class C(val value: String) {
fun getField() = value
fun getMethod() {}
}
fun foo(): Any {
var result: Any = ""
result = C("OK")
try {
result = result.getField()
} catch (e: Exception) {
result.getMethod()
}
return result
}
fun box() = foo().toString()
@@ -0,0 +1,30 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: result.getMethod OK in FE1.0, unresolved in FIR
// WITH_RUNTIME
// FULL_JDK
val defaultStringConverter = fun(s: String): Any {
var result: Any = s
var m: Array<String>? = arrayOf("1", "2", "3", "4")
if (m != null) {
val fname = m[4]
try {
result = Class.forName(m[1])
if (fname != "") {
try {
val f = result.getField(fname)
result = f.get(null)
} catch (nfe: NoSuchFieldException) {
val meth = result.getMethod(fname)
}
}
} catch (cnfe: ClassNotFoundException) {
}
}
return result
}
// Just check that there's no VerifyError.
// Semantics is checked in kt47851.kt.
fun box() = "OK"
@@ -4725,6 +4725,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testKt19246() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
}
@Test
@TestMetadata("kt47851.kt")
public void testKt47851() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851.kt");
}
@Test
@TestMetadata("kt47851a.kt")
public void testKt47851a() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851a.kt");
}
}
@Nested
@@ -4797,6 +4797,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
public void testKt19246() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
}
@Test
@TestMetadata("kt47851.kt")
public void testKt47851() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851.kt");
}
@Test
@TestMetadata("kt47851a.kt")
public void testKt47851a() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851a.kt");
}
}
@Nested
@@ -4133,6 +4133,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
public void testKt19246() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
}
@TestMetadata("kt47851.kt")
public void testKt47851() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851.kt");
}
@TestMetadata("kt47851a.kt")
public void testKt47851a() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851a.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/classLiteral")
@@ -3489,6 +3489,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
public void testKt19246() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
}
@Test
@TestMetadata("kt47851.kt")
public void testKt47851() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851.kt");
}
}
@Nested
@@ -3531,6 +3531,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
public void testKt19246() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
}
@Test
@TestMetadata("kt47851.kt")
public void testKt47851() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851.kt");
}
}
@Nested
@@ -3128,6 +3128,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
public void testKt19246() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
}
@TestMetadata("kt47851.kt")
public void testKt47851() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/classLiteral")
@@ -4859,6 +4859,18 @@ public class NativeExtBlackBoxTestGenerated extends AbstractNativeBlackBoxTest {
public void testKt19246() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
}
@Test
@TestMetadata("kt47851.kt")
public void testKt47851() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851.kt");
}
@Test
@TestMetadata("kt47851a.kt")
public void testKt47851a() throws Exception {
runTest("compiler/testData/codegen/box/checkcastOptimization/kt47851a.kt");
}
}
@Nested
@@ -7956,6 +7968,18 @@ public class NativeExtBlackBoxTestGenerated extends AbstractNativeBlackBoxTest {
runTest("compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt");
}
@Test
@TestMetadata("kt49615.kt")
public void testKt49615() throws Exception {
runTest("compiler/testData/codegen/box/constructorCall/kt49615.kt");
}
@Test
@TestMetadata("kt49615a.kt")
public void testKt49615a() throws Exception {
runTest("compiler/testData/codegen/box/constructorCall/kt49615a.kt");
}
@Test
@TestMetadata("loopInInlineFun.kt")
public void testLoopInInlineFun() throws Exception {