API version calls preprocessing, 1st passing test

This commit is contained in:
Dmitry Petrov
2017-09-22 17:20:45 +03:00
parent 4e7c4c3976
commit fd16035bbd
8 changed files with 208 additions and 4 deletions
@@ -263,7 +263,8 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
"Method inlining " + sourceCompiler.callElementText,
createNestedSourceMapper(nodeAndSmap, defaultSourceMapper), info.callSiteInfo,
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
!isInlinedToInlineFunInKotlinRuntime()
) //with captured
val remapper = LocalVarRemapper(parameters, initialFrameSize)
@@ -297,6 +298,14 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
return result
}
private fun isInlinedToInlineFunInKotlinRuntime(): Boolean {
val codegen = this.codegen as? ExpressionCodegen ?: return false
val caller = codegen.context.functionDescriptor
if (!caller.isInline) return false
val callerPackage = DescriptorUtils.getParentOfType(caller, PackageFragmentDescriptor::class.java) ?: return false
return callerPackage.fqName.asString().startsWith("kotlin.")
}
private fun generateClosuresBodies() {
for (info in expressionMap.values) {
info.generateLambdaBody(sourceCompiler, reifiedTypeInliner)
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ClosureCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
@@ -46,7 +47,8 @@ class MethodInliner(
private val errorPrefix: String,
private val sourceMapper: SourceMapper,
private val inlineCallSiteInfo: InlineCallSiteInfo,
private val inlineOnlySmapSkipper: InlineOnlySmapSkipper? //non null only for root
private val inlineOnlySmapSkipper: InlineOnlySmapSkipper?, //non null only for root
private val shouldPreprocessApiVersionCalls: Boolean = false
) {
private val typeMapper = inliningContext.state.typeMapper
private val invokeCalls = ArrayList<InvokeCall>()
@@ -394,7 +396,7 @@ class MethodInliner(
): MethodNode {
val processingNode = prepareNode(node, finallyDeepShift)
normalizeNodeBeforeInline(processingNode, labelOwner)
preprocessNodeBeforeInline(processingNode, labelOwner)
val sources = analyzeMethodNodeBeforeInline(processingNode)
@@ -508,7 +510,7 @@ class MethodInliner(
return processingNode
}
private fun normalizeNodeBeforeInline(node: MethodNode, labelOwner: LabelOwner) {
private fun preprocessNodeBeforeInline(node: MethodNode, labelOwner: LabelOwner) {
try {
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
}
@@ -516,6 +518,11 @@ class MethodInliner(
throw wrapException(e, node, "couldn't inline method call")
}
if (shouldPreprocessApiVersionCalls) {
val targetApiVersion = inliningContext.state.languageVersionSettings.apiVersion
ApiVersionCallsPreprocessingMethodTransformer(targetApiVersion).transform("fake", node)
}
val frames = analyzeMethodNodeBeforeInline(node)
val localReturnsNormalizer = LocalReturnsNormalizer()
@@ -0,0 +1,87 @@
/*
* Copyright 2010-2017 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.kotlin.codegen.optimization.boxing.isMethodInsnWith
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.MavenComparableVersion
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.*
class ApiVersionCallsPreprocessingMethodTransformer(private val targetApiVersion: ApiVersion) : MethodTransformer() {
private val constantConditionElimination = ConstantConditionEliminationMethodTransformer()
override fun transform(internalClassName: String, methodNode: MethodNode) {
for (insn in methodNode.instructions.toArray()) {
if (!insn.isApiVersionIsAtLeastCall()) continue
val prev3 = insn.previous ?: continue
val minor = prev3.getIntConstValue() ?: continue
val prev2 = prev3.previous ?: continue
val major = prev2.getIntConstValue() ?: continue
val prev1 = prev2.previous ?: continue
val epic = prev1.getIntConstValue() ?: continue
val atLeastVersion = MavenComparableVersion("$epic.$major.$minor")
val replacementInsn =
if (targetApiVersion.version >= atLeastVersion)
InsnNode(Opcodes.ICONST_1)
else
InsnNode(Opcodes.ICONST_0)
methodNode.instructions.run {
remove(prev1)
remove(prev2)
remove(prev3)
set(insn, replacementInsn)
}
}
constantConditionElimination.transform(internalClassName, methodNode)
}
private fun AbstractInsnNode.isApiVersionIsAtLeastCall(): Boolean =
isMethodInsnWith(Opcodes.INVOKESTATIC) {
owner.startsWith("kotlin/internal") &&
name == "apiVersionIsAtLeast" &&
desc == "(III)Z"
}
private fun AbstractInsnNode.getIntConstValue(): Int? =
when (this) {
is InsnNode ->
if (opcode in Opcodes.ICONST_M1..Opcodes.ICONST_5)
opcode - Opcodes.ICONST_0
else
null
is IntInsnNode ->
when (opcode) {
Opcodes.BIPUSH -> operand
Opcodes.SIPUSH -> operand
else -> null
}
is LdcInsnNode -> cst as? Int
else -> null
}
}
@@ -0,0 +1,41 @@
// TARGET_BACKEND: JVM
// FILE: 1.kt
package kotlin.internal
fun apiVersionIsAtLeast(epic: Int, major: Int, minor: Int): Boolean {
return false
}
var properFunctionWasClled = false
fun doSomethingNew() {
properFunctionWasClled = true
}
fun doSomethingOld() {
throw AssertionError("Should not be called")
}
inline fun versionDependentInlineFun() {
if (apiVersionIsAtLeast(1, 1, 0)) {
doSomethingNew()
}
else {
doSomethingOld()
}
}
fun test() {
versionDependentInlineFun()
}
// FILE: 2.kt
import kotlin.internal.*
fun box(): String {
test()
if (!properFunctionWasClled) return "Fail 1"
return "OK"
}
@@ -642,6 +642,21 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
}
}
@TestMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class BytecodePreprocessing extends AbstractIrBlackBoxInlineCodegenTest {
public void testAllFilesPresentInBytecodePreprocessing() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/bytecodePreprocessing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("apiVersionAtLeast1.kt")
public void testApiVersionAtLeast1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing/apiVersionAtLeast1.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -642,6 +642,21 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
}
}
@TestMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class BytecodePreprocessing extends AbstractIrCompileKotlinAgainstInlineKotlinTest {
public void testAllFilesPresentInBytecodePreprocessing() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/bytecodePreprocessing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("apiVersionAtLeast1.kt")
public void testApiVersionAtLeast1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing/apiVersionAtLeast1.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -642,6 +642,21 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
}
}
@TestMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class BytecodePreprocessing extends AbstractBlackBoxInlineCodegenTest {
public void testAllFilesPresentInBytecodePreprocessing() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/bytecodePreprocessing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("apiVersionAtLeast1.kt")
public void testApiVersionAtLeast1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing/apiVersionAtLeast1.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -642,6 +642,21 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
}
}
@TestMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class BytecodePreprocessing extends AbstractCompileKotlinAgainstInlineKotlinTest {
public void testAllFilesPresentInBytecodePreprocessing() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/bytecodePreprocessing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("apiVersionAtLeast1.kt")
public void testApiVersionAtLeast1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing/apiVersionAtLeast1.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)