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
}
}
@@ -0,0 +1,12 @@
import kotlin.test.assertEquals
fun foo(): Int {
return 1
// val xyz has empty live range because everything after return will be removed as dead
val xyz = 1
}
fun box(): String {
assertEquals(1, foo())
return "OK"
}
@@ -0,0 +1,16 @@
fun foo(x: Any?) {}
fun box() {
val x: Int? = 1
x!!
val z: Int? = if (1 == 1) x else null
z!!
foo(1 as java.lang.Integer)
}
// 0 IFNULL
// 1 IFNONNULL
// 1 throwNpe
// 0 ATHROW
@@ -0,0 +1,7 @@
fun foo() {
return
// val xyz has empty live range because everything after return will be removed as dead
val xyz = 1
}
// 0 xyz
@@ -0,0 +1,23 @@
import kotlin.test.assertEquals
fun foo(value: Boolean): Int {
if (value)
return 1
else
return 2
}
fun box(): String {
assertEquals(1, foo(true))
assertEquals(2, foo(false))
return "OK"
}
/*
3 return's are defined in functions
2 are from package facade
1 is from package clinit
*/
// 6 RETURN
@@ -30,7 +30,7 @@ import java.util.regex.Pattern;
@SuppressWarnings("all")
@TestMetadata("compiler/testData/codegen/bytecodeText")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({BytecodeTextTestGenerated.BoxingOptimization.class, BytecodeTextTestGenerated.Constants.class, BytecodeTextTestGenerated.DirectInvoke.class, BytecodeTextTestGenerated.Inline.class, BytecodeTextTestGenerated.LazyCodegen.class, BytecodeTextTestGenerated.LineNumbers.class, BytecodeTextTestGenerated.Statements.class, BytecodeTextTestGenerated.StaticFields.class, BytecodeTextTestGenerated.StoreStackBeforeInline.class, BytecodeTextTestGenerated.When.class, BytecodeTextTestGenerated.WhenEnumOptimization.class, BytecodeTextTestGenerated.WhenStringOptimization.class})
@InnerTestClasses({BytecodeTextTestGenerated.BoxingOptimization.class, BytecodeTextTestGenerated.Constants.class, BytecodeTextTestGenerated.DeadCodeElimination.class, BytecodeTextTestGenerated.DirectInvoke.class, BytecodeTextTestGenerated.Inline.class, BytecodeTextTestGenerated.LazyCodegen.class, BytecodeTextTestGenerated.LineNumbers.class, BytecodeTextTestGenerated.Statements.class, BytecodeTextTestGenerated.StaticFields.class, BytecodeTextTestGenerated.StoreStackBeforeInline.class, BytecodeTextTestGenerated.When.class, BytecodeTextTestGenerated.WhenEnumOptimization.class, BytecodeTextTestGenerated.WhenStringOptimization.class})
@RunWith(JUnit3RunnerWithInners.class)
public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
@TestMetadata("accessorForProtected.kt")
@@ -301,6 +301,33 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DeadCodeElimination extends AbstractBytecodeTextTest {
public void testAllFilesPresentInDeadCodeElimination() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/deadCodeElimination"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("boxing.kt")
public void testBoxing() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt");
doTest(fileName);
}
@TestMetadata("emptyVariableRange.kt")
public void testEmptyVariableRange() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/emptyVariableRange.kt");
doTest(fileName);
}
@TestMetadata("lastReturn.kt")
public void testLastReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/lastReturn.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/directInvoke")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -30,7 +30,7 @@ import java.util.regex.Pattern;
@SuppressWarnings("all")
@TestMetadata("compiler/testData/codegen/boxWithStdlib")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Annotations.class, BlackBoxWithStdlibCodegenTestGenerated.Arrays.class, BlackBoxWithStdlibCodegenTestGenerated.BoxingOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.CallableReference.class, BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.Classes.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.DefaultArguments.class, BlackBoxWithStdlibCodegenTestGenerated.Enum.class, BlackBoxWithStdlibCodegenTestGenerated.Evaluate.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.HashPMap.class, BlackBoxWithStdlibCodegenTestGenerated.Intrinsics.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.LazyCodegen.class, BlackBoxWithStdlibCodegenTestGenerated.LocalFunInLambda.class, BlackBoxWithStdlibCodegenTestGenerated.MultiDeclForArray.class, BlackBoxWithStdlibCodegenTestGenerated.Native.class, BlackBoxWithStdlibCodegenTestGenerated.NonLocalReturns.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformNames.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformStatic.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformTypes.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Reflection.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.Reified.class, BlackBoxWithStdlibCodegenTestGenerated.StoreStackBeforeInline.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class, BlackBoxWithStdlibCodegenTestGenerated.ToArray.class, BlackBoxWithStdlibCodegenTestGenerated.Vararg.class, BlackBoxWithStdlibCodegenTestGenerated.When.class, BlackBoxWithStdlibCodegenTestGenerated.WhenEnumOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.WhenStringOptimization.class})
@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Annotations.class, BlackBoxWithStdlibCodegenTestGenerated.Arrays.class, BlackBoxWithStdlibCodegenTestGenerated.BoxingOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.CallableReference.class, BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.Classes.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.DeadCodeElimination.class, BlackBoxWithStdlibCodegenTestGenerated.DefaultArguments.class, BlackBoxWithStdlibCodegenTestGenerated.Enum.class, BlackBoxWithStdlibCodegenTestGenerated.Evaluate.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.HashPMap.class, BlackBoxWithStdlibCodegenTestGenerated.Intrinsics.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.LazyCodegen.class, BlackBoxWithStdlibCodegenTestGenerated.LocalFunInLambda.class, BlackBoxWithStdlibCodegenTestGenerated.MultiDeclForArray.class, BlackBoxWithStdlibCodegenTestGenerated.Native.class, BlackBoxWithStdlibCodegenTestGenerated.NonLocalReturns.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformNames.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformStatic.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformTypes.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Reflection.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.Reified.class, BlackBoxWithStdlibCodegenTestGenerated.StoreStackBeforeInline.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class, BlackBoxWithStdlibCodegenTestGenerated.ToArray.class, BlackBoxWithStdlibCodegenTestGenerated.Vararg.class, BlackBoxWithStdlibCodegenTestGenerated.When.class, BlackBoxWithStdlibCodegenTestGenerated.WhenEnumOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.WhenStringOptimization.class})
@RunWith(JUnit3RunnerWithInners.class)
public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInBoxWithStdlib() throws Exception {
@@ -1186,6 +1186,21 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DeadCodeElimination extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInDeadCodeElimination() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/deadCodeElimination"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("emptyVariableRange.kt")
public void testEmptyVariableRange() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/deadCodeElimination/emptyVariableRange.kt");
doTestWithStdlib(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/defaultArguments")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({DefaultArguments.Constructor.class})