Dead code elimination

#KT-6602 Fixed
 #KT-6305 Fixed
 #KT-5656 Fixed
This commit is contained in:
Denis Zharkov
2014-12-30 14:55:45 +03:00
parent aecb925b7b
commit 5675d2b26b
10 changed files with 218 additions and 10 deletions
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2015 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
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
public class DeadCodeEliminationMethodTransformer : MethodTransformer() {
override fun transform(internalClassName: String, methodNode: MethodNode) {
val frames = MethodTransformer.analyze(internalClassName, methodNode, OptimizationBasicInterpreter())
val insnList = methodNode.instructions
val insnsArray = insnList.toArray()
// Do not remove not meaningful nodes (labels/linenumbers) because they can be referred
// by try/catch blocks or local variables table
// We remove unneeded ones further after all optimizations by calling CommonPackage.prepareForEmitting(methodNode)
insnsArray.zip(frames).filter {
it.second == null && it.first.isMeaningful
}.forEach { insnList.remove(it.first) }
}
}
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer;
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantNullCheckMethodTransformer;
import org.jetbrains.kotlin.codegen.optimization.common.CommonPackage;
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
@@ -35,8 +36,11 @@ import java.util.List;
public class OptimizationMethodVisitor extends MethodVisitor {
private static final int MEMORY_LIMIT_BY_METHOD_MB = 50;
private static final MethodTransformer[] TRANSFORMERS = new MethodTransformer[]{
new RedundantNullCheckMethodTransformer(), new RedundantBoxingMethodTransformer(),
new RedundantGotoMethodTransformer(), new StoreStackBeforeInlineMethodTransformer()
new RedundantNullCheckMethodTransformer(),
new RedundantBoxingMethodTransformer(),
new DeadCodeEliminationMethodTransformer(),
new RedundantGotoMethodTransformer(),
new StoreStackBeforeInlineMethodTransformer()
};
private final MethodNode methodNode;
@@ -70,6 +74,7 @@ public class OptimizationMethodVisitor extends MethodVisitor {
for (MethodTransformer transformer : TRANSFORMERS) {
transformer.transform("fake", methodNode);
}
CommonPackage.prepareForEmitting(methodNode);
}
methodNode.accept(new EndIgnoringMethodVisitorDecorator(Opcodes.ASM5, delegate));
@@ -22,6 +22,7 @@ import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
public class RedundantGotoMethodTransformer : MethodTransformer() {
/**
@@ -53,9 +54,3 @@ public class RedundantGotoMethodTransformer : MethodTransformer() {
}
}
}
private val AbstractInsnNode.isMeaningful : Boolean get() =
when (this.getType()) {
AbstractInsnNode.LABEL, AbstractInsnNode.LINE, AbstractInsnNode.FRAME -> false
else -> true
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2014 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.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
import org.jetbrains.org.objectweb.asm.tree.MethodNode
val AbstractInsnNode.isMeaningful : Boolean get() =
when (this.getType()) {
AbstractInsnNode.LABEL, AbstractInsnNode.LINE, AbstractInsnNode.FRAME -> false
else -> true
}
class InsnStream(val from: AbstractInsnNode, val to: AbstractInsnNode?) : Stream<AbstractInsnNode> {
override fun iterator(): Iterator<AbstractInsnNode> {
return object : Iterator<AbstractInsnNode> {
var current = from
override fun next(): AbstractInsnNode {
val result = current
current = current.getNext()
return result
}
override fun hasNext() = current != to
}
}
}
fun MethodNode.prepareForEmitting() {
tryCatchBlocks = tryCatchBlocks.filter { tcb ->
InsnStream(tcb.start, tcb.end).any { insn ->
insn.isMeaningful
}
}
// local variables with live ranges starting after last meaningful instruction lead to VerifyError
localVariables = localVariables.filter { lv ->
InsnStream(lv.start, instructions.getLast()).any { insn ->
insn.isMeaningful
}
}
// We should remove linenumbers after last meaningful instruction
// because they point to index of non-existing instruction and it leads to VerifyError
var current = instructions.getLast()
while (!current.isMeaningful) {
val prev = current.getPrevious()
if (current.getType() == AbstractInsnNode.LINE) {
instructions.remove(current)
}
current = prev
}
}