[IR] Support const optimizations for JS backend

This commit is contained in:
Ivan Kylchik
2023-05-29 00:19:47 +02:00
committed by Space Team
parent abbafc0b2a
commit d3d28783c5
22 changed files with 292 additions and 32 deletions
@@ -26,6 +26,8 @@ import org.jetbrains.kotlin.ir.backend.js.lower.inline.*
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
import org.jetbrains.kotlin.platform.js.JsPlatforms
private fun DeclarationContainerLoweringPass.runOnFilesPostfix(files: Iterable<IrFile>) = files.forEach { runOnFilePostfix(it) }
@@ -470,19 +472,6 @@ private val booleanPropertyInExternalLowering = makeBodyLoweringPhase(
description = "Lowering which wrap boolean in external declarations with Boolean() call and add diagnostic for such cases"
)
private val foldConstantLoweringPhase = makeBodyLoweringPhase(
{ FoldConstantLowering(it, true) },
name = "FoldConstantLowering",
description = "[Optimization] Constant Folding",
prerequisite = setOf(propertyAccessorInlinerLoweringPhase)
)
private val computeStringTrimPhase = makeJsModulePhase(
::StringTrimLowering,
name = "StringTrimLowering",
description = "Compute trimIndent and trimMargin operations on constant strings"
).toModuleLowering()
private val localDelegatedPropertiesLoweringPhase = makeBodyLoweringPhase(
{ LocalDelegatedPropertiesLowering() },
name = "LocalDelegatedPropertiesLowering",
@@ -846,6 +835,17 @@ private val jsSuspendArityStorePhase = makeDeclarationTransformerPhase(
description = "Store arity for suspend functions to not remove it during DCE"
)
val constEvaluationPhase = makeJsModulePhase(
{
ConstEvaluationLowering(
it,
configuration = IrInterpreterConfiguration(printOnlyExceptionMessage = true, platform = JsPlatforms.defaultJsPlatform)
)
},
name = "ConstEvaluationLowering",
description = "Evaluate functions that are marked as `IntrinsicConstEvaluation`",
).toModuleLowering()
val loweringList = listOf<Lowering>(
scriptRemoveReceiverLowering,
validateIrBeforeLowering,
@@ -868,6 +868,7 @@ val loweringList = listOf<Lowering>(
wrapInlineDeclarationsWithReifiedTypeParametersLowering,
saveInlineFunctionsBeforeInlining,
functionInliningPhase,
constEvaluationPhase,
copyInlineFunctionBodyLoweringPhase,
removeInlineDeclarationsWithReifiedTypeParametersLoweringPhase,
createScriptFunctionsPhase,
@@ -917,8 +918,6 @@ val loweringList = listOf<Lowering>(
propertyAccessorInlinerLoweringPhase,
copyPropertyAccessorBodiesLoweringPass,
booleanPropertyInExternalLowering,
foldConstantLoweringPhase,
computeStringTrimPhase,
privateMembersLoweringPhase,
privateMemberUsagesLoweringPhase,
defaultArgumentStubGeneratorPhase,
@@ -172,10 +172,16 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
if (environment.configuration.platform.isJs()) {
if (signature.name == "toString") return signature.args[0].value.specialToStringForJs()
if (signature.name == "toFloat") signature.name = "toDouble"
signature.args.filter { it.type == "kotlin.Float" }.forEach {
it.type = "kotlin.Double"
it.value = it.value.toString().toDouble()
}
signature.args
.filter { it.value is Float || it.type == "kotlin.Float" || it.type == "kotlin.Float?" }
.forEach {
it.type = when (it.type) {
"kotlin.Float" -> "kotlin.Double"
"kotlin.Float?" -> "kotlin.Double?"
else -> it.type
}
it.value = it.value.toString().toDouble()
}
}
val name = signature.name
@@ -234,6 +234,15 @@ private fun unfoldBody(body: IrBody, callStack: CallStack) {
}
private fun unfoldBlock(block: IrBlock, callStack: CallStack) {
if (block is IrReturnableBlock) {
val inlinedDeclaration = block.inlineFunction?.originalFunction?.let { it.property ?: it }
if (inlinedDeclaration != null && inlinedDeclaration.hasAnnotation(intrinsicConstEvaluationAnnotation)) {
val inlinedBlock = block.statements.single() as IrInlinedFunctionBlock
callStack.pushCompoundInstruction(inlinedBlock.inlineCall)
return
}
}
callStack.newSubFrame(block)
callStack.pushSimpleInstruction(block)
unfoldStatements(block.statements, callStack)
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
@@ -1,5 +1,4 @@
// IGNORE_BACKEND_K1: WASM
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
var initialized = 0
object O {
@@ -1,6 +1,7 @@
// !LANGUAGE: +IntrinsicConstEvaluation
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: JVM_IR
// TARGET_BACKEND: JS_IR
// IGNORE_BACKEND_K1: JVM_IR, JS_IR, JS_IR_ES6
// WITH_STDLIB
fun <T> T.id() = this
@@ -1,6 +1,7 @@
// !LANGUAGE: +IntrinsicConstEvaluation
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: JVM_IR
// TARGET_BACKEND: JS_IR
// IGNORE_BACKEND_K1: JVM_IR, JS_IR, JS_IR_ES6
// WITH_STDLIB
fun <T> T.id() = this
@@ -1,6 +1,7 @@
// !LANGUAGE: +IntrinsicConstEvaluation
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: JVM_IR
// TARGET_BACKEND: JS_IR
// IGNORE_BACKEND_K1: JVM_IR, JS_IR, JS_IR_ES6
fun <T> T.id() = this
enum class EnumClass {
@@ -1,5 +1,6 @@
// !LANGUAGE: +IntrinsicConstEvaluation
// TARGET_BACKEND: JVM_IR
// TARGET_BACKEND: JS_IR
fun <T> T.id() = this
@@ -1,5 +1,7 @@
// !LANGUAGE: +IntrinsicConstEvaluation
// TARGET_BACKEND: JVM_IR
// TARGET_BACKEND: JS_IR
fun <T> T.id() = this
const val trueVal = <!EVALUATED("true")!>true<!>
@@ -1,6 +1,8 @@
// !LANGUAGE: +IntrinsicConstEvaluation
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: JVM_IR
// TARGET_BACKEND: JS_IR
// IGNORE_BACKEND_K1: JVM_IR, JS_IR, JS_IR_ES6
fun <T> T.id() = this
class A(val OK: Int, val somePropertyWithLongName: String) {
@@ -1,15 +1,18 @@
// !LANGUAGE: +IntrinsicConstEvaluation
// TARGET_BACKEND: JVM_IR
// TARGET_BACKEND: JS_IR
// WITH_STDLIB
fun <T> T.id() = this
fun someSideEffect(value: Any?) = {}
class A {
val a = ""
fun b() = ""
init {
println("A init")
someSideEffect("A init")
}
fun test() {
@@ -1,6 +1,7 @@
// !LANGUAGE: +IntrinsicConstEvaluation
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: JVM_IR
// TARGET_BACKEND: JS_IR
// IGNORE_BACKEND_K1: JVM_IR, JS_IR, JS_IR_ES6
// FILE: 1.kt
@@ -1,5 +1,7 @@
// !LANGUAGE: +IntrinsicConstEvaluation
// TARGET_BACKEND: JVM_IR
// TARGET_BACKEND: JS_IR
var result = "Fail"
object O {
@@ -1,7 +1,6 @@
// TARGET_BACKEND: JS_IR
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
// IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6
// TODO enable for K2 when const lowering is applied for js
fun <T> T.id() = this
const val toStringDouble1 = 1.0.<!EVALUATED("1")!>toString()<!>
@@ -1,4 +1,5 @@
// TARGET_BACKEND: JVM_IR
// TARGET_BACKEND: JS_IR
annotation class Key(val value: String)
object Messanger {
@@ -1,5 +1,5 @@
// TARGET_BACKEND: JVM_IR
// TODO enable for JS, Native when const lowering is applied in corresponding backends
// TARGET_BACKEND: JS_IR
// WITH_STDLIB
object Test {
@@ -1,5 +1,5 @@
// TARGET_BACKEND: JVM_IR
// TODO enable for JS, Native when const lowering is applied in corresponding backends
// TARGET_BACKEND: JS_IR
object Test
@@ -1,4 +1,5 @@
// TARGET_BACKEND: JVM_IR
// TARGET_BACKEND: JS_IR
// This test is needed to check that IrCompileTimeChecker will not fail trying to find and analyze correct toString method
object Obj {