Revision: 7984
Author: sco...@google.com
Date: Mon Apr 26 10:59:53 2010
Log: Unifies temp local tracking under a shared base visitor.
- All temps now definitely get their own JDeclarationStatement inserted
into the AST at the appropriate point.
- Temp locals are never reused anymore; however, whenever legal we reuse
names in order to produce smaller compiled output.
http://gwt-code-reviews.appspot.com/354801/show
Review by: spoon
http://code.google.com/p/google-web-toolkit/source/detail?r=7984
Added:
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/TempLocalVisitor.java
/branches/2.1/dev/core/test/com/google/gwt/dev/jjs/impl/TempLocalVisitorTest.java
Modified:
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/FixAssignmentToUnbox.java
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java
/branches/2.1/dev/core/test/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizerTest.java
=======================================
--- /dev/null
+++
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/TempLocalVisitor.java
Mon Apr 26 10:59:53 2010
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.impl;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JBlock;
+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
+import com.google.gwt.dev.jjs.ast.JLocal;
+import com.google.gwt.dev.jjs.ast.JLocalRef;
+import com.google.gwt.dev.jjs.ast.JMethodBody;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JStatement;
+import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.jjs.ast.JVariable;
+import com.google.gwt.dev.jjs.ast.JVisitor;
+
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * Utility base class for visitors that need to replace expressions with
temp
+ * locals. This class specifically handles allocating temp locals, and
inserting
+ * a {...@link JDeclarationStatement} at the appropriate point. It tracks
scopes
+ * and assigns any potentially conflicting uses with unique names.
Subclasses
+ * are only allowed to use a temp within the {...@link JBlock} within which
it is
+ * allocated. Non-conflicting temp locals attempt to reuse as many names as
+ * possible in order to produce the most optimal output.
+ *
+ * <p>
+ * Subclasses must always visit a {...@link JMethodBody} naturally, rather
than
+ * individual blocks.
+ * </p>
+ */
+public abstract class TempLocalVisitor extends JModVisitor {
+ /*
+ * TODO(scottb): right now our handling of for statements is sub-optimal.
+ * Technically, a for statement creates an implicit scope that is not
part of
+ * any block. This scope is a child of the block containing the for
statement,
+ * and a parent of the for statement's action block. We don't presently
model
+ * this scope, instead we just allow the for statement's top-level
constructs
+ * to bleed up into the containing block, which is correct if
sub-optimal for
+ * name reuse.
+ */
+
+ /**
+ * Creates a Scope for each JBlock in the current method body.
+ */
+ private static class CollectScopes extends JVisitor {
+
+ private Scope curScope = null;
+
+ private final Map<JBlock, Scope> scopes;
+
+ public CollectScopes(Map<JBlock, Scope> scopes) {
+ this.scopes = scopes;
+ }
+
+ @Override
+ public void endVisit(JBlock x, Context ctx) {
+ exit(x);
+ }
+
+ @Override
+ public void endVisit(JDeclarationStatement x, Context ctx) {
+ JVariable target = x.getVariableRef().getTarget();
+ if (target instanceof JLocal) {
+ String name = target.getName();
+ if (name.startsWith(PREFIX)) {
+ curScope.recordTempAllocated(Integer.parseInt(
+ name.substring(PREFIX.length()), 10));
+ }
+ }
+ }
+
+ @Override
+ public boolean visit(JBlock x, Context ctx) {
+ enter(x);
+ return true;
+ }
+
+ private void enter(JBlock x) {
+ curScope = new Scope(curScope);
+ scopes.put(x, curScope);
+ }
+
+ private void exit(JBlock x) {
+ assert scopes.get(x) == curScope;
+ curScope = curScope.parent;
+ }
+ }
+
+ /**
+ * Represents a single logical scope (ie, JBlock), and tracks allocation
and
+ * usage of any temps.
+ */
+ private static class Scope {
+ /**
+ * My containing scope, will be <code>null</code> if this scope
corresponds
+ * to the top-level method body block.
+ */
+ public final Scope parent;
+
+ /**
+ * Caches which temps have been allocated both in this scope and all
parent
+ * scopes.
+ */
+ private transient BitSet allAllocated;
+
+ /**
+ * Caches the last temp allocated in this scope, for speedier lookup.
+ */
+ private transient int lastTemp;
+
+ /**
+ * The set of all temps allocated by all my child blocks. This
prevents me
+ * from allocating a temp one of my children already allocated;
however it
+ * does not prevent my children from reusing temps also used by their
+ * siblings.
+ */
+ private final BitSet myChildTemps = new BitSet();
+
+ /**
+ * The set of temps that have been directly allocated in this scope.
+ */
+ private final BitSet myTemps = new BitSet();
+
+ public Scope(Scope parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Acquires the next free temp in this scope.
+ */
+ public int allocateNextFreeTemp() {
+ if (allAllocated == null) {
+ allAllocated = new BitSet();
+ // Any temps already allocated by my parents are not available to
me.
+ for (Scope it = this; it != null; it = it.parent) {
+ allAllocated.or(it.myTemps);
+ }
+ }
+ // Any temps already allocated by my children are not available to
me.
+ allAllocated.or(myChildTemps);
+ // Find the next free temp.
+ lastTemp = allAllocated.nextClearBit(lastTemp);
+ recordTempAllocated(lastTemp);
+ return lastTemp;
+ }
+
+ /**
+ * Called when entering this scope, clears transient state.
+ */
+ public void enter() {
+ // Assume dirty, lazy recompute.
+ allAllocated = null;
+ lastTemp = 0;
+ }
+
+ /**
+ * Called when exiting this scope.
+ */
+ public void exit() {
+ // Free the memory.
+ allAllocated = null;
+ }
+
+ /**
+ * Record a temp as being allocated in this scope.
+ */
+ public void recordTempAllocated(int tempNumber) {
+ assert !myTemps.get(tempNumber);
+ // Record my own usage.
+ myTemps.set(tempNumber);
+ if (allAllocated != null) {
+ allAllocated.set(tempNumber);
+ }
+ // Tell all my parents I'm now using this one.
+ for (Scope it = this.parent; it != null; it = it.parent) {
+ assert !it.myTemps.get(tempNumber);
+ it.myChildTemps.set(tempNumber);
+ }
+ }
+ }
+
+ /**
+ * Prefix for temp locals.
+ */
+ private static final String PREFIX = "$t";
+
+ private JMethodBody curMethodBody = null;
+ private Scope curScope = null;
+ private final Stack<Context> insertionStack = new Stack<Context>();
+ private Map<JBlock, Scope> scopes = null;
+
+ @Override
+ public final void endVisit(JBlock x, Context ctx) {
+ exit(x);
+ super.endVisit(x, ctx);
+ }
+
+ @Override
+ public final void endVisit(JMethodBody x, Context ctx) {
+ curMethodBody = null;
+ scopes = null;
+ super.endVisit(x, ctx);
+ }
+
+ @Override
+ public final void endVisit(JStatement x, Context ctx) {
+ if (ctx.canInsert()) {
+ Context popped = insertionStack.pop();
+ assert popped == ctx;
+ }
+ super.endVisit(x, ctx);
+ }
+
+ @Override
+ public final boolean visit(JBlock x, Context ctx) {
+ enter(x);
+ return super.visit(x, ctx);
+ }
+
+ @Override
+ public final boolean visit(JMethodBody x, Context ctx) {
+ curMethodBody = x;
+ scopes = new HashMap<JBlock, Scope>();
+ new CollectScopes(scopes).accept(x);
+ return super.visit(x, ctx);
+ }
+
+ @Override
+ public final boolean visit(JStatement x, Context ctx) {
+ if (ctx.canInsert()) {
+ insertionStack.push(ctx);
+ }
+ return super.visit(x, ctx);
+ }
+
+ protected JLocal createTempLocal(SourceInfo info, JType type) {
+ int tempNum = curScope.allocateNextFreeTemp();
+ String name = PREFIX + tempNum;
+ JLocal local = JProgram.createLocal(info, name, type, false,
curMethodBody);
+ JDeclarationStatement init = new JDeclarationStatement(info, new
JLocalRef(
+ info, local), null);
+ insertionStack.peek().insertBefore(init);
+ return local;
+ }
+
+ private boolean enter(JBlock x) {
+ Scope enterScope = scopes.get(x);
+ assert enterScope.parent == curScope;
+ curScope = enterScope;
+ enterScope.enter();
+ return true;
+ }
+
+ private void exit(JBlock x) {
+ assert scopes.get(x) == curScope;
+ curScope.exit();
+ curScope = curScope.parent;
+ }
+}
=======================================
--- /dev/null
+++
/branches/2.1/dev/core/test/com/google/gwt/dev/jjs/impl/TempLocalVisitorTest.java
Mon Apr 26 10:59:53 2010
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.impl;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JBinaryOperation;
+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
+import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JLocal;
+import com.google.gwt.dev.jjs.ast.JLocalRef;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JPostfixOperation;
+import com.google.gwt.dev.jjs.ast.JPrefixOperation;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JType;
+
+/**
+ * Test for {...@link TempLocalVisitor}.
+ */
+public class TempLocalVisitorTest extends OptimizerTestBase {
+ /**
+ * For testing purposes; replaces all non-lvalue expressions with a temp.
+ */
+ private static final class AlwaysReplacer extends TempLocalVisitor {
+ @Override
+ public void endVisit(JExpression x, Context ctx) {
+ SourceInfo info = x.getSourceInfo();
+ JType type = x.getType();
+ JLocal local = createTempLocal(info, type);
+ local.getDeclarationStatement().initializer = x;
+ ctx.replaceMe(new JLocalRef(info, local));
+ }
+
+ @Override
+ public boolean visit(JBinaryOperation x, Context ctx) {
+ super.visit(x, ctx);
+ if (x.getRhs() != null) {
+ JExpression newRhs = accept(x.getRhs());
+ if (newRhs != x.getRhs()) {
+ ctx.replaceMe(new JBinaryOperation(x.getSourceInfo(),
x.getType(),
+ x.getOp(), x.getLhs(), newRhs));
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(JDeclarationStatement x, Context ctx) {
+ super.visit(x, ctx);
+ if (x.initializer != null) {
+ x.initializer = accept(x.initializer);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(JPostfixOperation x, Context ctx) {
+ if (x.getOp().isModifying()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visit(JPrefixOperation x, Context ctx) {
+ if (x.getOp().isModifying()) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private final class Result {
+ private final String optimized;
+ private final String returnType;
+ private final String userCode;
+
+ public Result(String returnType, String userCode, String optimized) {
+ this.returnType = returnType;
+ this.userCode = userCode;
+ this.optimized = optimized;
+ }
+
+ public void into(String expected) throws UnableToCompleteException {
+ JProgram program = compileSnippet(returnType, expected);
+ expected = getMainMethodSource(program);
+ assertEquals(userCode, expected, optimized);
+ }
+ }
+
+ public void testBasic() throws Exception {
+ assertTransform("int i = 3;").into("int $t0 = 3; int i = $t0;");
+ }
+
+ public void testExisting() throws Exception {
+ assertTransform("int $t0 = 3; int i = $t0;").into(
+ "int $t1 = 3; int $t0 = $t1; int $t2 = $t0; int i = $t2;");
+ }
+
+ public void testForStatement() throws Exception {
+ StringBuilder original = new StringBuilder();
+ original.append("for (int i = 3; true; );");
+
+ StringBuilder expected = new StringBuilder();
+ /*
+ * TODO(scottb): technically $t1 could be part of the for statement's
+ * initializer list.
+ */
+ expected.append("boolean $t1 = true;");
+ expected.append("for (int $t0 = 3, i = $t0; $t1; );");
+
+ assertTransform(original.toString()).into(expected.toString());
+ }
+
+ public void testForStatementScoping() throws Exception {
+ StringBuilder original = new StringBuilder();
+ original.append("boolean f = false;");
+ original.append("for (int i = 3; f; );");
+ original.append("int j = 4;");
+
+ StringBuilder expected = new StringBuilder();
+ expected.append("boolean $t0 = false;");
+ expected.append("boolean f = $t0;");
+ expected.append("boolean $t2 = f;");
+ expected.append("for (int $t1 = 3, i = $t1; $t2; );");
+ /*
+ * TODO(scottb): technically we could reuse $t1 here as a minor
improvement.
+ */
+ expected.append("int $t3 = 4; int j = $t3;");
+
+ assertTransform(original.toString()).into(expected.toString());
+ }
+
+ public void testNested() throws Exception {
+ assertTransform("{ int i = 3; } ").into("{ int $t0 = 3; int i = $t0;
}");
+ }
+
+ public void testNestedPost() throws Exception {
+ StringBuilder original = new StringBuilder();
+ original.append("int i = 3;");
+ original.append("{ int j = 4; }");
+
+ StringBuilder expected = new StringBuilder();
+ expected.append("int $t0 = 3; int i = $t0;");
+ expected.append("{ int $t1 = 4; int j = $t1; }");
+
+ assertTransform(original.toString()).into(expected.toString());
+ }
+
+ public void testNestedPre() throws Exception {
+ StringBuilder original = new StringBuilder();
+ original.append("{ int i = 3; }");
+ original.append("int j = 4;");
+
+ StringBuilder expected = new StringBuilder();
+ expected.append("{ int $t0 = 3; int i = $t0; }");
+ expected.append("int $t1 = 4; int j = $t1;");
+
+ assertTransform(original.toString()).into(expected.toString());
+ }
+
+ public void testReuse() throws Exception {
+ StringBuilder original = new StringBuilder();
+ original.append("{ int i = 3; }");
+ original.append("{ int j = 4; }");
+
+ StringBuilder expected = new StringBuilder();
+ expected.append("{ int $t0 = 3; int i = $t0; }");
+ expected.append("{ int $t0 = 4; int j = $t0; }");
+
+ assertTransform(original.toString()).into(expected.toString());
+ }
+
+ public void testReuseTwice() throws Exception {
+ StringBuilder original = new StringBuilder();
+ original.append("{ int i = 3; int j = 4; }");
+ original.append("{ int i = 3; int j = 4; }");
+
+ StringBuilder expected = new StringBuilder();
+ expected.append("{ int $t0 = 3; int i = $t0; int $t1 = 4; int j = $t1;
}");
+ expected.append("{ int $t0 = 3; int i = $t0; int $t1 = 4; int j = $t1;
}");
+
+ assertTransform(original.toString()).into(expected.toString());
+ }
+
+ public void testVeryComplex() throws Exception {
+ StringBuilder original = new StringBuilder();
+ original.append("int a = 0;");
+ original.append("{ int i = 3; int j = 4; }");
+ original.append("int b = 1;");
+ original.append("{ int i = 3; int j = 4; }");
+ original.append("int c = 2;");
+
+ StringBuilder expected = new StringBuilder();
+ expected.append("int $t0 = 0; int a = $t0;");
+ expected.append("{ int $t1 = 3; int i = $t1; int $t2 = 4; int j = $t2;
}");
+ expected.append("int $t3 = 1; int b = $t3;");
+ expected.append("{ int $t1 = 3; int i = $t1; int $t2 = 4; int j = $t2;
}");
+ expected.append("int $t4 = 2; int c = $t4;");
+
+ assertTransform(original.toString()).into(expected.toString());
+ }
+
+ private Result assertTransform(String codeSnippet)
+ throws UnableToCompleteException {
+ JProgram program = compileSnippet("void", codeSnippet);
+ JMethod mainMethod = findMainMethod(program);
+ new AlwaysReplacer().accept(mainMethod);
+ return new Result("void", codeSnippet,
mainMethod.getBody().toSource());
+ }
+}
=======================================
---
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
Fri Apr 2 14:35:01 2010
+++
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
Mon Apr 26 10:59:53 2010
@@ -26,25 +26,16 @@
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JLocalRef;
import com.google.gwt.dev.jjs.ast.JLongLiteral;
-import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
-import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JThisRef;
-import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JUnaryOperator;
import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
-import java.util.ArrayList;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Stack;
-
/**
* <p>
* Replace problematic compound assignments with a sequence of simpler
@@ -75,7 +66,80 @@
/**
* Breaks apart certain complex assignments.
*/
- private class BreakupAssignOpsVisitor extends JModVisitor {
+ private class BreakupAssignOpsVisitor extends TempLocalVisitor {
+
+ /**
+ * Replaces side effects in lvalue.
+ */
+ private class ReplaceSideEffectsInLvalue extends JModVisitor {
+
+ private final JMultiExpression multi;
+
+ ReplaceSideEffectsInLvalue(JMultiExpression multi) {
+ this.multi = multi;
+ }
+
+ public JMultiExpression getMultiExpr() {
+ return multi;
+ }
+
+ @Override
+ public boolean visit(JArrayRef x, Context ctx) {
+ JExpression newInstance = possiblyReplace(x.getInstance());
+ JExpression newIndexExpr = possiblyReplace(x.getIndexExpr());
+ if (newInstance != x.getInstance() || newIndexExpr !=
x.getIndexExpr()) {
+ JArrayRef newExpr = new JArrayRef(x.getSourceInfo(), newInstance,
+ newIndexExpr);
+ ctx.replaceMe(newExpr);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(JFieldRef x, Context ctx) {
+ if (x.getInstance() != null) {
+ JExpression newInstance = possiblyReplace(x.getInstance());
+ if (newInstance != x.getInstance()) {
+ JFieldRef newExpr = new JFieldRef(x.getSourceInfo(),
newInstance,
+ x.getField(), x.getEnclosingType());
+ ctx.replaceMe(newExpr);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(JLocalRef x, Context ctx) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(JParameterRef x, Context ctx) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(JThisRef x, Context ctx) {
+ return false;
+ }
+
+ private JExpression possiblyReplace(JExpression x) {
+ if (!x.hasSideEffects()) {
+ return x;
+ }
+
+ // Create a temp local
+ JLocal tempLocal = createTempLocal(x.getSourceInfo(), x.getType());
+
+ // Create an assignment for this temp and add it to multi.
+ JLocalRef tempRef = new JLocalRef(x.getSourceInfo(), tempLocal);
+ JBinaryOperation asg = new JBinaryOperation(x.getSourceInfo(),
+ x.getType(), JBinaryOperator.ASG, tempRef, x);
+ multi.exprs.add(asg);
+ // Update me with the temp
+ return cloner.cloneExpression(tempRef);
+ }
+ }
@Override
public void endVisit(JBinaryOperation x, Context ctx) {
@@ -93,11 +157,9 @@
* expressions that could have side effects with temporaries, so
that they
* are only run once.
*/
- enterTempUsageScope();
ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(
new JMultiExpression(x.getSourceInfo()));
JExpression newLhs = replacer.accept(x.getLhs());
- exitTempUsageScope();
JBinaryOperation operation = new JBinaryOperation(x.getSourceInfo(),
newLhs.getType(), op.getNonAssignmentOf(), newLhs, x.getRhs());
@@ -116,12 +178,6 @@
ctx.replaceMe(multiExpr);
}
}
-
- @Override
- public void endVisit(JMethodBody x, Context ctx) {
- clearLocals();
- currentMethodBody = null;
- }
@Override
public void endVisit(JPostfixOperation x, Context ctx) {
@@ -137,7 +193,6 @@
// (t = x, x += 1, t)
// First, replace the arg with a non-side-effect causing one.
- enterTempUsageScope();
JMultiExpression multi = new JMultiExpression(x.getSourceInfo());
ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(
multi);
@@ -146,7 +201,8 @@
JExpression expressionReturn = expressionToReturn(newArg);
// Now generate the appropriate expressions.
- JLocal tempLocal = getTempLocal(expressionReturn.getType());
+ JLocal tempLocal = createTempLocal(x.getSourceInfo(),
+ expressionReturn.getType());
// t = x
JLocalRef tempRef = new JLocalRef(x.getSourceInfo(), tempLocal);
@@ -164,7 +220,6 @@
multi.exprs.add(tempRef);
ctx.replaceMe(multi);
- exitTempUsageScope();
}
@Override
@@ -184,13 +239,6 @@
// Visit the result to break it up even more.
ctx.replaceMe(accept(asg));
}
-
- @Override
- public boolean visit(JMethodBody x, Context ctx) {
- currentMethodBody = x;
- clearLocals();
- return true;
- }
private JBinaryOperation createAsgOpFromUnary(JExpression arg,
JUnaryOperator op) {
@@ -221,165 +269,7 @@
}
}
- /**
- * Replaces side effects in lvalue.
- */
- private class ReplaceSideEffectsInLvalue extends JModVisitor {
-
- private final JMultiExpression multi;
-
- ReplaceSideEffectsInLvalue(JMultiExpression multi) {
- this.multi = multi;
- }
-
- public JMultiExpression getMultiExpr() {
- return multi;
- }
-
- @Override
- public boolean visit(JArrayRef x, Context ctx) {
- JExpression newInstance = possiblyReplace(x.getInstance());
- JExpression newIndexExpr = possiblyReplace(x.getIndexExpr());
- if (newInstance != x.getInstance() || newIndexExpr !=
x.getIndexExpr()) {
- JArrayRef newExpr = new JArrayRef(x.getSourceInfo(), newInstance,
- newIndexExpr);
- ctx.replaceMe(newExpr);
- }
- return false;
- }
-
- @Override
- public boolean visit(JFieldRef x, Context ctx) {
- if (x.getInstance() != null) {
- JExpression newInstance = possiblyReplace(x.getInstance());
- if (newInstance != x.getInstance()) {
- JFieldRef newExpr = new JFieldRef(x.getSourceInfo(), newInstance,
- x.getField(), x.getEnclosingType());
- ctx.replaceMe(newExpr);
- }
- }
- return false;
- }
-
- @Override
- public boolean visit(JLocalRef x, Context ctx) {
- return false;
- }
-
- @Override
- public boolean visit(JParameterRef x, Context ctx) {
- return false;
- }
-
- @Override
- public boolean visit(JThisRef x, Context ctx) {
- return false;
- }
-
- private JExpression possiblyReplace(JExpression x) {
- if (!x.hasSideEffects()) {
- return x;
- }
-
- // Create a temp local
- JLocal tempLocal = getTempLocal(x.getType());
-
- // Create an assignment for this temp and add it to multi.
- JLocalRef tempRef = new JLocalRef(x.getSourceInfo(), tempLocal);
- JBinaryOperation asg = new JBinaryOperation(x.getSourceInfo(),
x.getType(),
- JBinaryOperator.ASG, tempRef, x);
- multi.exprs.add(asg);
- // Update me with the temp
- return cloner.cloneExpression(tempRef);
- }
- }
-
- /**
- * For some particular type, tracks all usage of temporary local
variables of
- * that type within the current method.
- */
- private static class TempLocalTracker {
- /**
- * Temps previously created in nested usage scopes that are now
available
- * for reuse by subsequent code.
- */
- private List<JLocal> reusable = new ArrayList<JLocal>();
-
- /**
- * Temps in use in active scopes. They cannot currently be reused;
however,
- * any time a scope is exited, the set of locals at the top of stack is
- * popped off and all contained locals become reusable. The top of
stack
- * correlates to the current scope.
- */
- private Stack<List<JLocal>> usageStack = new Stack<List<JLocal>>();
-
- public TempLocalTracker() {
- /*
- * Due to the lazy-creation nature, we must assume upon creation that
- * we're already in a valid scope (and thus create the initial empty
scope
- * ourselves).
- */
- enterScope();
- }
-
- public void enterScope() {
- usageStack.push(new ArrayList<JLocal>());
- }
-
- public void exitScope() {
- /*
- * Due to the lazy-creation nature, the program flow might be several
- * levels deep already when this object is created. Since we don't
know
- * how many exitScope() calls to accept, we must be empty-tolerant.
In
- * other words, this isn't naively defensive programming. :)
- */
- if (!usageStack.isEmpty()) {
- List<JLocal> freed = usageStack.pop();
- reusable.addAll(freed);
- }
- }
-
- public JLocal tryGetReusableLocal() {
- if (!reusable.isEmpty()) {
- return reusable.remove(reusable.size() - 1);
- }
- return null;
- }
-
- public void useLocal(JLocal local) {
- usageStack.peek().add(local);
- }
- }
-
- private final CloneExpressionVisitor cloner;
-
- private JMethodBody currentMethodBody;
-
- /**
- * Counter to generate a unique name for each temporary within the
current
- * method.
- */
- private int localCounter;
-
- /**
- * Map of type onto lazily-created local tracker.
- */
- private Map<JType, TempLocalTracker> localTrackers = new
IdentityHashMap<JType, TempLocalTracker>();
-
- /**
- * If <code>true</code>, reuse temps. The pre-optimization subclass does
- * not reuse temps because doing so can defeat optimizations because
different
- * uses impact each other and we do nothing to disambiguate usage. After
- * optimizations, it makes sense to reuse temps to reduce code size and
memory
- * consumption of the output.
- */
- private final boolean reuseTemps;
-
- protected CompoundAssignmentNormalizer(boolean reuseTemps) {
- this.reuseTemps = reuseTemps;
- cloner = new CloneExpressionVisitor();
- clearLocals();
- }
+ private final CloneExpressionVisitor cloner = new
CloneExpressionVisitor();
public void accept(JNode node) {
BreakupAssignOpsVisitor breaker = new BreakupAssignOpsVisitor();
@@ -394,56 +284,10 @@
protected JExpression expressionToReturn(JExpression lhs) {
return lhs;
}
-
- protected abstract String getTempPrefix();
protected abstract boolean shouldBreakUp(JBinaryOperation x);
protected abstract boolean shouldBreakUp(JPostfixOperation x);
protected abstract boolean shouldBreakUp(JPrefixOperation x);
-
- private void clearLocals() {
- localCounter = 0;
- localTrackers.clear();
- }
-
- private void enterTempUsageScope() {
- for (TempLocalTracker tracker : localTrackers.values()) {
- tracker.enterScope();
- }
- }
-
- private void exitTempUsageScope() {
- for (TempLocalTracker tracker : localTrackers.values()) {
- tracker.exitScope();
- }
- }
-
- /**
- * Allocate a temporary local variable.
- */
- private JLocal getTempLocal(JType type) {
- TempLocalTracker tracker = localTrackers.get(type);
- if (tracker == null) {
- tracker = new TempLocalTracker();
- localTrackers.put(type, tracker);
- }
-
- JLocal temp = null;
- if (reuseTemps) {
- /*
- * If the return is non-null, we now "own" the returned JLocal; it's
- * important to call tracker.useLocal() on the returned value
(below).
- */
- temp = tracker.tryGetReusableLocal();
- }
-
- if (temp == null) {
- temp = JProgram.createLocal(currentMethodBody.getSourceInfo(),
- getTempPrefix() + localCounter++, type, false,
currentMethodBody);
- }
- tracker.useLocal(temp);
- return temp;
- }
-}
+}
=======================================
---
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/FixAssignmentToUnbox.java
Fri Apr 2 14:35:01 2010
+++
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/FixAssignmentToUnbox.java
Mon Apr 26 10:59:53 2010
@@ -52,7 +52,6 @@
private final AutoboxUtils autoboxUtils;
protected CompoundAssignmentToUnboxNormalizer(JProgram program) {
- super(false);
autoboxUtils = new AutoboxUtils(program);
}
@@ -68,11 +67,6 @@
}
return lhs;
}
-
- @Override
- protected String getTempPrefix() {
- return "$tunbox";
- }
@Override
protected boolean shouldBreakUp(JBinaryOperation x) {
=======================================
---
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java
Fri Apr 2 14:35:01 2010
+++
/branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java
Mon Apr 26 10:59:53 2010
@@ -33,12 +33,6 @@
}
protected PostOptimizationCompoundAssignmentNormalizer() {
- super(true);
- }
-
- @Override
- protected String getTempPrefix() {
- return "$t";
}
@Override
=======================================
---
/branches/2.1/dev/core/test/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizerTest.java
Fri Feb 12 08:53:54 2010
+++
/branches/2.1/dev/core/test/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizerTest.java
Mon Apr 26 10:59:53 2010
@@ -1,5 +1,18 @@
-// Copyright 2009 Google Inc. All Rights Reserved.
-
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.core.ext.UnableToCompleteException;
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors