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
|
package org.jetbrains.kotlin.codegen.optimization
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
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.common.OptimizationBasicInterpreter
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
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
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
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() {
|
class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
|
||||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
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 { it.opcode == Opcodes.CHECKCAST }) return
|
||||||
if (insns.any { ReifiedTypeInliner.isOperationReifiedMarker(it) }) 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 redundantCheckCasts = ArrayList<TypeInsnNode>()
|
||||||
|
|
||||||
val frames = analyze(internalClassName, methodNode, OptimizationBasicInterpreter())
|
val frames = analyze(internalClassName, methodNode, interpreter)
|
||||||
for (i in insns.indices) {
|
for (i in insns.indices) {
|
||||||
val valueType = frames[i]?.top()?.type ?: continue
|
val valueType = frames[i]?.top()?.type ?: continue
|
||||||
val insn = insns[i]
|
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) =
|
private fun isTrivialSubtype(superType: Type, subType: Type) =
|
||||||
superType == subType
|
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.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
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 instructions = method.instructions
|
||||||
private val nInsns = instructions.size()
|
private val nInsns = instructions.size()
|
||||||
|
|
||||||
@@ -92,8 +95,10 @@ class InstructionLivenessAnalyzer(val method: MethodNode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers[insn]?.forEach { tcb ->
|
if (visitExceptionHandlers) {
|
||||||
visitControlFlowEdge(tcb.handler.indexOf)
|
handlers[insn]?.forEach { tcb ->
|
||||||
|
visitControlFlowEdge(tcb.handler.indexOf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,6 +156,7 @@ class InstructionLivenessAnalyzer(val method: MethodNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun computeExceptionHandlersForEachInsn(m: MethodNode) {
|
private fun computeExceptionHandlersForEachInsn(m: MethodNode) {
|
||||||
|
if (!visitExceptionHandlers) return
|
||||||
for (tcb in m.tryCatchBlocks) {
|
for (tcb in m.tryCatchBlocks) {
|
||||||
val begin = tcb.start.indexOf
|
val begin = tcb.start.indexOf
|
||||||
val end = tcb.end.indexOf
|
val end = tcb.end.indexOf
|
||||||
|
|||||||
+12
@@ -4797,6 +4797,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
public void testKt19246() throws Exception {
|
public void testKt19246() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
|
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
|
@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 {
|
public void testKt19246() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+12
@@ -4797,6 +4797,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
public void testKt19246() throws Exception {
|
public void testKt19246() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+10
@@ -4133,6 +4133,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
public void testKt19246() throws Exception {
|
public void testKt19246() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
|
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")
|
@TestMetadata("compiler/testData/codegen/box/classLiteral")
|
||||||
|
|||||||
+6
@@ -3489,6 +3489,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
public void testKt19246() throws Exception {
|
public void testKt19246() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+6
@@ -3531,6 +3531,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
public void testKt19246() throws Exception {
|
public void testKt19246() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+5
@@ -3128,6 +3128,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
|||||||
public void testKt19246() throws Exception {
|
public void testKt19246() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
|
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")
|
@TestMetadata("compiler/testData/codegen/box/classLiteral")
|
||||||
|
|||||||
+24
@@ -4859,6 +4859,18 @@ public class NativeExtBlackBoxTestGenerated extends AbstractNativeBlackBoxTest {
|
|||||||
public void testKt19246() throws Exception {
|
public void testKt19246() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/checkcastOptimization/kt19246.kt");
|
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
|
@Nested
|
||||||
@@ -7956,6 +7968,18 @@ public class NativeExtBlackBoxTestGenerated extends AbstractNativeBlackBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt");
|
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
|
@Test
|
||||||
@TestMetadata("loopInInlineFun.kt")
|
@TestMetadata("loopInInlineFun.kt")
|
||||||
public void testLoopInInlineFun() throws Exception {
|
public void testLoopInInlineFun() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user