From bc2077bfaf531550c177bfe098db0b9b372339f8 Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Wed, 3 Feb 2016 13:49:20 +0300 Subject: [PATCH] More wise lambda search during inlining --- .../codegen/inline/InstructionsAndFrames.kt | 30 ++++++++++++ .../kotlin/codegen/inline/MethodInliner.java | 48 +++++++++++++++++-- .../boxInline/compexStack/asCheck.1.kt | 5 ++ .../boxInline/compexStack/asCheck.2.kt | 18 +++++++ .../boxInline/compexStack/asCheck2.1.kt | 5 ++ .../boxInline/compexStack/asCheck2.2.kt | 18 +++++++ .../codegen/boxInline/compexStack/simple.1.kt | 5 ++ .../codegen/boxInline/compexStack/simple.2.kt | 5 ++ .../boxInline/compexStack/simple2.1.kt | 5 ++ .../boxInline/compexStack/simple2.2.kt | 11 +++++ .../boxInline/compexStack/simple3.1.kt | 7 +++ .../boxInline/compexStack/simple3.2.kt | 11 +++++ .../boxInline/compexStack/simple4.1.kt | 5 ++ .../boxInline/compexStack/simple4.2.kt | 10 ++++ .../BlackBoxInlineCodegenTestGenerated.java | 45 +++++++++++++++++ ...otlinAgainstInlineKotlinTestGenerated.java | 45 +++++++++++++++++ 16 files changed, 269 insertions(+), 4 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InstructionsAndFrames.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/asCheck.1.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/asCheck.2.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/asCheck2.1.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/asCheck2.2.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/simple.1.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/simple.2.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/simple2.1.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/simple2.2.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/simple3.1.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/simple3.2.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/simple4.1.kt create mode 100644 compiler/testData/codegen/boxInline/compexStack/simple4.2.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InstructionsAndFrames.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InstructionsAndFrames.kt new file mode 100644 index 00000000000..2de62f7d935 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InstructionsAndFrames.kt @@ -0,0 +1,30 @@ +/* + * 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.inline + +import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode +import org.jetbrains.org.objectweb.asm.tree.InsnList +import org.jetbrains.org.objectweb.asm.tree.analysis.Frame +import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue + +class InstructionsAndFrames(val frames: Array>, ins: InsnList) { + + val indexes = (0..ins.size() - 1).associate { Pair(ins.get(it), it) } + + operator fun get(node: AbstractInsnNode): Frame? = frames[indexes[node]!!] + +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.java index 1c8002071ce..23e5304898f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.java @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.codegen.inline; import com.google.common.collect.Lists; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.SmartHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.codegen.ClosureCodegen; @@ -427,7 +428,8 @@ public class MethodInliner { catch (AnalyzerException e) { throw wrapException(e, node, "couldn't inline method call"); } - + Set toDelete = new SmartHashSet(); + InstructionsAndFrames instructionsAndFrames = new InstructionsAndFrames(sources, node.instructions); AbstractInsnNode cur = node.instructions.getFirst(); int index = 0; @@ -468,7 +470,7 @@ public class MethodInliner { if (insnNode.getOpcode() == Opcodes.SWAP) { processingInstruction = InlineCodegenUtil.getPrevMeaningful(insnNode); } - lambdaInfo = getLambdaIfExists(processingInstruction); + lambdaInfo = getLambdaIfExistsAndMarkInstructions(processingInstruction, frame, instructionsAndFrames, toDelete); if (lambdaInfo != null) { //remove inlinable access assert processingInstruction != null; @@ -486,7 +488,7 @@ public class MethodInliner { SourceValue sourceValue = frame.getStack(firstParameterIndex + i); if (sourceValue.insns.size() == 1) { AbstractInsnNode insnNode = sourceValue.insns.iterator().next(); - LambdaInfo lambdaInfo = getLambdaIfExists(insnNode); + LambdaInfo lambdaInfo = getLambdaIfExistsAndMarkInstructions(insnNode, frame, instructionsAndFrames, toDelete); if (lambdaInfo != null) { lambdaMapping.put(offset, lambdaInfo); node.instructions.remove(insnNode); @@ -533,6 +535,10 @@ public class MethodInliner { } } + for (AbstractInsnNode insnNode : toDelete) { + node.instructions.remove(insnNode); + } + //clean dead try/catch blocks List blocks = node.tryCatchBlocks; for (Iterator iterator = blocks.iterator(); iterator.hasNext(); ) { @@ -575,7 +581,41 @@ public class MethodInliner { } @Nullable - public LambdaInfo getLambdaIfExists(@Nullable AbstractInsnNode insnNode) { + private LambdaInfo getLambdaIfExistsAndMarkInstructions( + @Nullable AbstractInsnNode insnNode, + @NotNull Frame localFrame, + @NotNull InstructionsAndFrames insAndFrames, + @NotNull Set toDelete + ) { + LambdaInfo lambdaInfo = getLambdaIfExists(insnNode); + + if (lambdaInfo == null && insnNode instanceof VarInsnNode && insnNode.getOpcode() == Opcodes.ALOAD) { + int varIndex = ((VarInsnNode) insnNode).var; + SourceValue local = localFrame.getLocal(varIndex); + if (local.insns.size() == 1) { + AbstractInsnNode storeIns = local.insns.iterator().next(); + if (storeIns instanceof VarInsnNode && storeIns.getOpcode() == Opcodes.ASTORE) { + Frame frame = insAndFrames.get(storeIns); + if (frame != null) { + SourceValue topOfStack = frame.getStack(frame.getStackSize() - 1); + if(topOfStack.insns.size() == 1) { + AbstractInsnNode lambdaAload = topOfStack.insns.iterator().next(); + lambdaInfo = getLambdaIfExistsAndMarkInstructions(lambdaAload, frame, insAndFrames, toDelete); + if (lambdaInfo != null) { + toDelete.add(storeIns); + toDelete.add(lambdaAload); + } + } + } + } + } + } + + return lambdaInfo; + } + + @Nullable + private LambdaInfo getLambdaIfExists(@Nullable AbstractInsnNode insnNode) { if (insnNode == null) { return null; } diff --git a/compiler/testData/codegen/boxInline/compexStack/asCheck.1.kt b/compiler/testData/codegen/boxInline/compexStack/asCheck.1.kt new file mode 100644 index 00000000000..9ee32276d63 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/asCheck.1.kt @@ -0,0 +1,5 @@ +import test.* + +fun box(): String { + return ContentTypeByExtension.processRecords { ext -> ext } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/compexStack/asCheck.2.kt b/compiler/testData/codegen/boxInline/compexStack/asCheck.2.kt new file mode 100644 index 00000000000..cc97b4127e8 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/asCheck.2.kt @@ -0,0 +1,18 @@ +package test + +object ContentTypeByExtension { + inline fun processRecords(crossinline operation: (String) -> String) = + listOf("O", "K").map { + val ext = B(it) + operation(ext.toLowerCase()) + }.joinToString("") +} + + + + +inline fun A.toLowerCase(): String = (this as B).value + +open class A + +open class B(val value: String) : A() \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/compexStack/asCheck2.1.kt b/compiler/testData/codegen/boxInline/compexStack/asCheck2.1.kt new file mode 100644 index 00000000000..9ee32276d63 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/asCheck2.1.kt @@ -0,0 +1,5 @@ +import test.* + +fun box(): String { + return ContentTypeByExtension.processRecords { ext -> ext } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/compexStack/asCheck2.2.kt b/compiler/testData/codegen/boxInline/compexStack/asCheck2.2.kt new file mode 100644 index 00000000000..4c2721f48c3 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/asCheck2.2.kt @@ -0,0 +1,18 @@ +package test + +object ContentTypeByExtension { + inline fun processRecords(crossinline operation: (String) -> String) = + { + val ext = B("OK") + operation(ext.toLowerCase()) + }() +} + + + + +inline fun A.toLowerCase(): String = (this as B).value + +open class A + +open class B(val value: String) : A() \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/compexStack/simple.1.kt b/compiler/testData/codegen/boxInline/compexStack/simple.1.kt new file mode 100644 index 00000000000..603b3f0ca67 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/simple.1.kt @@ -0,0 +1,5 @@ +import test.* + +fun box(): String { + return processRecords { ext -> ext + "K" } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/compexStack/simple.2.kt b/compiler/testData/codegen/boxInline/compexStack/simple.2.kt new file mode 100644 index 00000000000..9917f01aef1 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/simple.2.kt @@ -0,0 +1,5 @@ +package test + +inline fun foo(x: String) = x + +inline fun processRecords(block: (String) -> String) = block(foo("O")) \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/compexStack/simple2.1.kt b/compiler/testData/codegen/boxInline/compexStack/simple2.1.kt new file mode 100644 index 00000000000..34d8b03cb7a --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/simple2.1.kt @@ -0,0 +1,5 @@ +import test.* + +fun box(): String { + return processRecords { "O" + it } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/compexStack/simple2.2.kt b/compiler/testData/codegen/boxInline/compexStack/simple2.2.kt new file mode 100644 index 00000000000..906a69bb551 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/simple2.2.kt @@ -0,0 +1,11 @@ +package test + +inline fun foo(x: String) = x + +class A { + fun test(s: String) = s +} + +inline fun processRecords(block: (String) -> String): String { + return A().test(block(foo("K"))) +} diff --git a/compiler/testData/codegen/boxInline/compexStack/simple3.1.kt b/compiler/testData/codegen/boxInline/compexStack/simple3.1.kt new file mode 100644 index 00000000000..4ecac599c3a --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/simple3.1.kt @@ -0,0 +1,7 @@ +import test.* + +fun box(): String { + val result = processRecords { "B" + it } + + return if (result == "BOK1") "OK" else "fail: $result" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/compexStack/simple3.2.kt b/compiler/testData/codegen/boxInline/compexStack/simple3.2.kt new file mode 100644 index 00000000000..78f4ee4d615 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/simple3.2.kt @@ -0,0 +1,11 @@ +package test + +inline fun foo(x: String, y: String) = x + y + +class A { + fun test(s: String) = s +} + +inline fun processRecords(block: (String) -> String): String { + return A().test(block(foo("O", foo("K", "1")))) +} diff --git a/compiler/testData/codegen/boxInline/compexStack/simple4.1.kt b/compiler/testData/codegen/boxInline/compexStack/simple4.1.kt new file mode 100644 index 00000000000..2d54cb809d3 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/simple4.1.kt @@ -0,0 +1,5 @@ +import test.* + +fun box(): String { + return processRecords { a, b -> a + b} +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/compexStack/simple4.2.kt b/compiler/testData/codegen/boxInline/compexStack/simple4.2.kt new file mode 100644 index 00000000000..372e25c3600 --- /dev/null +++ b/compiler/testData/codegen/boxInline/compexStack/simple4.2.kt @@ -0,0 +1,10 @@ +package test + +inline fun foo(x: String) = x + +fun test(a: String, s: String) = s + + +inline fun processRecords(block: (String, String) -> String): String { + return test("stub", block(foo("O"), foo("K"))) +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java index 24101afaa78..10061b63f45 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java @@ -548,6 +548,51 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo } } + @TestMetadata("compiler/testData/codegen/boxInline/compexStack") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CompexStack extends AbstractBlackBoxInlineCodegenTest { + public void testAllFilesPresentInCompexStack() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/compexStack"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("asCheck.1.kt") + public void testAsCheck() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/asCheck.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("asCheck2.1.kt") + public void testAsCheck2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/asCheck2.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("simple.1.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/simple.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("simple2.1.kt") + public void testSimple2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/simple2.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("simple3.1.kt") + public void testSimple3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/simple3.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("simple4.1.kt") + public void testSimple4() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/simple4.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxInline/complex") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java index 2264f8c50b7..81168d7f138 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -548,6 +548,51 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi } } + @TestMetadata("compiler/testData/codegen/boxInline/compexStack") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CompexStack extends AbstractCompileKotlinAgainstInlineKotlinTest { + public void testAllFilesPresentInCompexStack() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/compexStack"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("asCheck.1.kt") + public void testAsCheck() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/asCheck.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("asCheck2.1.kt") + public void testAsCheck2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/asCheck2.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("simple.1.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/simple.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("simple2.1.kt") + public void testSimple2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/simple2.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("simple3.1.kt") + public void testSimple3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/simple3.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("simple4.1.kt") + public void testSimple4() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/compexStack/simple4.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxInline/complex") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)