JS/Inlining: introduce removal of unused variables

This commit is contained in:
Alexey Andreev
2016-05-05 10:52:10 +03:00
parent 776c7447b1
commit 551ed28d84
6 changed files with 133 additions and 3 deletions
@@ -33,6 +33,7 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
private val namesToProcess = mutableSetOf<JsName>()
private val statementsToRemove = mutableSetOf<JsNode>()
private val namesToSubstitute = mutableSetOf<JsName>()
private val variablesToRemove = mutableSetOf<JsName>()
fun apply(): Boolean {
analyze()
@@ -268,7 +269,13 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
}
return
}
else if (shouldConsiderUnused(name)) {
variablesToRemove += name
handleTopLevel(value)
return
}
}
handleTopLevel(expression)
}
@@ -383,6 +390,9 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
}
}
else {
if (shouldConsiderUnused(name)) {
variablesToRemove += name
}
handleTopLevel(initializer)
}
}
@@ -428,8 +438,33 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
private fun cleanUp() {
object : JsVisitorWithContextImpl() {
override fun visit(x: JsVars, ctx: JsContext<*>): Boolean {
x.vars.removeAll { it in statementsToRemove }
override fun visit(x: JsVars, ctx: JsContext<JsNode>): Boolean {
x.vars.removeAll(statementsToRemove)
var lastDroppedIndex = 0
for ((index, v) in x.vars.toList().withIndex()) {
val name = v.name
if (name in variablesToRemove) {
hasChanges = true
if (index > lastDroppedIndex) {
val droppedVars = JsVars(*x.vars.subList(lastDroppedIndex, index).toTypedArray())
droppedVars.synthetic = x.synthetic
ctx.addPrevious(droppedVars)
}
val initExpression = v.initExpression
if (initExpression != null) {
ctx.addPrevious(JsExpressionStatement(initExpression).apply {
synthetic = true
accept(this)
})
}
lastDroppedIndex = index + 1
}
}
if (lastDroppedIndex > 0) {
x.vars.subList(0, lastDroppedIndex).clear()
}
if (x.vars.isEmpty()) {
ctx.removeMe()
hasChanges = true
@@ -438,12 +473,27 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
return super.visit(x, ctx)
}
override fun visit(x: JsExpressionStatement, ctx: JsContext<*>): Boolean {
override fun visit(x: JsExpressionStatement, ctx: JsContext<JsNode>): Boolean {
if (x in statementsToRemove) {
ctx.removeMe()
hasChanges = true
return false
}
val assignment = JsAstUtils.decomposeAssignmentToVariable(x.expression)
if (assignment != null) {
val (name, value) = assignment
if (name in variablesToRemove) {
hasChanges = true
JsExpressionStatement(value).apply {
synthetic = true
accept(this)
ctx.replaceMe(this)
}
return false
}
}
return super.visit(x, ctx)
}
@@ -481,6 +531,8 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
usages[name] = (usages[name] ?: 0) + 1
}
private fun shouldConsiderUnused(name: JsName) = (definitions[name] ?: 0) == 1 && (usages[name] ?: 0) == 0 && name in temporary
private fun shouldConsiderTemporary(name: JsName): Boolean {
if (definitions[name] ?: 0 != 1 || name !in temporary) return false
@@ -34,4 +34,10 @@ class TemporaryVariableEliminationTest : BasicOptimizerTest("temporary-variable"
@Test fun nonSideEffect() = box()
@Test fun propertyAccess() = box()
@Test fun removeUnused() = box()
@Test fun innerExpressionProcessed() = box()
@Test fun transitiveNotConsideredTrivial() = box()
}
@@ -0,0 +1,5 @@
function box() {
var a = 2;
return "OK";
}
@@ -0,0 +1,7 @@
function box() {
var a = 2;
var $b = a;
var $c = 2 + $b;
return "OK";
}
@@ -0,0 +1,30 @@
var global = 0;
function test1() {
global++;
return global;
}
function test2() {
global++;
return global;
}
function test3() {
global++;
var $b = global--;
return $b + $b;
}
function box() {
var result = test1();
if (result != 1) return "fail1: " + result;
result = test2();
if (result != 2) return "fail2: " + result;
result = test3();
if (result != 6) return "fail3: " + result;
return "OK"
}
@@ -0,0 +1,30 @@
var global = 0;
function test1() {
var $tmp = global++;
return global;
}
function test2() {
var $tmp;
$tmp = global++;
return global;
}
function test3() {
var $a = global++, $b = global--;
return $b + $b;
}
function box() {
var result = test1();
if (result != 1) return "fail1: " + result;
result = test2();
if (result != 2) return "fail2: " + result;
result = test3();
if (result != 6) return "fail3: " + result;
return "OK"
}