JVM_IR: generate instance fields even when reification is needed

The inliner supports that, we just need to generate the reification
markers on GETFIELD to instance fields.
This commit is contained in:
pyos
2021-07-05 13:44:22 +02:00
committed by max-kammerer
parent c01c356817
commit a15a5aa429
17 changed files with 146 additions and 96 deletions
@@ -3914,6 +3914,12 @@ public class FirBlackBoxInlineCodegenTestGenerated extends AbstractFirBlackBoxIn
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
}
@Test
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@Nested
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.isSyntheticSingleton
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField
import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
@@ -312,7 +313,7 @@ class ClassCodegen private constructor(
if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) {
val skipNullabilityAnnotations =
flags and (Opcodes.ACC_SYNTHETIC or Opcodes.ACC_ENUM) != 0 ||
field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE
(field.origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE && irClass.isSyntheticSingleton)
object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) {
override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
return fv.visitAnnotation(descr, visible)
@@ -596,16 +596,10 @@ class ExpressionCodegen(
val owner = typeMapper.mapClass(callee.constructedClass)
val signature = methodSignatureMapper.mapSignatureSkipGeneric(callee)
closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let {
if (it.wereUsedReifiedParameters()) {
putNeedClassReificationMarker(mv)
propagateChildReifiedTypeParametersUsages(it)
}
}
// IR constructors have no receiver and return the new instance, but on JVM they are void-returning
// instance methods named <init>.
markLineNumber(expression)
putNeedClassReificationMarker(callee.constructedClass)
mv.anew(owner)
mv.dup()
@@ -735,6 +729,9 @@ class ExpressionCodegen(
assert(expression.type.isUnit())
unitValue
} else {
if (expression.symbol.owner.origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE) {
putNeedClassReificationMarker(expression.symbol.owner.parentAsClass)
}
mv.visitFieldInsn(if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD, ownerName, fieldName, fieldType.descriptor)
MaterialValue(this, fieldType, callee.type)
}
@@ -914,6 +911,14 @@ class ExpressionCodegen(
return unitValue
}
private fun putNeedClassReificationMarker(declaration: IrClass) {
val reifiedTypeParameters = closureReifiedMarkers[declaration] ?: return
if (reifiedTypeParameters.wereUsedReifiedParameters()) {
putNeedClassReificationMarker(mv)
propagateChildReifiedTypeParametersUsages(reifiedTypeParameters)
}
}
private fun generateGlobalReturnFlagIfPossible(expression: IrExpression, label: String) {
if (state.isInlineDisabled) {
context.psiErrorBuilder.at(expression, irFunction).report(Errors.NON_LOCAL_RETURN_IN_DISABLED_INLINE)
@@ -413,3 +413,9 @@ inline fun IrElement.hasChild(crossinline block: (IrElement) -> Boolean): Boolea
}, null)
return result
}
val IrClass.isSyntheticSingleton: Boolean
get() = (origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL
|| origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|| origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE)
&& primaryConstructor!!.valueParameters.isEmpty()
@@ -9,31 +9,18 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.backend.jvm.ir.isSyntheticSingleton
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
internal val staticCallableReferencePhase = makeIrFilePhase(
::StaticCallableReferenceLowering,
@@ -42,15 +29,13 @@ internal val staticCallableReferencePhase = makeIrFilePhase(
)
class StaticCallableReferenceLowering(val backendContext: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
private val staticInstanceFields = HashMap<IrClass, IrField>()
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
override fun visitClass(declaration: IrClass): IrStatement {
declaration.transformChildrenVoid()
if (declaration.isSyntheticSingleton) {
declaration.declarations += getFieldForStaticCallableReferenceInstance(declaration).also { field ->
field.initializer = backendContext.createIrBuilder(field.symbol).run {
declaration.declarations += backendContext.cachedDeclarations.getFieldForObjectInstance(declaration).apply {
initializer = backendContext.createIrBuilder(symbol).run {
irExprBody(irCall(declaration.primaryConstructor!!))
}
}
@@ -58,77 +43,12 @@ class StaticCallableReferenceLowering(val backendContext: JvmBackendContext) : F
return declaration
}
private fun getFieldForStaticCallableReferenceInstance(irClass: IrClass): IrField =
staticInstanceFields.getOrPut(irClass) {
backendContext.irFactory.buildField {
name = Name.identifier(JvmAbi.INSTANCE_FIELD)
type = irClass.defaultType
origin = JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE
isFinal = true
isStatic = true
visibility = DescriptorVisibilities.PUBLIC
}.apply {
parent = irClass
}
}
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
val constructor = expression.symbol.owner
if (!constructor.constructedClass.isSyntheticSingleton)
val constructedClass = expression.symbol.owner.constructedClass
if (!constructedClass.isSyntheticSingleton)
return super.visitConstructorCall(expression)
val instanceField = getFieldForStaticCallableReferenceInstance(constructor.constructedClass)
val instanceField = backendContext.cachedDeclarations.getFieldForObjectInstance(constructedClass)
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
}
// Recognize callable references with no value or type arguments. The only type arguments in Kotlin stem from usages of
// reified type parameters, which we unfortunately don't record as parameters so we have to check the body of the class.
private val IrClass.isSyntheticSingleton: Boolean
get() = (origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL
|| origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|| origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE)
&& primaryConstructor!!.valueParameters.isEmpty()
&& !containsReifiedTypeParameters
// Check whether there is any usage of reified type parameters in the body of the given class. This method does not
// distinguish between reified type parameters declared in inline functions inside the class and those coming from the
// outside. This is sufficient, because we only apply this function to callable references where this does not matter.
private val IrClass.containsReifiedTypeParameters: Boolean
get() = containsReifiedTypeParametersCache.getOrPut(this) {
var containsReified = false
acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
if (!containsReified)
element.acceptChildrenVoid(this)
}
override fun visitMemberAccess(expression: IrMemberAccessExpression<*>) {
for (i in 0 until expression.typeArgumentsCount) {
if (expression.getTypeArgument(i)?.isReified == true) {
containsReified = true
break
}
}
super.visitMemberAccess(expression)
}
override fun visitTypeOperator(expression: IrTypeOperatorCall) {
if (expression.typeOperand.isReified)
containsReified = true
super.visitTypeOperator(expression)
}
override fun visitClassReference(expression: IrClassReference) {
if (expression.classType.isReified)
containsReified = true
super.visitClassReference(expression)
}
private val IrType.isReified: Boolean
get() = classifierOrNull?.safeAs<IrTypeParameterSymbol>()?.owner?.isReified == true
})
return containsReified
}
private val containsReifiedTypeParametersCache = mutableMapOf<IrClass, Boolean>()
}
@@ -0,0 +1,18 @@
// CHECK_BYTECODE_LISTING
// FILE: 1.kt
package test
inline fun <reified U> bar() = U::class.simpleName!!
inline fun <reified T> foo(): String {
val x = { bar<Array<T>>() }
return x()
}
// FILE: 2.kt
import test.*
fun box(): String {
val result = foo<Int>()
return if (result == "Array") "OK" else result
}
@@ -0,0 +1,37 @@
@kotlin.Metadata
public final class _2Kt$box$$inlined$foo$1 {
// source: '1.kt'
enclosing method _2Kt.box()Ljava/lang/String;
public final static field INSTANCE: _2Kt$box$$inlined$foo$1
inner (anonymous) class _2Kt$box$$inlined$foo$1
static method <clinit>(): void
public method <init>(): void
public synthetic bridge method invoke(): java.lang.Object
public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String
}
@kotlin.Metadata
public final class _2Kt {
// source: '2.kt'
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
}
@kotlin.Metadata
public final class test/_1Kt$foo$x$1 {
// source: '1.kt'
enclosing method test/_1Kt.foo()Ljava/lang/String;
public final static field INSTANCE: test._1Kt$foo$x$1
inner (anonymous) class test/_1Kt$foo$x$1
static method <clinit>(): void
public method <init>(): void
public synthetic bridge method invoke(): java.lang.Object
public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String
}
@kotlin.Metadata
public final class test/_1Kt {
// source: '1.kt'
inner (anonymous) class test/_1Kt$foo$x$1
public synthetic final static method bar(): java.lang.String
public synthetic final static method foo(): java.lang.String
}
@@ -3902,6 +3902,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
}
@Test
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@Nested
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@@ -3902,6 +3902,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
}
@Test
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@Nested
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@@ -3914,6 +3914,12 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
}
@Test
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@Nested
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@@ -3914,6 +3914,12 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
}
@Test
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@Nested
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@@ -3914,6 +3914,12 @@ public class IrSerializeCompileKotlinAgainstInlineKotlinTestGenerated extends Ab
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
}
@Test
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@Nested
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@@ -3914,6 +3914,12 @@ public class JvmIrAgainstOldBoxInlineTestGenerated extends AbstractJvmIrAgainstO
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
}
@Test
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@Nested
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@@ -3902,6 +3902,12 @@ public class JvmOldAgainstIrBoxInlineTestGenerated extends AbstractJvmOldAgainst
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
}
@Test
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@Nested
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@@ -3156,6 +3156,11 @@ public class IrJsCodegenInlineES6TestGenerated extends AbstractIrJsCodegenInline
runTest("compiler/testData/codegen/boxInline/reified/nonCapturingObjectInLambda.kt");
}
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -3156,6 +3156,11 @@ public class IrJsCodegenInlineTestGenerated extends AbstractIrJsCodegenInlineTes
runTest("compiler/testData/codegen/boxInline/reified/nonCapturingObjectInLambda.kt");
}
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -3156,6 +3156,11 @@ public class JsCodegenInlineTestGenerated extends AbstractJsCodegenInlineTest {
runTest("compiler/testData/codegen/boxInline/reified/nonCapturingObjectInLambda.kt");
}
@TestMetadata("singletonLambda.kt")
public void testSingletonLambda() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
}
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)