JVM prune exception edges in DFA in some cases

This commit is contained in:
Dmitry Petrov
2022-02-22 15:09:16 +03:00
committed by Space
parent 817f9f13b7
commit 872b81ac8a
14 changed files with 219 additions and 73 deletions
@@ -82,13 +82,15 @@ class UninitializedStoresProcessor(private val methodNode: MethodNode) {
private val isInSpecialMethod = methodNode.name == "<init>" || methodNode.name == "<clinit>"
fun run() {
val interpreter = UninitializedNewValueMarkerInterpreter(methodNode.instructions)
if (methodNode.instructions.toArray().none { it.opcode == Opcodes.NEW })
return
val frames = CustomFramesMethodAnalyzer("fake", methodNode, interpreter, this::UninitializedNewValueFrame).analyze()
val interpreter = UninitializedNewValueMarkerInterpreter(methodNode.instructions)
val analyzer = object : FastMethodAnalyzer<BasicValue>("fake", methodNode, interpreter, pruneExceptionEdges = true) {
override fun newFrame(nLocals: Int, nStack: Int): Frame<BasicValue> =
UninitializedNewValueFrame(nLocals, nStack)
}
val frames = analyzer.analyze()
interpreter.analyzePopInstructions(frames)
for ((index, insn) in methodNode.instructions.toArray().withIndex()) {
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen.optimization
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
import org.jetbrains.kotlin.codegen.optimization.common.InstructionLivenessAnalyzer
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
@@ -49,22 +50,21 @@ class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
val redundantCheckCasts = ArrayList<TypeInsnNode>()
val frames = analyze(internalClassName, methodNode, interpreter)
val frames = FastMethodAnalyzer(internalClassName, methodNode, interpreter, pruneExceptionEdges = true).analyze()
for (i in insns.indices) {
val valueType = frames[i]?.top()?.type ?: continue
val insn = insns[i]
if (insn is TypeInsnNode) {
val insnType = Type.getObjectType(insn.desc)
if (insn.opcode == Opcodes.CHECKCAST) {
val typeInsn = insn as TypeInsnNode
val insnType = Type.getObjectType(typeInsn.desc)
if (!isTrivialSubtype(insnType, valueType)) continue
//Keep casts to multiarray types cause dex doesn't recognize ANEWARRAY [Ljava/lang/Object; as Object [][], but Object [] type
//It's not clear is it bug in dex or not and maybe best to distinguish such types from MULTINEWARRRAY ones in method analyzer
if (isMultiArrayType(insnType)) continue
if (insn.opcode == Opcodes.CHECKCAST) {
redundantCheckCasts.add(insn)
}
redundantCheckCasts.add(typeInsn)
}
}
@@ -1,29 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
class CustomFramesMethodAnalyzer<V : Value>(
owner: String, method: MethodNode, interpreter: Interpreter<V>,
private val frameFactory: (Int, Int) -> Frame<V>
) : FastMethodAnalyzer<V>(owner, method, interpreter) {
override fun newFrame(nLocals: Int, nStack: Int) = frameFactory(nLocals, nStack)
}
@@ -47,10 +47,12 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value
* @see org.jetbrains.kotlin.codegen.optimization.fixStack.FastStackAnalyzer
*/
@Suppress("DuplicatedCode")
open class FastMethodAnalyzer<V : Value>(
open class FastMethodAnalyzer<V : Value>
@JvmOverloads constructor(
private val owner: String,
val method: MethodNode,
private val interpreter: Interpreter<V>
private val method: MethodNode,
private val interpreter: Interpreter<V>,
private val pruneExceptionEdges: Boolean = false
) {
private val insnsArray = method.instructions.toArray()
private val nInsns = method.instructions.size()
@@ -74,6 +76,11 @@ open class FastMethodAnalyzer<V : Value>(
computeExceptionHandlersForEachInsn(method)
initMergeNodes()
val isTcbStart = BooleanArray(nInsns)
for (tcb in method.tryCatchBlocks) {
isTcbStart[tcb.start.indexOf()] = true
}
val current = newFrame(method.maxLocals, method.maxStack)
val handler = newFrame(method.maxLocals, method.maxStack)
initLocals(current)
@@ -107,14 +114,20 @@ open class FastMethodAnalyzer<V : Value>(
}
}
handlers[insn]?.forEach { tcb ->
val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable")
val jump = tcb.handler.indexOf()
// Jump by an exception edge clears the stack, putting exception on top.
// So, unless we have a store operation, anything we change on stack would be lost,
// and there's no need to analyze exception handler again.
// Add an exception edge from TCB start to make sure handler itself is still visited.
if (!pruneExceptionEdges || insnOpcode in Opcodes.ISTORE..Opcodes.ASTORE || insnOpcode == Opcodes.IINC || isTcbStart[insn]) {
handlers[insn]?.forEach { tcb ->
val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable")
val jump = tcb.handler.indexOf()
handler.init(f)
handler.clearStack()
handler.push(interpreter.newValue(exnType))
mergeControlFlowEdge(jump, handler)
handler.init(f)
handler.clearStack()
handler.push(interpreter.newValue(exnType))
mergeControlFlowEdge(jump, handler)
}
}
} catch (e: AnalyzerException) {
@@ -8787,12 +8787,6 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/controlStructures/quicksort.kt");
}
@Test
@TestMetadata("slowHtmlLikeDsl.kt")
public void testSlowHtmlLikeDsl() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowHtmlLikeDsl.kt");
}
@Test
@TestMetadata("tcbInEliminatedCondition.kt")
public void testTcbInEliminatedCondition() throws Exception {
@@ -9451,6 +9445,28 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/slowDsl")
@TestDataPath("$PROJECT_ROOT")
public class SlowDsl {
@Test
public void testAllFilesPresentInSlowDsl() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/slowDsl"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("slowHtmlLikeDsl.kt")
public void testSlowHtmlLikeDsl() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowDsl/slowHtmlLikeDsl.kt");
}
@Test
@TestMetadata("slowHtmlLikeDslNoInline.kt")
public void testSlowHtmlLikeDslNoInline() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowDsl/slowHtmlLikeDslNoInline.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
@TestDataPath("$PROJECT_ROOT")
@@ -35,9 +35,13 @@ inline fun Builder.t2(body: Builder.() -> Unit) {
val expectedLength = 1906
fun doStuff(b: Builder) {
b.t2 { t2 { t2 { t2 { t2 { t2 { t2 { text("1") } } } } } } }
}
fun box(): String {
val b = Builder("")
b.t2 { t2 { t2 { t2 { t2 { t2 { t2 { text("1") } } } } } } }
doStuff(b)
if (b.content.length != expectedLength)
return "${b.content.length}"
else
@@ -0,0 +1,45 @@
// TARGET_BACKEND: JVM
// ^ this test might be rather slow
class Builder(var content: String)
fun Builder.begin(t: String) {
content += "<$t>"
}
fun Builder.text(t: String) {
content += t
}
fun Builder.end(t: String) {
content += "</$t>"
}
fun err(e: Throwable) {}
fun Builder.tag(t: String, body: Builder.() -> Unit) {
begin(t)
try {
body()
} catch (e: Throwable) {
err(e)
} finally {
end(t)
}
}
fun Builder.t2(body: Builder.() -> Unit) {
tag("t", body)
tag("t", body)
}
val expectedLength = 1906
fun box(): String {
val b = Builder("")
b.t2 { t2 { t2 { t2 { t2 { t2 { t2 { text("1") } } } } } } }
if (b.content.length != expectedLength)
return "${b.content.length}"
else
return "OK"
}
@@ -8667,12 +8667,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/controlStructures/quicksort.kt");
}
@Test
@TestMetadata("slowHtmlLikeDsl.kt")
public void testSlowHtmlLikeDsl() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowHtmlLikeDsl.kt");
}
@Test
@TestMetadata("tcbInEliminatedCondition.kt")
public void testTcbInEliminatedCondition() throws Exception {
@@ -9331,6 +9325,28 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/slowDsl")
@TestDataPath("$PROJECT_ROOT")
public class SlowDsl {
@Test
public void testAllFilesPresentInSlowDsl() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/slowDsl"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@Test
@TestMetadata("slowHtmlLikeDsl.kt")
public void testSlowHtmlLikeDsl() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowDsl/slowHtmlLikeDsl.kt");
}
@Test
@TestMetadata("slowHtmlLikeDslNoInline.kt")
public void testSlowHtmlLikeDslNoInline() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowDsl/slowHtmlLikeDslNoInline.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
@TestDataPath("$PROJECT_ROOT")
@@ -8787,12 +8787,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/controlStructures/quicksort.kt");
}
@Test
@TestMetadata("slowHtmlLikeDsl.kt")
public void testSlowHtmlLikeDsl() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowHtmlLikeDsl.kt");
}
@Test
@TestMetadata("tcbInEliminatedCondition.kt")
public void testTcbInEliminatedCondition() throws Exception {
@@ -9451,6 +9445,28 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/slowDsl")
@TestDataPath("$PROJECT_ROOT")
public class SlowDsl {
@Test
public void testAllFilesPresentInSlowDsl() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/slowDsl"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("slowHtmlLikeDsl.kt")
public void testSlowHtmlLikeDsl() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowDsl/slowHtmlLikeDsl.kt");
}
@Test
@TestMetadata("slowHtmlLikeDslNoInline.kt")
public void testSlowHtmlLikeDslNoInline() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowDsl/slowHtmlLikeDslNoInline.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
@TestDataPath("$PROJECT_ROOT")
@@ -6698,11 +6698,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/controlStructures/quicksort.kt");
}
@TestMetadata("slowHtmlLikeDsl.kt")
public void testSlowHtmlLikeDsl() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowHtmlLikeDsl.kt");
}
@TestMetadata("tcbInEliminatedCondition.kt")
public void testTcbInEliminatedCondition() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/tcbInEliminatedCondition.kt");
@@ -7284,6 +7279,29 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/controlStructures/slowDsl")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SlowDsl extends AbstractLightAnalysisModeTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInSlowDsl() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/slowDsl"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("slowHtmlLikeDsl.kt")
public void testSlowHtmlLikeDsl() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowDsl/slowHtmlLikeDsl.kt");
}
@TestMetadata("slowHtmlLikeDslNoInline.kt")
public void testSlowHtmlLikeDslNoInline() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/slowDsl/slowHtmlLikeDslNoInline.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -6525,6 +6525,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/slowDsl")
@TestDataPath("$PROJECT_ROOT")
public class SlowDsl {
@Test
public void testAllFilesPresentInSlowDsl() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/slowDsl"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
@TestDataPath("$PROJECT_ROOT")
@@ -6567,6 +6567,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/slowDsl")
@TestDataPath("$PROJECT_ROOT")
public class SlowDsl {
@Test
public void testAllFilesPresentInSlowDsl() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/slowDsl"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
@TestDataPath("$PROJECT_ROOT")
@@ -5691,6 +5691,19 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
}
}
@TestMetadata("compiler/testData/codegen/box/controlStructures/slowDsl")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SlowDsl extends AbstractIrCodegenBoxWasmTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
}
public void testAllFilesPresentInSlowDsl() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/slowDsl"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
}
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -7369,6 +7369,18 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/slowDsl")
@TestDataPath("$PROJECT_ROOT")
@Tag("codegen")
@UseExtTestCaseGroupProvider()
public class SlowDsl {
@Test
public void testAllFilesPresentInSlowDsl() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/slowDsl"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true);
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
@TestDataPath("$PROJECT_ROOT")