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) {