[JS IR] Perform optimizations on the generated JS code
The patch adopts and reuses the optimizations from the legacy backend. The optimizations remove useless temporary variables, statements and simplify generated JS code. The optimizations can be disabled by `-Xoptimize-generated-js=false`. Related to KT-51139
This commit is contained in:
committed by
Space Team
parent
84b5af3c89
commit
79d378f2bd
@@ -121,4 +121,6 @@ public class JSConfigurationKeys {
|
||||
public static final CompilerConfigurationKey<ZipFileSystemAccessor> ZIP_FILE_SYSTEM_ACCESSOR =
|
||||
CompilerConfigurationKey.create("zip file system accessor, used for klib reading");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> OPTIMIZE_GENERATED_JS =
|
||||
CompilerConfigurationKey.create("perform additional optimizations on the generated JS code");
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ internal class DoWhileGuardElimination(private val root: JsStatement) {
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (guard != null) {
|
||||
if (guard != null && guard.statement !is JsLoop) {
|
||||
|
||||
// When do..while loop has no label and we encounter `break guard` from nested loop, we can't
|
||||
// replace this break with continue. Example:
|
||||
|
||||
+36
-4
@@ -88,6 +88,16 @@ import org.jetbrains.kotlin.js.translate.utils.splitToRanges
|
||||
* foo(B, $a)
|
||||
*
|
||||
* we get `$a` eliminated.
|
||||
*
|
||||
* It is also worth taking care of the temporary variables captured into closure as they cannot be simply removed.
|
||||
*
|
||||
* function test(a) {
|
||||
* var tmp_a = a // removing this temporary variable changes function behaviour
|
||||
* var f = function() { console.log(tmp_a) }
|
||||
* a = []
|
||||
* return f
|
||||
* }
|
||||
*
|
||||
*/
|
||||
internal class TemporaryVariableElimination(private val function: JsFunction) {
|
||||
private val root = function.body
|
||||
@@ -95,6 +105,7 @@ internal class TemporaryVariableElimination(private val function: JsFunction) {
|
||||
private val usages = mutableMapOf<JsName, Int>()
|
||||
private val definedValues = mutableMapOf<JsName, JsExpression>()
|
||||
private val temporary = mutableSetOf<JsName>()
|
||||
private val capturedInClosure = mutableSetOf<JsName>()
|
||||
private var hasChanges = false
|
||||
private val localVariables = function.collectLocalVariables()
|
||||
|
||||
@@ -201,6 +212,7 @@ internal class TemporaryVariableElimination(private val function: JsFunction) {
|
||||
for (freeVar in x.collectFreeVariables()) {
|
||||
useVariable(freeVar)
|
||||
useVariable(freeVar)
|
||||
capturedInClosure += freeVar
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,9 +272,12 @@ internal class TemporaryVariableElimination(private val function: JsFunction) {
|
||||
namesWithSideEffects += name
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sideEffects) {
|
||||
invalidateTemporaries()
|
||||
} else {
|
||||
if (sideEffects) {
|
||||
invalidateTemporaries()
|
||||
} else {
|
||||
invalidateTemporariesUsingName(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,6 +374,21 @@ internal class TemporaryVariableElimination(private val function: JsFunction) {
|
||||
lastAssignedVars.clear()
|
||||
}
|
||||
|
||||
private fun invalidateTemporariesUsingName(name: JsName) {
|
||||
lastAssignedVars.removeAll { (_, expr) ->
|
||||
var nameUsed = false
|
||||
object : RecursiveJsVisitor() {
|
||||
override fun visitNameRef(nameRef: JsNameRef) {
|
||||
if (nameRef.name == name) {
|
||||
nameUsed = true
|
||||
}
|
||||
super.visitNameRef(nameRef)
|
||||
}
|
||||
}.accept(expr)
|
||||
nameUsed
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleExpression(expression: JsExpression): Boolean {
|
||||
val candidateFinder = SubstitutionCandidateFinder()
|
||||
candidateFinder.accept(expression)
|
||||
@@ -593,7 +623,9 @@ internal class TemporaryVariableElimination(private val function: JsFunction) {
|
||||
(definitions[name] ?: 0) > 0 && (usages[name] ?: 0) == 0 && name in temporary && !name.imported
|
||||
|
||||
private fun shouldConsiderTemporary(name: JsName): Boolean {
|
||||
if (definitions[name] != 1 || name !in temporary) return false
|
||||
if (definitions[name] != 1 || name !in temporary || name in capturedInClosure) {
|
||||
return false
|
||||
}
|
||||
|
||||
val expr = definedValues[name]
|
||||
// It's useful to copy trivial expressions when they are used more than once. Example are temporary variables
|
||||
|
||||
+1
-1
@@ -12,4 +12,4 @@ fun baz() = 1
|
||||
fun bar() = 2
|
||||
|
||||
// LINES(JS): 1 3 3 2 2 4 3 4 4 4 5 5 2 8 8 10 10 10 12 12 12
|
||||
// LINES(JS_IR): 1 2 8 * 3 4 5 * 8 8 10 10 10 10 12 12 12 12
|
||||
// LINES(JS_IR): 1 2 8 * 4 3 4 5 * 8 8 10 10 10 10 12 12 12 12
|
||||
|
||||
@@ -14,5 +14,5 @@ private inline fun foo(): Int {
|
||||
return 23
|
||||
}
|
||||
|
||||
// LINES(JS): 1 10 3 3 3 4 3 6 13 13 3 14 2 12 15 13 13 14 14
|
||||
// LINES(JS_IR): 1 1 * 3 4 6 * 8 * 13 13 14 14 8 2 12 12 13 13 14 14
|
||||
// LINES(JS): 1 10 3 3 3 4 3 6 13 13 3 14 2 12 15 13 13 14 14
|
||||
// LINES(JS_IR): 1 1 * 3 4 6 * 13 13 14 2 12 12 13 13 14 14
|
||||
|
||||
+1
-1
@@ -15,4 +15,4 @@ suspend fun bar(): Unit {
|
||||
}
|
||||
|
||||
// LINES(JS): 39 4 4 4 7 5 5 45 45 5 93 45 5 5 6 4 4 4 9 15 9 9 9 * 9 15 10 10 11 11 11 11 11 * 11 12 12 13 13 13 13 13 13 13 14 14 * 9 15 9 9 9 9
|
||||
// LINES(JS_IR): 4 4 * 19 * 5 * 45 * 93 93 45 45 45 6 6 19 7 7 9 9 * 9 * 9 * 10 10 * 11 * 11 12 12 * 13 * 13 14 14 15 15
|
||||
// LINES(JS_IR): 4 4 * 93 93 45 45 7 7 6 9 9 * 9 * 9 * 10 10 * 11 * 11 12 12 * 13 * 13 14 14 15 15
|
||||
|
||||
@@ -33,4 +33,4 @@ inline operator fun P.component1() = a
|
||||
inline operator fun P.component2() = b
|
||||
|
||||
// LINES(JS): 15 22 17 17 31 18 18 33 20 20 21 21 22 22 3 23 9 9 9 9 4 9 9 9 6 6 31 7 7 33 11 11 12 12 15 15 25 27 26 26 29 29 29 * 31 31 31 33 33 33 * 1 * 1
|
||||
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 9 9 4 9 * 6 * 31 31 6 * 7 * 33 33 7 11 11 12 12 15 15 25 25 26 26 29 29 29 29 29 29 29 29 29 29 29 29 31 31 31 31 33 33 33 33 15 16 15 * 17 * 31 31 17 * 18 * 33 33 18 20 20 21 21 22 22 * 1
|
||||
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 9 9 4 9 * 6 31 * 7 33 11 11 12 12 15 15 25 25 26 26 29 29 29 29 29 29 29 29 29 29 29 29 31 31 31 31 33 33 33 33 15 16 15 * 17 31 * 18 33 20 20 21 21 22 22 * 1
|
||||
|
||||
+1
-1
@@ -10,4 +10,4 @@ fun box(x: String?) {
|
||||
fun foo() = "bar"
|
||||
|
||||
// LINES(JS): 3 8 7 7 4 4 4 5 5 7 7 7 7 * 5 4 5 10 10 10 * 1 * 1
|
||||
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 4 5 * 5 5 * 7 7 7 * 5 4 4 10 10 10 10 * 1
|
||||
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 4 * 5 5 * 7 7 7 * 5 4 4 10 10 10 10 * 1
|
||||
|
||||
+2
-2
@@ -17,5 +17,5 @@ fun box() {
|
||||
}
|
||||
}
|
||||
|
||||
// LINES(JS): 1 18 2 2 10 2 2 2 2 2 2 3 3 6 6 6 6 7 7 10 10 10 10 10 10 11 11 14 14 15 15 15 15 16 16
|
||||
// LINES(JS_IR): 1 1 * 2 * 35 * 18 * 12 2 18 18 35 35 2 2 2 2 2 2 2 2 3 3 6 6 6 6 6 6 6 7 7 10 10 10 10 11 11 14 15 15 15 15 15 15 15 15 16 16
|
||||
// LINES(JS): 1 18 2 2 10 2 2 2 2 2 2 3 3 6 6 6 6 7 7 10 10 10 10 10 10 11 11 14 14 15 15 15 15 16 16
|
||||
// LINES(JS_IR): 1 1 * 2 2 2 2 2 2 2 2 3 3 6 6 6 6 6 6 6 7 7 10 10 10 10 11 11 14 15 15 15 15 15 15 15 15 16 16
|
||||
|
||||
+2
-2
@@ -8,5 +8,5 @@ fun foo(x: Int) {
|
||||
println(y)
|
||||
}
|
||||
|
||||
// LINES(JS): 1 9 2 2 3 3 4 4 5 5 6 6 7 7 8 8
|
||||
// LINES(JS_IR): 1 1 2 3 3 3 4 4 5 5 6 6 7 7 8 8
|
||||
// LINES(JS): 1 9 2 2 3 3 4 4 5 5 6 6 7 7 8 8
|
||||
// LINES(JS_IR): 1 1 2 3 3 4 4 5 5 6 6 7 7 8 8
|
||||
|
||||
@@ -12,4 +12,4 @@ inline fun foo(x: Int) {
|
||||
fun bar() = 23
|
||||
|
||||
// LINES(JS): 3 5 4 4 8 8 9 9 7 10 8 8 9 9 12 12 12 * 1 * 1
|
||||
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 4 * 4 8 8 9 9 7 7 8 8 9 9 12 12 12 12 * 1
|
||||
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 * 4 8 8 9 9 7 7 8 8 9 9 12 12 12 12 * 1
|
||||
|
||||
@@ -10,4 +10,4 @@ fun bar() {
|
||||
}
|
||||
|
||||
// LINES(JS): 1 1 1 1 1 6 2 2 3 3 4 4 8 10 2 2 9 2 3 3 4 4
|
||||
// LINES(JS_IR): 1 1 2 3 3 3 4 4 8 8 9 * 2 3 3 3 4 4
|
||||
// LINES(JS_IR): 1 1 2 3 3 3 4 4 8 8 * 2 3 3 3 4 4
|
||||
|
||||
@@ -22,5 +22,5 @@ fun box() {
|
||||
foo("42")
|
||||
}
|
||||
|
||||
// LINES(JS): 6 20 23 7 7 21 7 8 8 21 8 7 7 22 7 8 8 22 8
|
||||
// LINES(JS_IR): 20 20 21 * 7 7 8 8 22 * 7 7 8 8
|
||||
// LINES(JS): 6 20 23 7 7 21 7 8 8 21 8 7 7 22 7 8 8 22 8
|
||||
// LINES(JS_IR): 20 20 * 7 7 8 8 * 7 7 8 8
|
||||
|
||||
+2
-2
@@ -11,5 +11,5 @@ inline fun bar() {
|
||||
println("bar2")
|
||||
}
|
||||
|
||||
// LINES(JS): 1 7 2 2 10 10 11 11 4 4 10 10 11 11 6 6 9 9 9 9 9 12 10 10 11 11
|
||||
// LINES(JS_IR): 1 1 2 2 3 * 10 10 11 11 4 4 5 * 10 10 11 11 6 6 9 9 10 10 11 11
|
||||
// LINES(JS): 1 7 2 2 10 10 11 11 4 4 10 10 11 11 6 6 9 9 9 9 9 12 10 10 11 11
|
||||
// LINES(JS_IR): 1 1 2 2 * 10 10 11 11 4 4 * 10 10 11 11 6 6 9 9 10 10 11 11
|
||||
|
||||
@@ -16,5 +16,5 @@ inline fun foo(f: () -> Unit) {
|
||||
println("after")
|
||||
}
|
||||
|
||||
// LINES(JS): 1 11 2 2 14 14 4 4 16 16 6 6 14 14 8 8 16 16 10 10 13 13 13 13 13 17 14 14 15 15 16 16
|
||||
// LINES(JS_IR): 1 1 2 2 3 * 14 14 15 * 4 4 16 16 6 6 7 * 14 14 15 * 8 8 16 16 10 10 13 13 14 14 15 15 16 16
|
||||
// LINES(JS): 1 11 2 2 14 14 4 4 16 16 6 6 14 14 8 8 16 16 10 10 13 13 13 13 13 17 14 14 15 15 16 16
|
||||
// LINES(JS_IR): 1 1 2 2 * 14 14 * 4 4 16 16 6 6 * 14 14 * 8 8 16 16 10 10 13 13 14 14 15 15 16 16
|
||||
|
||||
+2
-2
@@ -11,5 +11,5 @@ inline fun foo(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
// LINES(JS): 3 7 4 4 4 10 10 4 11 4 5 5 9 12 10 10 11 11 * 1 * 1
|
||||
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 * 4 4 * 4 * 10 10 11 11 4 4 4 5 5 9 9 10 10 11 11 * 1
|
||||
// LINES(JS): 3 7 4 4 4 10 10 4 11 4 5 5 9 12 10 10 11 11 * 1 * 1
|
||||
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 * 4 4 * 10 10 11 4 4 4 5 5 9 9 10 10 11 11 * 1
|
||||
|
||||
@@ -24,5 +24,5 @@ fun baz() = "baz"
|
||||
|
||||
fun boo() = "boo"
|
||||
|
||||
// LINES(JS): 1 17 9 4 6 6 7 7 3 3 3 * 13 12 13 13 14 19 21 20 20 23 23 23 25 25 25
|
||||
// LINES(JS_IR): 1 1 * 2 * 20 * 4 * 6 7 * 3 20 20 * 11 * 20 * 12 12 13 14 20 20 19 19 20 20 23 23 23 23 25 25 25 25
|
||||
// LINES(JS): 1 17 9 4 6 6 7 7 3 3 3 * 13 12 13 13 14 19 21 20 20 23 23 23 25 25 25
|
||||
// LINES(JS_IR): 1 1 * 4 * 6 7 * 13 12 13 19 19 20 20 23 23 23 23 25 25 25 25
|
||||
|
||||
@@ -19,4 +19,4 @@ fun box(x: Int) {
|
||||
}
|
||||
|
||||
// LINES(JS): 1 19 4 4 3 3 4 6 9 9 6 11 13 13 11 16 16 3 2
|
||||
// LINES(JS_IR): 1 1 4 2 8 7 6 7 8 9 12 11 12 13 16
|
||||
// LINES(JS_IR): 1 1 2 8 7 6 4 6 7 4 7 8 4 8 9 12 11 4 11 12 4 12 13 16
|
||||
|
||||
+1
-1
@@ -29,4 +29,4 @@ fun four() = 4
|
||||
fun five() = 5
|
||||
|
||||
// LINES(JS): 1 19 4 4 3 6 4 6 4 7 4 8 9 9 11 4 11 4 12 13 13 16 16 2 21 21 21 23 23 23 25 25 25 27 27 27 29 29 29
|
||||
// LINES(JS_IR): 1 1 4 2 8 7 6 7 8 9 12 11 12 13 16 21 21 21 21 23 23 23 23 25 25 25 25 27 27 27 27 29 29 29 29
|
||||
// LINES(JS_IR): 1 1 2 8 7 6 4 6 7 4 7 8 4 8 9 12 11 4 11 12 4 12 13 16 21 21 21 21 23 23 23 23 25 25 25 25 27 27 27 27 29 29 29 29
|
||||
|
||||
Reference in New Issue
Block a user