Dead code elimination
#KT-6602 Fixed #KT-6305 Fixed #KT-5656 Fixed
This commit is contained in:
+37
@@ -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) }
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -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));
|
||||
|
||||
+1
-6
@@ -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)
|
||||
|
||||
+16
-1
@@ -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})
|
||||
|
||||
Reference in New Issue
Block a user