API version calls preprocessing, 1st passing test
This commit is contained in:
@@ -263,7 +263,8 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
|||||||
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
||||||
"Method inlining " + sourceCompiler.callElementText,
|
"Method inlining " + sourceCompiler.callElementText,
|
||||||
createNestedSourceMapper(nodeAndSmap, defaultSourceMapper), info.callSiteInfo,
|
createNestedSourceMapper(nodeAndSmap, defaultSourceMapper), info.callSiteInfo,
|
||||||
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null
|
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
||||||
|
!isInlinedToInlineFunInKotlinRuntime()
|
||||||
) //with captured
|
) //with captured
|
||||||
|
|
||||||
val remapper = LocalVarRemapper(parameters, initialFrameSize)
|
val remapper = LocalVarRemapper(parameters, initialFrameSize)
|
||||||
@@ -297,6 +298,14 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
|||||||
return result
|
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() {
|
private fun generateClosuresBodies() {
|
||||||
for (info in expressionMap.values) {
|
for (info in expressionMap.values) {
|
||||||
info.generateLambdaBody(sourceCompiler, reifiedTypeInliner)
|
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.ClosureCodegen
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
import org.jetbrains.kotlin.codegen.StackValue
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
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.FixStackWithLabelNormalizationMethodTransformer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||||
@@ -46,7 +47,8 @@ class MethodInliner(
|
|||||||
private val errorPrefix: String,
|
private val errorPrefix: String,
|
||||||
private val sourceMapper: SourceMapper,
|
private val sourceMapper: SourceMapper,
|
||||||
private val inlineCallSiteInfo: InlineCallSiteInfo,
|
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 typeMapper = inliningContext.state.typeMapper
|
||||||
private val invokeCalls = ArrayList<InvokeCall>()
|
private val invokeCalls = ArrayList<InvokeCall>()
|
||||||
@@ -394,7 +396,7 @@ class MethodInliner(
|
|||||||
): MethodNode {
|
): MethodNode {
|
||||||
val processingNode = prepareNode(node, finallyDeepShift)
|
val processingNode = prepareNode(node, finallyDeepShift)
|
||||||
|
|
||||||
normalizeNodeBeforeInline(processingNode, labelOwner)
|
preprocessNodeBeforeInline(processingNode, labelOwner)
|
||||||
|
|
||||||
val sources = analyzeMethodNodeBeforeInline(processingNode)
|
val sources = analyzeMethodNodeBeforeInline(processingNode)
|
||||||
|
|
||||||
@@ -508,7 +510,7 @@ class MethodInliner(
|
|||||||
return processingNode
|
return processingNode
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun normalizeNodeBeforeInline(node: MethodNode, labelOwner: LabelOwner) {
|
private fun preprocessNodeBeforeInline(node: MethodNode, labelOwner: LabelOwner) {
|
||||||
try {
|
try {
|
||||||
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
|
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
|
||||||
}
|
}
|
||||||
@@ -516,6 +518,11 @@ class MethodInliner(
|
|||||||
throw wrapException(e, node, "couldn't inline method call")
|
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 frames = analyzeMethodNodeBeforeInline(node)
|
||||||
|
|
||||||
val localReturnsNormalizer = LocalReturnsNormalizer()
|
val localReturnsNormalizer = LocalReturnsNormalizer()
|
||||||
|
|||||||
+87
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -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"
|
||||||
|
}
|
||||||
+15
@@ -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")
|
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+15
@@ -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")
|
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@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")
|
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+15
@@ -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")
|
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user