[FIR2IR] Cache functions/properties both by symbol & signature

This commit is contained in:
Mikhail Glukhikh
2020-11-11 16:28:08 +03:00
parent 92840b8ec5
commit 6c1f5a4513
10 changed files with 141 additions and 58 deletions
@@ -5,9 +5,7 @@
package org.jetbrains.kotlin.fir.backend
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.parentClassOrNull
@@ -99,10 +97,9 @@ class Fir2IrConversionScope {
fun returnTarget(expression: FirReturnExpression, declarationStorage: Fir2IrDeclarationStorage): IrFunction {
val firTarget = expression.target.labeledElement
val irTarget = (firTarget as? FirFunction)?.let {
if (it is FirConstructor) {
declarationStorage.getCachedIrConstructor(it)
} else {
declarationStorage.getCachedIrFunction(it)
when (it) {
is FirConstructor -> declarationStorage.getCachedIrConstructor(it)
else -> declarationStorage.getCachedIrFunction(it)
}
}
for (potentialTarget in functionStack.asReversed()) {
@@ -297,9 +297,7 @@ class Fir2IrConverter(
// Necessary call to generate built-in IR classes
externalDependenciesGenerator.generateUnboundSymbolsAsDependencies()
classifierStorage.preCacheBuiltinClasses()
val fakeOverrideGenerator = FakeOverrideGenerator(
session, scopeSession, classifierStorage, declarationStorage, conversionScope
)
val fakeOverrideGenerator = FakeOverrideGenerator(components, conversionScope)
components.fakeOverrideGenerator = fakeOverrideGenerator
val callGenerator = CallAndReferenceGenerator(components, fir2irVisitor, conversionScope)
components.callGenerator = callGenerator
@@ -364,11 +364,24 @@ class Fir2IrDeclarationStorage(
return this
}
fun getCachedIrFunction(function: FirFunction<*>): IrSimpleFunction? {
return if (function !is FirSimpleFunction || function.visibility == Visibilities.Local) {
fun getCachedIrFunction(function: FirFunction<*>): IrSimpleFunction? =
if (function is FirSimpleFunction) getCachedIrFunction(function)
else localStorage.getLocalFunction(function)
fun getCachedIrFunction(
function: FirSimpleFunction,
signatureCalculator: (FirSimpleFunction) -> IdSignature? = { null }
): IrSimpleFunction? {
return if (function.visibility == Visibilities.Local) {
localStorage.getLocalFunction(function)
} else {
functionCache[function]
functionCache[function] ?: signatureCalculator(function)?.let { signature ->
symbolTable.referenceSimpleFunctionIfAny(signature)?.let { irFunctionSymbol ->
val irFunction = irFunctionSymbol.owner
functionCache[function] = irFunction
irFunction
}
}
}
}
@@ -394,7 +407,7 @@ class Fir2IrDeclarationStorage(
}
fun getOrCreateIrFunction(
function: FirFunction<*>,
function: FirSimpleFunction,
irParent: IrDeclarationParent?,
isLocal: Boolean = false,
): IrSimpleFunction {
@@ -792,7 +805,18 @@ class Fir2IrDeclarationStorage(
}
}
fun getCachedIrProperty(property: FirProperty): IrProperty? = propertyCache[property]
fun getCachedIrProperty(
property: FirProperty,
signatureCalculator: (FirProperty) -> IdSignature? = { null }
): IrProperty? {
return propertyCache[property] ?: signatureCalculator(property)?.let { signature ->
symbolTable.referencePropertyIfAny(signature)?.let { irPropertySymbol ->
val irProperty = irPropertySymbol.owner
propertyCache[property] = irProperty
irProperty
}
}
}
internal fun cacheDelegatedProperty(property: FirProperty, irProperty: IrProperty) {
propertyCache[property] = irProperty
@@ -808,11 +832,6 @@ class Fir2IrDeclarationStorage(
return irField
}
private fun getFirClassByFqName(classId: ClassId): FirClass<*>? {
val declaration = firSymbolProvider.getClassLikeSymbolByFqName(classId)
return declaration?.fir as? FirClass<*>
}
private fun createIrField(
field: FirField,
origin: IrDeclarationOrigin = IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
@@ -1012,33 +1031,43 @@ class Fir2IrDeclarationStorage(
fun getIrFunctionSymbol(firFunctionSymbol: FirFunctionSymbol<*>): IrFunctionSymbol {
return when (val firDeclaration = firFunctionSymbol.fir) {
is FirSimpleFunction, is FirAnonymousFunction -> {
is FirAnonymousFunction -> {
getCachedIrFunction(firDeclaration)?.let { return it.symbol }
val signature = signatureComposer.composeSignature(firDeclaration)
val irParent = findIrParent(firDeclaration)
val parentOrigin = (irParent as? IrDeclaration)?.origin ?: IrDeclarationOrigin.DEFINED
val declarationOrigin = computeDeclarationOrigin(firFunctionSymbol, parentOrigin, irParent)
createIrFunction(firDeclaration, irParent, origin = declarationOrigin).apply {
setAndModifyParent(irParent)
}.symbol
}
is FirSimpleFunction -> {
val irParent by lazy { findIrParent(firDeclaration) }
getCachedIrFunction(firDeclaration) {
// Parent calculation provokes function calculation for some members from IrBuiltIns
@Suppress("UNUSED_EXPRESSION") irParent
signatureComposer.composeSignature(it)
}?.let { return it.symbol }
val parentOrigin = (irParent as? IrDeclaration)?.origin ?: IrDeclarationOrigin.DEFINED
if (signature != null) {
symbolTable.referenceSimpleFunctionIfAny(signature)?.let { irFunctionSymbol ->
val irFunction = irFunctionSymbol.owner
functionCache[firDeclaration] = irFunction
return irFunctionSymbol
// TODO: package fragment members (?)
val parent = irParent
if (parent is Fir2IrLazyClass) {
assert(parentOrigin != IrDeclarationOrigin.DEFINED) {
"Should not have reference to public API uncached simple function from source code"
}
// TODO: package fragment members (?)
if (firDeclaration is FirSimpleFunction && irParent is Fir2IrLazyClass) {
assert(parentOrigin != IrDeclarationOrigin.DEFINED) {
"Should not have reference to public API uncached simple function from source code"
}
val signature = signatureComposer.composeSignature(firDeclaration)
if (signature != null) {
val symbol = Fir2IrSimpleFunctionSymbol(signature, firDeclaration.containerSource)
val irFunction = firDeclaration.convertWithOffsets { startOffset, endOffset ->
symbolTable.declareSimpleFunction(signature, { symbol }) {
val isFakeOverride =
firFunctionSymbol is FirNamedFunctionSymbol && firFunctionSymbol.fir.isSubstitutionOverride &&
firFunctionSymbol.dispatchReceiverClassOrNull() != firFunctionSymbol.originalForSubstitutionOverride?.dispatchReceiverClassOrNull()
firFunctionSymbol.dispatchReceiverClassOrNull() !=
firFunctionSymbol.originalForSubstitutionOverride?.dispatchReceiverClassOrNull()
Fir2IrLazySimpleFunction(
components, startOffset, endOffset, parentOrigin, firDeclaration, irParent.fir, symbol, isFakeOverride
components, startOffset, endOffset, parentOrigin, firDeclaration, parent.fir, symbol, isFakeOverride
).apply {
parent = irParent
this.parent = parent
}
}
}
@@ -1078,24 +1107,24 @@ class Fir2IrDeclarationStorage(
if (fir.isLocal) {
return localStorage.getDelegatedProperty(fir)?.symbol ?: getIrVariableSymbol(fir)
}
propertyCache[fir]?.let { return it.symbol }
val signature = signatureComposer.composeSignature(fir)
val irParent = findIrParent(fir)
val irParent by lazy { findIrParent(fir) }
getCachedIrProperty(fir) {
// Parent calculation provokes property calculation for some members from IrBuiltIns
@Suppress("UNUSED_EXPRESSION") irParent
signatureComposer.composeSignature(it)
}?.let { return it.symbol }
val parentOrigin = (irParent as? IrDeclaration)?.origin ?: IrDeclarationOrigin.DEFINED
// TODO: Use computeDeclarationOrigin(firPropertySymbol, parentOrigin)
// Currently many backend tests are failing
val declarationOrigin = parentOrigin
if (signature != null) {
symbolTable.referencePropertyIfAny(signature)?.let { irPropertySymbol ->
val irProperty = irPropertySymbol.owner
propertyCache[fir] = irProperty
return irPropertySymbol
// TODO: package fragment members (?)
val parent = irParent
if (parent is Fir2IrLazyClass) {
assert(parentOrigin != IrDeclarationOrigin.DEFINED) {
"Should not have reference to public API uncached property from source code"
}
// TODO: package fragment members (?)
if (irParent is Fir2IrLazyClass) {
assert(parentOrigin != IrDeclarationOrigin.DEFINED) {
"Should not have reference to public API uncached property from source code"
}
val signature = signatureComposer.composeSignature(fir)
if (signature != null) {
val symbol = Fir2IrPropertySymbol(signature, fir.containerSource)
val irProperty = fir.convertWithOffsets { startOffset, endOffset ->
symbolTable.declareProperty(signature, { symbol }) {
@@ -1103,9 +1132,9 @@ class Fir2IrDeclarationStorage(
firPropertySymbol.fir.isSubstitutionOverride &&
firPropertySymbol.dispatchReceiverClassOrNull() != firPropertySymbol.originalForSubstitutionOverride?.dispatchReceiverClassOrNull()
Fir2IrLazyProperty(
components, startOffset, endOffset, declarationOrigin, fir, irParent.fir, symbol, isFakeOverride
components, startOffset, endOffset, declarationOrigin, fir, parent.fir, symbol, isFakeOverride
).apply {
parent = irParent
this.parent = parent
}
}
}
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenFunctions
@@ -28,15 +27,13 @@ import org.jetbrains.kotlin.ir.types.IrErrorType
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
class FakeOverrideGenerator(
private val session: FirSession,
private val scopeSession: ScopeSession,
private val classifierStorage: Fir2IrClassifierStorage,
private val declarationStorage: Fir2IrDeclarationStorage,
private val components: Fir2IrComponents,
private val conversionScope: Fir2IrConversionScope
) {
) : Fir2IrComponents by components {
private val baseFunctionSymbols = mutableMapOf<IrFunction, List<FirNamedFunctionSymbol>>()
private val basePropertySymbols = mutableMapOf<IrProperty, List<FirPropertySymbol>>()
@@ -134,7 +131,7 @@ class FakeOverrideGenerator(
irClass: IrClass,
isLocal: Boolean,
originalSymbol: FirCallableSymbol<*>,
cachedIrDeclaration: (D) -> I?,
cachedIrDeclaration: (D, (D) -> IdSignature?) -> I?,
createIrDeclaration: (D, irParent: IrClass, thisReceiverOwner: IrClass?, origin: IrDeclarationOrigin, isLocal: Boolean) -> I,
createFakeOverrideSymbol: (D, S) -> S,
baseSymbols: MutableMap<I, List<S>>,
@@ -156,7 +153,13 @@ class FakeOverrideGenerator(
if (originalSymbol.fir.origin.fromSupertypes && originalSymbol.dispatchReceiverClassOrNull() == classLookupTag) {
// Substitution case
// NB: see comment above about substituted function' parent
val irDeclaration = cachedIrDeclaration(originalDeclaration)?.takeIf { it.parent == irClass }
val irDeclaration = cachedIrDeclaration(originalDeclaration) {
// Sometimes we can have clashing here when FIR substitution/intersection override
// have the same signature.
// Now we avoid this problem by signature caching,
// so both FIR overrides correspond to one IR fake override
signatureComposer.composeSignature(originalDeclaration)
}?.takeIf { it.parent == irClass }
?: createIrDeclaration(
originalDeclaration, irClass,
declarationStorage.findIrParent(baseSymbol.fir) as? IrClass,
@@ -410,6 +410,11 @@ public class FirCompileKotlinAgainstKotlinTestGenerated extends AbstractFirCompi
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/AnonymousObjectInProperty.kt");
}
@TestMetadata("ExistingSymbolInFakeOverride.kt")
public void testExistingSymbolInFakeOverride() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/ExistingSymbolInFakeOverride.kt");
}
@TestMetadata("LibraryProperty.kt")
public void testLibraryProperty() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/LibraryProperty.kt");
@@ -0,0 +1,31 @@
// TARGET_BACKEND: JVM
// FILE: A.kt
interface KotlinMangler<D : Any> {
val String.hashMangle: Long
val D.fqnString: String
val D.fqnMangle: Long get() = fqnString.hashMangle
val manglerName: String
interface IrMangler : KotlinMangler<String> {
override val manglerName: String
get() = "Ir"
}
}
abstract class AbstractKotlinMangler<D : Any> : KotlinMangler<D> {
override val String.hashMangle get() = 42L
}
abstract class IrBasedKotlinManglerImpl : AbstractKotlinMangler<String>(), KotlinMangler.IrMangler {
override val String.fqnString: String
get() = this
}
// FILE: B.kt
abstract class AbstractJvmManglerIr : IrBasedKotlinManglerImpl()
object JvmManglerIr : AbstractJvmManglerIr()
fun box() = "OK"
@@ -415,6 +415,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/AnonymousObjectInProperty.kt");
}
@TestMetadata("ExistingSymbolInFakeOverride.kt")
public void testExistingSymbolInFakeOverride() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/ExistingSymbolInFakeOverride.kt");
}
@TestMetadata("LibraryProperty.kt")
public void testLibraryProperty() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/LibraryProperty.kt");
@@ -410,6 +410,11 @@ public class IrCompileKotlinAgainstKotlinTestGenerated extends AbstractIrCompile
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/AnonymousObjectInProperty.kt");
}
@TestMetadata("ExistingSymbolInFakeOverride.kt")
public void testExistingSymbolInFakeOverride() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/ExistingSymbolInFakeOverride.kt");
}
@TestMetadata("LibraryProperty.kt")
public void testLibraryProperty() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/LibraryProperty.kt");
@@ -410,6 +410,11 @@ public class JvmIrAgainstOldBoxTestGenerated extends AbstractJvmIrAgainstOldBoxT
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/AnonymousObjectInProperty.kt");
}
@TestMetadata("ExistingSymbolInFakeOverride.kt")
public void testExistingSymbolInFakeOverride() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/ExistingSymbolInFakeOverride.kt");
}
@TestMetadata("LibraryProperty.kt")
public void testLibraryProperty() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/LibraryProperty.kt");
@@ -410,6 +410,11 @@ public class JvmOldAgainstIrBoxTestGenerated extends AbstractJvmOldAgainstIrBoxT
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/AnonymousObjectInProperty.kt");
}
@TestMetadata("ExistingSymbolInFakeOverride.kt")
public void testExistingSymbolInFakeOverride() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/ExistingSymbolInFakeOverride.kt");
}
@TestMetadata("LibraryProperty.kt")
public void testLibraryProperty() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/fir/LibraryProperty.kt");