[FIR2IR] Don't run interpreter's preprocessors on fir2ir stage

In IR interpreter we have "preprocessors". Preprocessor is a
transformer that changes IR expressions in a way that we can
interpret them at least partially.

We have two places where interpretation is happening:
1. Right after fir2ir where we evaluate only strictly necessary
expression for `const val` and annotations.
2. In lowering for every backend where we are doing some
constant folding.

Earlier, to avoid double work, we didn't launch preprocessors
in backend if we were using K2. But this approach breaks compilation
for MPP projects where one module is compiled with K1 into klib
and the overall project is compiled with K2. On the backend side,
we are mistakenly assuming that preprocessors were launched, but they
were not.

The solution is to run preprocessors only on the backend side. If we
think about it, interpretation on fir2ir doesn't require any
preprocessing because we are working only with expressions that are
correct and must be fully evaluated.

#KT-62126 Fixed
This commit is contained in:
Ivan Kylchik
2023-10-12 18:32:08 +02:00
committed by Space Team
parent 7ff2fc9dbe
commit 70f195598e
6 changed files with 36 additions and 17 deletions
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
import org.jetbrains.kotlin.ir.interpreter.transformer.preprocessForConstTransformer
import org.jetbrains.kotlin.ir.interpreter.transformer.runConstOptimizations
class ConstEvaluationLowering(
@@ -27,11 +26,7 @@ class ConstEvaluationLowering(
private val mode = EvaluationMode.ONLY_INTRINSIC_CONST
override fun lower(irFile: IrFile) {
val useFir = context.configuration[CommonConfigurationKeys.USE_FIR] == true
val preprocessedFile = if (useFir) irFile else irFile.preprocessForConstTransformer(interpreter, mode)
preprocessedFile.runConstOptimizations(
interpreter, mode, evaluatedConstTracker, inlineConstTracker, suppressErrors
)
irFile.runConstOptimizations(interpreter, mode, evaluatedConstTracker, inlineConstTracker, suppressErrors)
}
}
@@ -40,23 +40,21 @@ fun IrFile.transformConst(
onError: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> },
suppressExceptions: Boolean = false,
) {
val preprocessedFile = this.preprocessForConstTransformer(interpreter, mode)
val checker = IrInterpreterCommonChecker()
val irConstExpressionTransformer = IrConstOnlyNecessaryTransformer(
interpreter, preprocessedFile, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
interpreter, this, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
)
preprocessedFile.transform(irConstExpressionTransformer, IrConstTransformer.Data())
this.transform(irConstExpressionTransformer, IrConstTransformer.Data())
val irConstDeclarationAnnotationTransformer = IrConstDeclarationAnnotationTransformer(
interpreter, preprocessedFile, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
interpreter, this, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
)
preprocessedFile.transform(irConstDeclarationAnnotationTransformer, IrConstTransformer.Data())
this.transform(irConstDeclarationAnnotationTransformer, IrConstTransformer.Data())
val irConstTypeAnnotationTransformer = IrConstTypeAnnotationTransformer(
interpreter, preprocessedFile, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
interpreter, this, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
)
preprocessedFile.transform(irConstTypeAnnotationTransformer, IrConstTransformer.Data())
this.transform(irConstTypeAnnotationTransformer, IrConstTransformer.Data())
}
fun IrFile.runConstOptimizations(
@@ -66,14 +64,18 @@ fun IrFile.runConstOptimizations(
inlineConstTracker: InlineConstTracker? = null,
suppressExceptions: Boolean = false,
) {
val preprocessedFile = this.preprocessForConstTransformer(interpreter, mode)
val checker = IrInterpreterCommonChecker()
val irConstExpressionTransformer = IrConstAllTransformer(
interpreter, this, mode, checker, evaluatedConstTracker, inlineConstTracker, { _, _, _ -> }, { _, _, _ -> }, suppressExceptions
interpreter, preprocessedFile, mode, checker, evaluatedConstTracker, inlineConstTracker,
{ _, _, _ -> }, { _, _, _ -> },
suppressExceptions
)
this.transform(irConstExpressionTransformer, IrConstTransformer.Data())
preprocessedFile.transform(irConstExpressionTransformer, IrConstTransformer.Data())
}
fun IrFile.preprocessForConstTransformer(
private fun IrFile.preprocessForConstTransformer(
interpreter: IrInterpreter,
mode: EvaluationMode,
): IrFile {
@@ -0,0 +1,10 @@
package lib
public class Plot {
public val layout: Int = 0
}
public fun plotFun(plot: Plot): String {
val mustBeOptimizedByCompiler = plot::layout.name
return mustBeOptimizedByCompiler
}
@@ -0,0 +1,4 @@
info: produce executable: $TMP_DIR$/usage.js
info: cache directory: null
info: executable production duration: [time]
OK
@@ -712,6 +712,14 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo
output.lines().filterNot { "argument -Xexpect-actual-linker is deprecated" in it }.joinToString("\n")
}
fun testConstEvaluationWithDifferentLV() {
val library = compileJsLibrary("library", additionalOptions = listOf("-language-version", "1.9"))
compileKotlin(
"src", File(tmpdir, "usage.js"), emptyList(), K2JSCompiler(),
additionalOptions = listOf("-Xinclude=${library.absolutePath}", "-Xir-produce-js"),
)
}
private fun doTestAnonymousObjectTypeMetadata(
extraCommandLineArguments: List<String> = emptyList(),
filterOutput: (String) -> String = { output -> output }