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
|
||||
|
||||
+12
@@ -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"
|
||||
+12
@@ -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
|
||||
|
||||
+12
@@ -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
|
||||
|
||||
+10
@@ -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")
|
||||
|
||||
+6
@@ -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
|
||||
|
||||
+6
@@ -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
|
||||
|
||||
+5
@@ -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")
|
||||
|
||||
+24
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user