JVM_IR: synchronize code generation for inline functions

This commit is contained in:
Georgy Bronnikov
2020-11-23 17:06:21 +03:00
parent c7feaee8ae
commit 3a10ea4f88
11 changed files with 93 additions and 34 deletions
@@ -15612,6 +15612,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/functions/max.kt");
}
@Test
@TestMetadata("mutualInline.kt")
public void testMutualInline() throws Exception {
runTest("compiler/testData/codegen/box/functions/mutualInline.kt");
}
@Test
@TestMetadata("nothisnoclosure.kt")
public void testNothisnoclosure() throws Exception {
@@ -132,6 +132,8 @@ class JvmBackendContext(
internal val continuationClassesVarsCountByType: MutableMap<IrAttributeContainer, Map<Type, Int>> = hashMapOf()
val inlineMethodGenerationLock = Any()
init {
state.mapInlineClass = { descriptor ->
typeMapper.mapType(referenceClass(descriptor).defaultType)
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrLock
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
@@ -48,8 +49,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.io.File
import java.util.concurrent.ConcurrentHashMap
interface MetadataSerializer {
fun serialize(metadata: MetadataSource): Pair<MessageLite, JvmStringTable>?
@@ -258,33 +259,6 @@ class ClassCodegen private constructor(
return listOf(File(entry.name))
}
companion object {
fun getOrCreate(
irClass: IrClass,
context: JvmBackendContext,
// The `parentFunction` is only set for classes nested inside of functions. This is usually safe, since there is no
// way to refer to (inline) members of such a class from outside of the function unless the function in question is
// itself declared as inline. In that case, the function will be compiled before we can refer to the nested class.
//
// The one exception to this rule are anonymous objects defined as members of a class. These are nested inside of the
// class initializer, but can be referred to from anywhere within the scope of the class. That's why we have to ensure
// that all references to classes inside of <clinit> have a non-null `parentFunction`.
parentFunction: IrFunction? = irClass.parent.safeAs<IrFunction>()?.takeIf {
it.origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER
},
): ClassCodegen =
context.classCodegens.getOrPut(irClass) { ClassCodegen(irClass, context, parentFunction) }.also {
assert(parentFunction == null || it.parentFunction == parentFunction) {
"inconsistent parent function for ${irClass.render()}:\n" +
"New: ${parentFunction!!.render()}\n" +
"Old: ${it.parentFunction?.render()}"
}
}
private fun JvmClassSignature.hasInvalidName() =
name.splitToSequence('/').any { identifier -> identifier.any { it in JvmSimpleNameBacktickChecker.INVALID_CHARS } }
}
private fun generateField(field: IrField) {
val fieldType = typeMapper.mapType(field)
val fieldSignature =
@@ -324,7 +298,7 @@ class ClassCodegen private constructor(
}
}
private val generatedInlineMethods = mutableMapOf<IrFunction, SMAPAndMethodNode>()
private val generatedInlineMethods = ConcurrentHashMap<IrFunction, SMAPAndMethodNode>()
fun generateMethodNode(method: IrFunction): SMAPAndMethodNode {
if (!method.isInline && !method.isSuspendCapturingCrossinline()) {
@@ -335,11 +309,13 @@ class ClassCodegen private constructor(
// multiple times if declared in a `finally` block - should they be cached?
return FunctionCodegen(method, this).generate()
}
val (node, smap) = generatedInlineMethods.getOrPut(method) { FunctionCodegen(method, this).generate() }
val copy = with(node) { MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions.toTypedArray()) }
node.instructions.resetLabels()
node.accept(copy)
return SMAPAndMethodNode(copy, smap)
// Only allow generation of one inline method at a time, to avoid deadlocks when files call inline methods of each other.
val (node, smap) =
generatedInlineMethods[method] ?: synchronized(context.inlineMethodGenerationLock) {
generatedInlineMethods.getOrPut(method) { FunctionCodegen(method, this).generate() }
}
return SMAPAndMethodNode(cloneMethodNode(node), smap)
}
private fun generateMethod(method: IrFunction, classSMAP: SourceMapper, delegatedPropertyOptimizer: DelegatedPropertyOptimizer?) {
@@ -483,6 +459,33 @@ class ClassCodegen private constructor(
else
OtherOrigin(psiElement, descriptor)
}
companion object {
fun getOrCreate(
irClass: IrClass,
context: JvmBackendContext,
// The `parentFunction` is only set for classes nested inside of functions. This is usually safe, since there is no
// way to refer to (inline) members of such a class from outside of the function unless the function in question is
// itself declared as inline. In that case, the function will be compiled before we can refer to the nested class.
//
// The one exception to this rule are anonymous objects defined as members of a class. These are nested inside of the
// class initializer, but can be referred to from anywhere within the scope of the class. That's why we have to ensure
// that all references to classes inside of <clinit> have a non-null `parentFunction`.
parentFunction: IrFunction? = irClass.parent.safeAs<IrFunction>()?.takeIf {
it.origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER
},
): ClassCodegen =
context.classCodegens.getOrPut(irClass) { ClassCodegen(irClass, context, parentFunction) }.also {
assert(parentFunction == null || it.parentFunction == parentFunction) {
"inconsistent parent function for ${irClass.render()}:\n" +
"New: ${parentFunction!!.render()}\n" +
"Old: ${it.parentFunction?.render()}"
}
}
private fun JvmClassSignature.hasInvalidName() =
name.splitToSequence('/').any { identifier -> identifier.any { it in JvmSimpleNameBacktickChecker.INVALID_CHARS } }
}
}
private fun IrClass.getFlags(languageVersionSettings: LanguageVersionSettings): Int =
+11
View File
@@ -0,0 +1,11 @@
// FILE: A.kt
inline fun a(): String = b2()
// FILE: B.kt
inline fun b1(): String = a()
inline fun b2(): String = "OK"
fun box() = b1()
@@ -15612,6 +15612,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/functions/max.kt");
}
@Test
@TestMetadata("mutualInline.kt")
public void testMutualInline() throws Exception {
runTest("compiler/testData/codegen/box/functions/mutualInline.kt");
}
@Test
@TestMetadata("nothisnoclosure.kt")
public void testNothisnoclosure() throws Exception {
@@ -15612,6 +15612,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/functions/max.kt");
}
@Test
@TestMetadata("mutualInline.kt")
public void testMutualInline() throws Exception {
runTest("compiler/testData/codegen/box/functions/mutualInline.kt");
}
@Test
@TestMetadata("nothisnoclosure.kt")
public void testNothisnoclosure() throws Exception {
@@ -12916,6 +12916,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/functions/max.kt");
}
@TestMetadata("mutualInline.kt")
public void testMutualInline() throws Exception {
runTest("compiler/testData/codegen/box/functions/mutualInline.kt");
}
@TestMetadata("nothisnoclosure.kt")
public void testNothisnoclosure() throws Exception {
runTest("compiler/testData/codegen/box/functions/nothisnoclosure.kt");
@@ -11394,6 +11394,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/functions/localReturnInsideFunctionExpression.kt");
}
@TestMetadata("mutualInline.kt")
public void testMutualInline() throws Exception {
runTest("compiler/testData/codegen/box/functions/mutualInline.kt");
}
@TestMetadata("nothisnoclosure.kt")
public void testNothisnoclosure() throws Exception {
runTest("compiler/testData/codegen/box/functions/nothisnoclosure.kt");
@@ -10851,6 +10851,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/functions/localReturnInsideFunctionExpression.kt");
}
@TestMetadata("mutualInline.kt")
public void testMutualInline() throws Exception {
runTest("compiler/testData/codegen/box/functions/mutualInline.kt");
}
@TestMetadata("nothisnoclosure.kt")
public void testNothisnoclosure() throws Exception {
runTest("compiler/testData/codegen/box/functions/nothisnoclosure.kt");
@@ -10851,6 +10851,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/functions/localReturnInsideFunctionExpression.kt");
}
@TestMetadata("mutualInline.kt")
public void testMutualInline() throws Exception {
runTest("compiler/testData/codegen/box/functions/mutualInline.kt");
}
@TestMetadata("nothisnoclosure.kt")
public void testNothisnoclosure() throws Exception {
runTest("compiler/testData/codegen/box/functions/nothisnoclosure.kt");
@@ -5512,6 +5512,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/functions/localReturnInsideFunctionExpression.kt");
}
@TestMetadata("mutualInline.kt")
public void testMutualInline() throws Exception {
runTest("compiler/testData/codegen/box/functions/mutualInline.kt");
}
@TestMetadata("nothisnoclosure.kt")
public void testNothisnoclosure() throws Exception {
runTest("compiler/testData/codegen/box/functions/nothisnoclosure.kt");