[JS IR] Allow constant inlining in JS IR

Fixed the incremental compilation issue,
which had prevented enabling const inlining during constant
evaluation and folding a constant expression with JS code.

^KT-62425
This commit is contained in:
Alexander Korepanov
2023-10-12 12:16:08 +02:00
committed by Space Team
parent 629e0628d6
commit 188cdf2f98
7 changed files with 38 additions and 48 deletions
@@ -849,13 +849,9 @@ private val jsSuspendArityStorePhase = makeDeclarationTransformerPhase(
val constEvaluationPhase = makeJsModulePhase(
{ context ->
// We can't inline `const val`s because this lowering can mess up incremental compilation.
// For example, if we inline some constant located in `lib` module then we are not going to track and update its value on change.
// The only usages of `const val`s that we allow to inline are the ones that are located at the same file as declaration.
val configuration = IrInterpreterConfiguration(
printOnlyExceptionMessage = true,
platform = JsPlatforms.defaultJsPlatform,
inlineConstVal = false
)
ConstEvaluationLowering(context, configuration = configuration)
},
@@ -73,6 +73,12 @@ private class HashCalculatorForIC {
updateForEach(annotationContainer.annotations, ::update)
}
fun updateProperty(irProperty: IrProperty) {
if (irProperty.isConst) {
irProperty.backingField?.initializer?.let(::update)
}
}
fun updateSymbol(symbol: IrSymbol) {
update(symbol.toString())
@@ -107,12 +113,11 @@ private class HashCalculatorForIC {
update(functionParam.defaultValue?.let { 1 } ?: 0)
}
}
(symbol.owner as? IrAnnotationContainer)?.let(::updateAnnotationContainer)
(symbol.owner as? IrProperty)?.let { irProperty ->
if (irProperty.isConst) {
irProperty.backingField?.initializer?.let(::update)
}
(symbol.owner as? IrSimpleFunction)?.let { irSimpleFunction ->
irSimpleFunction.correspondingPropertySymbol?.owner?.let(::updateProperty)
}
(symbol.owner as? IrAnnotationContainer)?.let(::updateAnnotationContainer)
(symbol.owner as? IrProperty)?.let(::updateProperty)
}
inline fun <T> updateForEach(collection: Collection<T>, f: (T) -> Unit) {
@@ -6,10 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.ic
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.symbols.IrSymbol
@@ -26,15 +23,26 @@ internal class IdSignatureHashCalculator(private val icHasher: ICHasher) {
private val idSignatureHashes = hashMapOf<IdSignature, ICHash>()
private val fileAnnotationHashes = hashMapOf<IrFile, ICHash>()
private val constantHashes = hashMapOf<IrProperty, ICHash>()
private val inlineFunctionFlatHashes = hashMapOf<IrFunction, ICHash>()
private val inlineFunctionDepends = hashMapOf<IrFunction, LinkedHashSet<IrFunction>>()
private data class InlineFunctionDependencies(
val usedInlineFunctions: LinkedHashSet<IrFunction>,
val usedConstants: LinkedHashSet<IrProperty>,
)
private val inlineFunctionDepends = hashMapOf<IrFunction, InlineFunctionDependencies>()
private val IrFile.annotationsHash: ICHash
get() = fileAnnotationHashes.getOrPut(this) {
icHasher.calculateIrAnnotationContainerHash(this)
}
private val IrProperty.constantHash: ICHash
get() = constantHashes.getOrPut(this) {
icHasher.calculateIrSymbolHash(symbol)
}
private val IrFunction.inlineFunctionFlatHash: ICHash
get() = inlineFunctionFlatHashes.getOrPut(this) {
val function = if (isFakeOverride && this is IrSimpleFunction) resolveFakeOverrideOrFail() else this
@@ -42,9 +50,10 @@ internal class IdSignatureHashCalculator(private val icHasher: ICHasher) {
ICHash(symbol.calculateSymbolHash().hash.combineWith(flatHash.hash))
}
private val IrFunction.inlineDepends: Collection<IrFunction>
private val IrFunction.inlineDepends: InlineFunctionDependencies
get() = inlineFunctionDepends.getOrPut(this) {
val usedInlineFunctions = linkedSetOf<IrFunction>()
val usedConstants = linkedSetOf<IrProperty>()
acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
@@ -56,6 +65,10 @@ internal class IdSignatureHashCalculator(private val icHasher: ICHasher) {
if (callee.isInline) {
usedInlineFunctions += callee
}
val correspondingProperty = callee.correspondingPropertySymbol?.owner
if (correspondingProperty?.isConst == true) {
usedConstants += correspondingProperty
}
expression.acceptChildrenVoid(this)
}
@@ -71,7 +84,7 @@ internal class IdSignatureHashCalculator(private val icHasher: ICHasher) {
}
})
usedInlineFunctions
InlineFunctionDependencies(usedInlineFunctions, usedConstants)
}
private fun IrSymbol.calculateSymbolHash(): ICHash {
@@ -94,12 +107,16 @@ internal class IdSignatureHashCalculator(private val icHasher: ICHasher) {
val newDependsStack = transitiveDepends.toMutableList()
while (newDependsStack.isNotEmpty()) {
newDependsStack.removeLast().inlineDepends.forEach { inlineFunction ->
val (usedInlineFunctions, usedConstants) = newDependsStack.removeLast().inlineDepends
for (inlineFunction in usedInlineFunctions) {
if (transitiveDepends.add(inlineFunction)) {
newDependsStack += inlineFunction
transitiveHash = ICHash(transitiveHash.hash.combineWith(inlineFunction.inlineFunctionFlatHash.hash))
}
}
for (constant in usedConstants) {
transitiveHash = ICHash(transitiveHash.hash.combineWith(constant.constantHash.hash))
}
}
return transitiveHash
@@ -1,12 +0,0 @@
STEP 0:
dependencies: lib1, lib2
added file: m.kt
STEP 1:
dependencies: lib1, lib2
updated imports: m.kt
STEP 2:
dependencies: lib1, lib2
modified ir: m.kt
STEP 3:
dependencies: lib1, lib2
updated imports: m.kt
@@ -3,8 +3,10 @@ STEP 0:
added file: m.kt
STEP 1:
dependencies: lib1, lib2
updated imports: m.kt
STEP 2:
dependencies: lib1, lib2
modified ir: m.kt
STEP 3:
dependencies: lib1, lib2
updated imports: m.kt
@@ -1,10 +0,0 @@
MODULES: lib1, lib2, main
STEP 0:
libs: lib1, lib2, main
dirty js modules: lib1, lib2, main
dirty js files: lib1/l1, lib2/l2, main/m, main/m.export, main
STEP 1..3:
libs: lib1, lib2, main
dirty js modules: lib1, lib2, main
dirty js files: lib1/l1, lib2/l2, main/m
@@ -4,15 +4,7 @@ STEP 0:
libs: lib1, lib2, main
dirty js modules: lib1, lib2, main
dirty js files: lib1/l1, lib2/l2, main/m, main/m.export, main
STEP 1:
libs: lib1, lib2, main
dirty js modules: lib1, lib2
dirty js files: lib1/l1, lib2/l2
STEP 2:
STEP 1..3:
libs: lib1, lib2, main
dirty js modules: lib1, lib2, main
dirty js files: lib1/l1, lib2/l2, main/m
STEP 3:
libs: lib1, lib2, main
dirty js modules: lib1, lib2
dirty js files: lib1/l1, lib2/l2