All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.dev.jjs.impl.SameParameterValueOptimizer Maven / Gradle / Ivy

/*
 * 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)
          /*
           * Don't optimize calls visible to JS callers, because you don't know what
           * parameter values they'll supply.
           */
          || program.isJsTypePrototype(method.getEnclosingType())
          || program.typeOracle.isJsTypeMethod(method)
          || program.typeOracle.isExportedMethod(method)) {
        return;
      }

      List args = x.getArgs();
      List 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 overrides = program.typeOracle.getAllOverriddenMethods(x);
      if (!overrides.isEmpty() || program.typeOracle.isJsTypeMethod(x)
          || program.typeOracle.isExportedMethod(x)) {
        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 parameterValues =
      new IdentityHashMap();
  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 rescuedMethods = new HashSet();

  private SameParameterValueOptimizer(JProgram program) {
    this.program = 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;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy