blob: 408afafd207d2070269467475e7c59cd305c429f [file] [log] [blame]
/*
* Copyright 2008 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.ast.Context;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JConstructor;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JParameter;
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.JProgram;
import com.google.gwt.dev.jjs.ast.JValueLiteral;
import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Detects when same literal is passed as parameter value, and uses literal
* instead of parameter. The unused parameter will be removed by other analyses.
*/
// TODO: this optimization can mistakenly act on methods such as LongLib.fromInt
// since only one call is seen in LongLib itself.
public class SameParameterValueOptimizer {
/**
* Fill parameterValues map.
*/
private class AnalysisVisitor extends JVisitor {
@Override
public void endVisit(JBinaryOperation x, Context ctx) {
if (x.isAssignment() && x.getLhs() instanceof JParameterRef) {
parameterValues.put(((JParameterRef) x.getLhs()).getParameter(), null);
}
}
@Override
public void endVisit(JMethodCall x, Context ctx) {
JMethod method = x.getTarget();
if (x.canBePolymorphic() || rescuedMethods.contains(method)) {
return;
}
List<JExpression> args = x.getArgs();
List<JParameter> params = method.getParams();
for (int i = 0; i < args.size() && i < params.size(); i++) {
JParameter param = params.get(i);
JExpression arg = args.get(i);
if (!(arg instanceof JValueLiteral)) {
parameterValues.put(param, null);
continue;
}
if (!parameterValues.containsKey(param)) {
parameterValues.put(param, (JValueLiteral) arg);
continue;
}
JValueLiteral commonParamValue = parameterValues.get(param);
if (commonParamValue == null) {
continue;
}
if (!equalLiterals(commonParamValue, (JValueLiteral) arg)) {
parameterValues.put(param, null);
}
}
}
@Override
public void endVisit(JPostfixOperation x, Context ctx) {
if (x.getArg() instanceof JParameterRef) {
parameterValues.put(((JParameterRef) x.getArg()).getParameter(), null);
}
}
@Override
public void endVisit(JPrefixOperation x, Context ctx) {
if (x.getArg() instanceof JParameterRef) {
parameterValues.put(((JParameterRef) x.getArg()).getParameter(), null);
}
}
@Override
public void endVisit(JsniMethodBody x, Context ctx) {
for (JsniMethodRef methodRef : x.getJsniMethodRefs()) {
rescuedMethods.add(methodRef.getTarget());
}
}
@Override
public boolean visit(JConstructor x, Context ctx) {
// Cannot be overridden or staticified.
return true;
}
@Override
public boolean visit(JMethod x, Context ctx) {
Set<JMethod> overrides = program.typeOracle.getAllOverrides(x);
if (!overrides.isEmpty()) {
for (JMethod m : overrides) {
rescuedMethods.add(m);
}
rescuedMethods.add(x);
}
return true;
}
private boolean equalLiterals(JValueLiteral l1, JValueLiteral l2) {
Object v1 = l1.getValueObj();
Object v2 = l2.getValueObj();
if (v1 == v2) {
return true;
}
if (v1 == null || v2 == null) {
return false;
}
return v1.equals(v2);
}
}
/**
* Substitute all parameter references with expression.
*/
private class SubstituteParameterVisitor extends JModVisitor {
private final CloneExpressionVisitor cloner;
private final JExpression expression;
private final JParameter parameter;
public SubstituteParameterVisitor(JParameter parameter, JExpression expression) {
this.parameter = parameter;
this.expression = expression;
cloner = new CloneExpressionVisitor();
}
@Override
public void endVisit(JParameterRef x, Context ctx) {
if (x.getParameter() == parameter) {
ctx.replaceMe(cloner.cloneExpression(expression));
}
}
}
private static final String NAME = SameParameterValueOptimizer.class.getSimpleName();
public static OptimizerStats exec(JProgram program) {
Event optimizeEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE, "optimizer", NAME);
OptimizerStats stats = new SameParameterValueOptimizer(program).execImpl(program);
optimizeEvent.end("didChange", "" + stats.didChange());
return stats;
}
/**
* Parameter values.
*
* If doesn't contain a parameter, then its value is unknown. If contains
* parameter, and value is null - the parameter's value is not the same across
* all calls. If value is not null - the parameter's value is the same across
* all calls.
*/
private final Map<JParameter, JValueLiteral> parameterValues =
new IdentityHashMap<JParameter, JValueLiteral>();
private final JProgram program;
/**
* These methods should not be tried to optimized due to their polymorphic
* nature.
*
* TODO: support polymorphic calls properly.
*/
private final Set<JMethod> rescuedMethods = new HashSet<JMethod>();
private final Simplifier simplifier;
private SameParameterValueOptimizer(JProgram program) {
this.program = program;
simplifier = new Simplifier(program);
}
private OptimizerStats execImpl(JNode node) {
OptimizerStats stats = new OptimizerStats(NAME);
AnalysisVisitor analysisVisitor = new AnalysisVisitor();
analysisVisitor.accept(node);
for (JParameter parameter : parameterValues.keySet()) {
if (rescuedMethods.contains(parameter.getEnclosingMethod())) {
continue;
}
JValueLiteral valueLiteral = parameterValues.get(parameter);
if (valueLiteral != null) {
SubstituteParameterVisitor substituteParameterVisitor =
new SubstituteParameterVisitor(parameter, simplifier.cast(parameter.getType(),
valueLiteral));
substituteParameterVisitor.accept(parameter.getEnclosingMethod());
stats.recordModified(substituteParameterVisitor.getNumMods());
}
}
return stats;
}
}