JS: during translation of expression mark assignment to temporary variables as synthetic statement.

This helps optimizer to find and eliminate temporary variables.
This commit is contained in:
Alexey Andreev
2016-05-10 12:22:40 +03:00
parent 82babaff7e
commit fe5a7aaf46
8 changed files with 69 additions and 25 deletions
@@ -133,7 +133,7 @@ public class JsFor extends SourceInfoAwareJsNode implements JsStatement {
if (initVars != null) { if (initVars != null) {
result = new JsFor(initVars.deepCopy(), conditionCopy, incrementalExprCopy, bodyCopy); result = new JsFor(initVars.deepCopy(), conditionCopy, incrementalExprCopy, bodyCopy);
} else { } else {
result = new JsFor(initExpression.deepCopy(), conditionCopy, incrementExpression, bodyCopy); result = new JsFor(initExpression.deepCopy(), conditionCopy, incrementalExprCopy, bodyCopy);
} }
return result.withMetadataFrom(this); return result.withMetadataFrom(this);
@@ -121,6 +121,16 @@ internal class TemporaryAssignmentElimination(private val root: JsBlock) {
override fun visitBreak(x: JsBreak) { } override fun visitBreak(x: JsBreak) { }
override fun visitContinue(x: JsContinue) { } override fun visitContinue(x: JsContinue) { }
override fun visitFor(x: JsFor) {
x.initVars?.let { it.vars.forEach { it.initExpression?.let { accept(it) } } }
x.initExpression?.let { accept(it) }
x.condition?.let { accept(it) }
x.body?.let { accept(it) }
x.incrementExpression?.let { accept(it) }
super.visitFor(x)
}
}.accept(root) }.accept(root)
usages.keys.retainAll(syntheticNames) usages.keys.retainAll(syntheticNames)
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.js.translate.context; package org.jetbrains.kotlin.js.translate.context;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -57,11 +58,14 @@ public final class DynamicContext {
public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) { public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) {
if (vars == null) { if (vars == null) {
vars = new JsVars(); vars = new JsVars();
MetadataProperties.setSynthetic(vars, true);
currentBlock.getStatements().add(vars); currentBlock.getStatements().add(vars);
} }
JsName temporaryName = currentScope.declareTemporary(); JsName temporaryName = currentScope.declareTemporary();
vars.add(new JsVar(temporaryName, null)); JsVar var = new JsVar(temporaryName, null);
MetadataProperties.setSynthetic(var, true);
vars.add(var);
return TemporaryVariable.create(temporaryName, initExpression); return TemporaryVariable.create(temporaryName, initExpression);
} }
@@ -16,9 +16,11 @@
package org.jetbrains.kotlin.js.translate.context; package org.jetbrains.kotlin.js.translate.context;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsName; import com.google.dart.compiler.backend.js.ast.JsName;
import com.google.dart.compiler.backend.js.ast.JsNameRef; import com.google.dart.compiler.backend.js.ast.JsNameRef;
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
@@ -26,7 +28,12 @@ import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
public class TemporaryVariable { public class TemporaryVariable {
/*package*/ static TemporaryVariable create(@NotNull JsName temporaryName, @Nullable JsExpression initExpression) { /*package*/ static TemporaryVariable create(@NotNull JsName temporaryName, @Nullable JsExpression initExpression) {
return new TemporaryVariable(temporaryName, initExpression == null ? null : JsAstUtils.assignment(temporaryName.makeRef(), initExpression)); JsBinaryOperation rhs = null;
if (initExpression != null) {
rhs = JsAstUtils.assignment(temporaryName.makeRef(), initExpression);
MetadataProperties.setSynthetic(rhs, true);
}
return new TemporaryVariable(temporaryName, rhs);
} }
@Nullable @Nullable
@@ -29,9 +29,8 @@ import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator; import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator;
import org.jetbrains.kotlin.js.translate.declaration.PropertyTranslatorKt;
import org.jetbrains.kotlin.js.translate.expression.loopTranslator.LoopTranslator;
import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.declaration.PropertyTranslatorKt;
import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor; import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor;
import org.jetbrains.kotlin.js.translate.operation.BinaryOperationTranslator; import org.jetbrains.kotlin.js.translate.operation.BinaryOperationTranslator;
import org.jetbrains.kotlin.js.translate.operation.UnaryOperationTranslator; import org.jetbrains.kotlin.js.translate.operation.UnaryOperationTranslator;
@@ -16,14 +16,13 @@
@file:JvmName("LoopTranslator") @file:JvmName("LoopTranslator")
package org.jetbrains.kotlin.js.translate.expression.loopTranslator package org.jetbrains.kotlin.js.translate.expression
import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable import org.jetbrains.kotlin.js.translate.context.TemporaryVariable
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.DestructuringDeclarationTranslator
import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.CompositeFIF import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.CompositeFIF
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getHasNextCallable import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getHasNextCallable
@@ -33,7 +32,6 @@ import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeForExpression
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.* import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopParameter import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopParameter
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopRange import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopRange
import org.jetbrains.kotlin.js.translate.utils.TemporariesUtils.temporariesInitialization
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBinaryExpression
@@ -117,16 +115,16 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
val destructuringParameter: KtDestructuringDeclaration? = expression.destructuringParameter val destructuringParameter: KtDestructuringDeclaration? = expression.destructuringParameter
fun declareParameter(): JsName { fun declareParameter(): Pair<JsName, Boolean> {
val loopParameter = getLoopParameter(expression) val loopParameter = getLoopParameter(expression)
if (loopParameter != null) { if (loopParameter != null) {
return context.getNameForElement(loopParameter) return Pair(context.getNameForElement(loopParameter), false)
} }
assert(destructuringParameter != null) { "If loopParameter is null, multi parameter must be not null ${expression.text}" } assert(destructuringParameter != null) { "If loopParameter is null, multi parameter must be not null ${expression.text}" }
return context.scope().declareTemporary() return Pair(context.scope().declareTemporary(), true)
} }
val parameterName: JsName = declareParameter() val (parameterName, parameterIsTemporary) = declareParameter()
fun translateBody(itemValue: JsExpression?): JsStatement? { fun translateBody(itemValue: JsExpression?): JsStatement? {
val realBody = expression.body?.let { Translation.translateAsStatementAndMergeInBlockIfNeeded(it, context) } val realBody = expression.body?.let { Translation.translateAsStatementAndMergeInBlockIfNeeded(it, context) }
@@ -135,10 +133,12 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
} }
else { else {
val currentVarInit = val currentVarInit =
if (destructuringParameter == null) if (destructuringParameter == null) {
newVar(parameterName, itemValue) newVar(parameterName, itemValue)
else }
else {
DestructuringDeclarationTranslator.translate(destructuringParameter, parameterName, itemValue, context) DestructuringDeclarationTranslator.translate(destructuringParameter, parameterName, itemValue, context)
}
if (realBody == null) return JsBlock(currentVarInit) if (realBody == null) return JsBlock(currentVarInit)
@@ -170,12 +170,16 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
val rangeEnd = context.declareTemporary(rightExpression) val rangeEnd = context.declareTemporary(rightExpression)
val body = translateBody(null) val body = translateBody(null)
val initExpression = newVar(parameterName, rangeStart)
val conditionExpression = lessThanEq(parameterName.makeRef(), rangeEnd.reference()) val conditionExpression = lessThanEq(parameterName.makeRef(), rangeEnd.reference())
val incrementExpression = JsPostfixOperation(JsUnaryOperator.INC, parameterName.makeRef()) val incrementExpression = JsPostfixOperation(JsUnaryOperator.INC, parameterName.makeRef())
context.addStatementToCurrentBlock(temporariesInitialization(rangeEnd).makeStmt()) context.addStatementToCurrentBlock(asSyntheticStatement(rangeEnd.assignmentExpression()))
return JsFor(initExpression, conditionExpression, incrementExpression, body) return if (parameterIsTemporary) {
JsFor(assignment(parameterName.makeRef(), rangeStart), conditionExpression, incrementExpression, body)
}
else {
JsFor(newVar(parameterName, rangeStart), conditionExpression, incrementExpression, body)
}
} }
fun translateForOverRange(): JsStatement { fun translateForOverRange(): JsStatement {
@@ -188,12 +192,21 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
val increment = context.declareTemporary(getProperty("step")) val increment = context.declareTemporary(getProperty("step"))
val body = translateBody(null) val body = translateBody(null)
val initExpression = newVar(parameterName, start.reference())
val conditionExpression = lessThanEq(parameterName.makeRef(), end.reference()) val conditionExpression = lessThanEq(parameterName.makeRef(), end.reference())
val incrementExpression = addAssign(parameterName.makeRef(), increment.reference()) val incrementExpression = addAssign(parameterName.makeRef(), increment.reference())
context.addStatementToCurrentBlock(temporariesInitialization(rangeExpression, start, end, increment).makeStmt()) context.addStatementToCurrentBlock(asSyntheticStatement(rangeExpression.assignmentExpression()))
return JsFor(initExpression, conditionExpression, incrementExpression, body) context.addStatementToCurrentBlock(asSyntheticStatement(start.assignmentExpression()))
context.addStatementToCurrentBlock(asSyntheticStatement(end.assignmentExpression()))
context.addStatementToCurrentBlock(asSyntheticStatement(increment.assignmentExpression()))
return if (parameterIsTemporary) {
JsFor(assignment(parameterName.makeRef(), start.reference()), conditionExpression, incrementExpression, body)
}
else {
JsFor(newVar(parameterName, start.reference()), conditionExpression, incrementExpression, body)
}
} }
fun translateForOverArray(): JsStatement { fun translateForOverArray(): JsStatement {
@@ -204,11 +217,12 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
val arrayAccess = JsArrayAccess(rangeExpression.reference(), index.reference()) val arrayAccess = JsArrayAccess(rangeExpression.reference(), index.reference())
val body = translateBody(arrayAccess) val body = translateBody(arrayAccess)
val initExpression = newVar(index.name(), context.program().getNumberLiteral(0)) val initExpression = assignment(index.name().makeRef(), context.program().getNumberLiteral(0))
val conditionExpression = inequality(index.reference(), end.reference()) val conditionExpression = inequality(index.reference(), end.reference())
val incrementExpression = JsPrefixOperation(JsUnaryOperator.INC, index.reference()) val incrementExpression = JsPrefixOperation(JsUnaryOperator.INC, index.reference())
context.addStatementToCurrentBlock(temporariesInitialization(rangeExpression, end).makeStmt()) context.addStatementToCurrentBlock(asSyntheticStatement(rangeExpression.assignmentExpression()))
context.addStatementToCurrentBlock(asSyntheticStatement(end.assignmentExpression()))
return JsFor(initExpression, conditionExpression, incrementExpression, body) return JsFor(initExpression, conditionExpression, incrementExpression, body)
} }
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.js.translate.expression; package org.jetbrains.kotlin.js.translate.expression;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable; import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
@@ -57,9 +58,12 @@ public final class WhenTranslator extends AbstractTranslator {
KtExpression subject = expression.getSubjectExpression(); KtExpression subject = expression.getSubjectExpression();
if (subject != null) { if (subject != null) {
JsExpression subjectExpression = Translation.translateAsExpression(subject, context); JsExpression subjectExpression = Translation.translateAsExpression(subject, context);
if (TranslationUtils.isCacheNeeded(subjectExpression)) { if (!JsAstUtils.isEmptyExpression(subjectExpression)) {
TemporaryVariable subjectVar = context.declareTemporary(null); TemporaryVariable subjectVar = context.declareTemporary(null);
context.addStatementToCurrentBlock(JsAstUtils.assignment(subjectVar.reference(), subjectExpression).makeStmt()); JsExpressionStatement subjectAssignment = new JsExpressionStatement(
JsAstUtils.assignment(subjectVar.reference(), subjectExpression));
MetadataProperties.setSynthetic(subjectAssignment, true);
context.addStatementToCurrentBlock(subjectAssignment);
subjectExpression = subjectVar.reference(); subjectExpression = subjectVar.reference();
} }
expressionToMatch = subjectExpression; expressionToMatch = subjectExpression;
@@ -302,6 +302,12 @@ public final class JsAstUtils {
return new JsBinaryOperation(JsBinaryOperator.ASG, left, right); return new JsBinaryOperation(JsBinaryOperator.ASG, left, right);
} }
public static JsStatement asSyntheticStatement(@NotNull JsExpression expression) {
JsExpressionStatement statement = new JsExpressionStatement(expression);
MetadataProperties.setSynthetic(statement, true);
return statement;
}
@Nullable @Nullable
public static Pair<JsExpression, JsExpression> decomposeAssignment(@NotNull JsExpression expr) { public static Pair<JsExpression, JsExpression> decomposeAssignment(@NotNull JsExpression expr) {
if (!(expr instanceof JsBinaryOperation)) return null; if (!(expr instanceof JsBinaryOperation)) return null;