/** Performs peephole optimizations on a program's live methods. */
  private static void peephole(final BloatContext context) {

    final Set liveMethods = new TreeSet(new MemberRefComparator());
    final CallGraph cg = context.getCallGraph();
    liveMethods.addAll(cg.liveMethods());

    // Perform peephole optimizations. We do this separately because
    // some peephole optimizations do things to the stack that
    // inlining doesn't like. For instance, a peephole optimizations
    // might make it so that a method has a non-empty stack upon
    // return. Inlining will barf at the sight of this.
    BloatBenchmark.tr("Performing peephole optimizations");

    final Iterator iter = liveMethods.iterator();
    while (BloatBenchmark.PEEPHOLE && iter.hasNext()) {
      try {
        final MethodEditor live = context.editMethod((MemberRef) iter.next());
        Peephole.transform(live);
        context.commit(live.methodInfo());
        context.release(live.methodInfo());

      } catch (final NoSuchMethodException ex314) {
        BloatBenchmark.err.println("** Could not find method " + ex314.getMessage());
        ex314.printStackTrace(System.err);
        System.exit(1);
      }
    }
  }
  /** Creates a <tt>BloatContext</tt> that loads classes from a given CLASSPATH. */
  static BloatContext makeContext(final String classpath, final String outputDirName) {
    final ClassFileLoader loader = new ClassFileLoader();
    if (classpath != null) {
      loader.prependClassPath(classpath);
    }

    // if(TRACE) {
    // loader.setVerbose(true);
    // }

    BloatBenchmark.tr("  Creating a BloatContext for CLASSPATH: " + loader.getClassPath());

    if (outputDirName != null) {
      loader.setOutputDir(new File(outputDirName));
    }
    final BloatContext context = new CachingBloatContext(loader, BloatBenchmark.CLASSES, true);

    // Always ignore the sun packages and the opj stuff for
    // interprocedural stuff
    if (!BloatBenchmark.SUN) {
      context.addIgnorePackage("sun");
    }

    context.addIgnorePackage("java.lang.ref");
    context.addIgnorePackage("org.opj.system");

    if (BloatBenchmark.USE1_1) {
      // Toba can't deal with java.lang.Character
      context.addIgnoreClass(Type.getType("Ljava/lang/Character;"));
    }

    return (context);
  }
  /**
   * Returns the live methods of a program whose root methods are the <tt>main</tt> method of a set
   * of classes.
   *
   * @param classes Names of classes containing root methods
   * @param context Repository for accessing BLOAT stuff
   * @return The <tt>MemberRef</tt>s of the live methods
   */
  private static Collection liveMethods(final Collection classes, final BloatContext context) {

    // Determine the roots of the call graph
    final Set roots = new HashSet();
    Iterator iter = classes.iterator();
    while (iter.hasNext()) {
      final String className = (String) iter.next();
      try {
        final ClassEditor ce = context.editClass(className);
        final MethodInfo[] methods = ce.methods();

        for (int i = 0; i < methods.length; i++) {
          final MethodEditor me = context.editMethod(methods[i]);

          if (!me.name().equals("main")) {
            continue;
          }

          BloatBenchmark.tr("  Root " + ce.name() + "." + me.name() + me.type());
          roots.add(me.memberRef());
        }

      } catch (final ClassNotFoundException ex1) {
        BloatBenchmark.err.println("** Could not find class: " + ex1.getMessage());
        System.exit(1);
      }
    }

    if (roots.isEmpty()) {
      BloatBenchmark.err.print("** No main method found in classes: ");
      iter = classes.iterator();
      while (iter.hasNext()) {
        final String name = (String) iter.next();
        BloatBenchmark.err.print(name);
        if (iter.hasNext()) {
          BloatBenchmark.err.print(", ");
        }
      }
      BloatBenchmark.err.println("");
    }

    context.setRootMethods(roots);
    final CallGraph cg = context.getCallGraph();

    final Set liveMethods = new TreeSet(new MemberRefComparator());
    liveMethods.addAll(cg.liveMethods());

    return (liveMethods);
  }
  /** Specializes the live methods in a program. */
  private static void specialize(final BloatContext context) {

    final CallGraph cg = context.getCallGraph();

    final Set liveMethods = new TreeSet(new MemberRefComparator());
    liveMethods.addAll(cg.liveMethods());

    // Specialize all possible methods
    final InlineStats stats = context.getInlineStats();

    if (BloatBenchmark.statsFile != null) {
      Specialize.STATS = true;
      stats.setConfigName("BloatBenchmark");
    }

    if (BloatBenchmark.MORPH != -1) {
      Specialize.MAX_MORPH = BloatBenchmark.MORPH;
    }
    final Specialize spec = new Specialize(context);

    if (Specialize.STATS) {
      stats.noteLiveMethods(liveMethods.size());
      stats.noteLiveClasses(cg.liveClasses().size());
    }

    BloatBenchmark.tr("Specializing live methods");
    final Iterator iter = liveMethods.iterator();

    for (int count = 0; iter.hasNext(); count++) {
      try {
        final MethodEditor live = context.editMethod((MemberRef) iter.next());

        if (context.ignoreMethod(live.memberRef())) {
          // Don't display ignored methods, it's misleading.
          continue;
        }

        BloatBenchmark.tr(
            "  " + count + ") " + live.declaringClass().name() + "." + live.name() + live.type());

        spec.specialize(live);

      } catch (final NoSuchMethodException ex2) {
        BloatBenchmark.err.println("** Could not find method " + ex2.getMessage());
        System.exit(1);
      }
    }
  }
  /** Inlines calls to static methods in the live methods of a given program. */
  private static void inline(final BloatContext context) {

    final Set liveMethods = new TreeSet(new MemberRefComparator());
    final CallGraph cg = context.getCallGraph();
    liveMethods.addAll(cg.liveMethods());

    BloatBenchmark.tr("Inlining " + liveMethods.size() + " live methods");

    if (BloatBenchmark.CALLEE_SIZE != -1) {
      Inline.CALLEE_SIZE = BloatBenchmark.CALLEE_SIZE;
    }

    final Iterator iter = liveMethods.iterator();
    for (int count = 0; BloatBenchmark.INLINE && iter.hasNext(); count++) {
      try {
        final MethodEditor live = context.editMethod((MemberRef) iter.next());

        if (context.ignoreMethod(live.memberRef())) {
          // Don't display ignored methods, it's misleading.
          continue;
        }

        BloatBenchmark.tr(
            "  " + count + ") " + live.declaringClass().name() + "." + live.name() + live.type());

        final Inline inline = new Inline(context, BloatBenchmark.SIZE);
        inline.setMaxCallDepth(BloatBenchmark.DEPTH);
        inline.inline(live);

        // Commit here in an attempt to conserve memory
        context.commit(live.methodInfo());
        context.release(live.methodInfo());

      } catch (final NoSuchMethodException ex3) {
        BloatBenchmark.err.println("** Could not find method " + ex3.getMessage());
        System.exit(1);
      }
    }
  }
Exemple #6
0
  /** Runs BLOAT on a method. */
  public static void bloatMethod(final MethodEditor m, final BloatContext context) {
    try {
      if (Main.COMPACT_ARRAY_INIT) {
        // Compact the initialization of arrays of the basic types by
        // putting the values of the array into a string in the constant
        // pool. The initialization code is replaced with a loop that
        // loads the array from the string in the constant pool.

        if (Main.TRACE) {
          System.out.println("  Compacting Arrays: " + Main.dateFormat.format(new Date()));
        }

        CompactArrayInitializer.transform(m);

        if (Main.DEBUG) {
          System.out.println("---------- After compaction:");
          m.print(System.out);
          System.out.println("---------- end print");
        }
      }

      FlowGraph cfg; // The control flow graph for a method

      if (Main.TRACE) {
        System.out.println("  Constructing CFG: " + Main.dateFormat.format(new Date()));
      }

      try {
        // Construct the control flow graph for method m
        cfg = new FlowGraph(m);
      } catch (final ClassFormatException ex) {
        System.err.println(ex.getMessage());
        context.release(m.methodInfo());
        return;
      }

      // We separate out initialization since before this the FlowGraph
      // more exactly represents the input program.
      cfg.initialize();

      if (Main.TRACE) {
        System.out.println("  Transforming to SSA: " + Main.dateFormat.format(new Date()));
      }

      SSA.transform(cfg);

      if (FlowGraph.DEBUG) {
        System.out.println("---------- After SSA:");
        cfg.print(System.out);
        System.out.println("---------- end print");
      }

      if (Main.DEBUG) {
        cfg.visit(new VerifyCFG(false));
      }

      if (!Tree.USE_STACK) {
        // Do copy propagation and value numbering first to get rid of
        // all the extra copies inserted for dups. If they're left in,
        // it really slows down value numbering.
        if (Main.PROP) {
          if (Main.DEBUG) {
            System.out.println("-----Before Copy Propagation-----");
          }

          if (Main.TRACE) {
            System.out.println("  Copy propagation: " + Main.dateFormat.format(new Date()));
          }

          final ExprPropagation copy = new ExprPropagation(cfg);
          copy.transform();

          if (Main.DEBUG) {
            cfg.visit(new VerifyCFG(false));
          }

          if (Main.DEBUG) {
            System.out.println("------After Copy Propagation-----");
            cfg.print(System.out);
          }
        }
      }

      DeadCodeElimination dce = null;

      if (Main.DCE) {

        if (Main.TRACE) {
          System.out.println("  Dead Code Elimination: " + Main.dateFormat.format(new Date()));
        }

        if (Main.DEBUG) {
          System.out.println("---Before Dead Code Elimination--");
        }

        dce = new DeadCodeElimination(cfg);
        dce.transform();

        if (Main.DEBUG) {
          cfg.visit(new VerifyCFG(false));
        }

        if (Main.DEBUG) {
          System.out.println("---After Dead Code Elimination---");
          cfg.print(System.out);
        }
      }

      if (Main.INFER) {

        if (Main.DEBUG) {
          System.out.println("---------Doing type inference--------");
        }

        if (Main.TRACE) {
          System.out.println("  Type Inferencing: " + Main.dateFormat.format(new Date()));
        }

        TypeInference.transform(cfg, context.getHierarchy());
      }

      if (Main.NUMBER) {

        if (Main.TRACE) {
          System.out.println("  Value Numbering: " + Main.dateFormat.format(new Date()));
        }

        if (Main.DEBUG) {
          System.out.println("--------Doing value numbering--------");
        }

        (new ValueNumbering()).transform(cfg);
      }

      if (Main.FOLD) {
        if (Main.DEBUG) {
          System.out.println("--------Before Value Folding---------");
        }

        if (Main.TRACE) {
          System.out.println("  Value Folding: " + Main.dateFormat.format(new Date()));
        }

        (new ValueFolding()).transform(cfg);

        if (Main.DEBUG) {
          cfg.visit(new VerifyCFG());
        }

        if (Main.DEBUG) {
          System.out.println("---------After Value Folding---------");
          cfg.print(System.out);
        }
      }

      if (Main.PRE) {
        if (Main.DEBUG) {
          System.out.println("-------------Before SSAPRE-----------");
        }

        if (Main.TRACE) {
          System.out.println("  SSAPRE: " + Main.dateFormat.format(new Date()));
        }

        final SSAPRE pre = new SSAPRE(cfg, context);
        pre.transform();

        if (Main.DEBUG) {
          cfg.visit(new VerifyCFG());
        }

        if (Main.DEBUG) {
          System.out.println("-------------After SSAPRE------------");
          cfg.print(System.out);
        }
      }

      if (Main.FOLD) {
        if (Main.DEBUG) {
          System.out.println("--------Before Value Folding---------");
        }

        if (Main.TRACE) {
          System.out.println("  Value Folding: " + Main.dateFormat.format(new Date()));
        }

        (new ValueFolding()).transform(cfg);

        if (Main.DEBUG) {
          cfg.visit(new VerifyCFG());
        }

        if (Main.DEBUG) {
          System.out.println("---------After Value Folding---------");
          cfg.print(System.out);
        }
      }

      if (Main.PROP) {
        if (Main.DEBUG) {
          System.out.println("-------Before Copy Propagation-------");
        }

        if (Main.TRACE) {
          System.out.println("  Copy Propagation " + Main.dateFormat.format(new Date()));
        }

        final ExprPropagation copy = new ExprPropagation(cfg);
        copy.transform();

        if (Main.DEBUG) {
          cfg.visit(new VerifyCFG());
        }

        if (Main.DEBUG) {
          System.out.println("--------After Copy Propagation-------");
          cfg.print(System.out);
        }
      }

      // make sure we've done at least one thing since the last DCE
      if (Main.DCE && (Main.INFER || Main.NUMBER || Main.FOLD || Main.PRE || Main.PROP)) {
        if (Main.DEBUG) {
          System.out.println("-----Before Dead Code Elimination----");
        }

        if (Main.TRACE) {
          System.out.println("  Dead Code Elimination: " + Main.dateFormat.format(new Date()));
        }

        dce = new DeadCodeElimination(cfg);
        dce.transform();

        if (Main.DEBUG) {
          cfg.visit(new VerifyCFG());
        }

        if (Main.DEBUG) {
          System.out.println("-----After Dead Code Elimination-----");
          cfg.print(System.out);
        }
      }

      if (Main.PERSIST) {
        (new PersistentCheckElimination()).transform(cfg);
      }

      if (Main.DIVA) {
        if (Main.DEBUG) {
          System.out.println("-----Before DIVA------");
        }

        if (Main.TRACE) {
          System.out.println("  DIVA: " + Main.dateFormat.format(new Date()));
        }

        (new InductionVarAnalyzer()).transform(cfg);

        if (Main.DEBUG) {
          System.out.println("-----After DIVA-----");
          cfg.print(System.out);
        }
      }

      /*
       * if (STACK_ALLOC) { if (DEBUG) {
       * System.out.println("------------Before StackPRE----------"); }
       *
       * StackPRE pre = new StackPRE(cfg); pre.transform();
       *
       * if (DEBUG) { cfg.visit(new VerifyCFG()); }
       *
       * if (DEBUG) { System.out.println("------------After
       * StackPRE-----------"); cfg.print(System.out); } }
       */

      // Do the new stack optimization
      if (Main.OPT_STACK_2) {

        if (Main.TRACE) {
          System.out.println("  New stack optimization: " + Main.dateFormat.format(new Date()));
        }

        // generate code without doing liveness or register allocation
        final CodeGenerator codegen = new CodeGenerator(m);
        codegen.replacePhis(cfg);
        m.clearCode2();
        cfg.visit(codegen);
        // do stack optimization on the bytecode

        final StackOpt so = new StackOpt();
        so.transform(m);

        // convert it back to a cfg
        cfg = new FlowGraph(m);
        cfg.initialize();

        // convert it back to SSA
        SSA.transform(cfg);

        // do more dead code elimination (eliminate stores)
        dce = new DeadCodeElimination(cfg);
        dce.transform();
      }

      if (Main.TRACE) {
        System.out.println("  Register allocation: " + Main.dateFormat.format(new Date()));
      }

      if (Main.VERIFY) {
        try {
          cfg.visit(new VerifyCFG());
        } catch (final IllegalArgumentException ee) {
          System.out.println(
              " NOTE: CFG did not verify while "
                  + "bloating "
                  + m.name()
                  + " after all optimizations. Exception: "
                  + ee);
        }
      }

      // We're all done performing optimizations. Let's generate some code
      // and go home.

      // Perform liveness analysis of variables in the method.
      // Assign local variables ("registers") to expression values.
      final Liveness liveness = new Liveness(cfg);
      final RegisterAllocator alloc = new RegisterAllocator(cfg, liveness);

      // Gather information which can be used to optimize use of the stack
      if (CodeGenerator.OPT_STACK) {
        if (Main.TRACE) {
          System.out.println("  Old stack optimization: " + Main.dateFormat.format(new Date()));
        }
        StackOptimizer.optimizeCFG(cfg);
      }

      if (Main.TRACE) {
        System.out.println("  Code Generation: " + Main.dateFormat.format(new Date()));
      }

      // Start the code generation process.
      final CodeGenerator codegen = new CodeGenerator(m);
      codegen.replacePhis(cfg);

      if (Main.DEBUG) {
        System.out.println("After fixing Phis------------------------");
        cfg.print(System.out);
        System.out.println("End print--------------------------------");
      }

      codegen.simplifyControlFlow(cfg);
      codegen.allocReturnAddresses(cfg, alloc);

      if (Main.DEBUG) {
        System.out.println("After removing empty blocks--------------");
        cfg.print(System.out);
        System.out.println("End print--------------------------------");
      }

      // Clear the old contents of the bytecode store and generate new
      // code.
      // Code is generated using a visitor pattern on the CFG.
      m.clearCode();
      cfg.visit(codegen);

      Peephole.transform(m);

      // Commit any changes that have been made to the method
      context.commit(m.methodInfo());

    } catch (final Exception ex99) {
      final String msg =
          "** Exception while optimizing "
              + m.name()
              + m.type()
              + " of class "
              + m.declaringClass().name();
      System.err.println(msg);
      System.err.println(ex99.getMessage());
      ex99.printStackTrace(System.err);
      System.exit(1);
    }
  }
  /**
   * Performs intraprocedural BLOAT on a program's live methods.
   *
   * @param liveMethods Should be alphabetized. This way we can commit a class once we've BLOATed
   *     all of its methods.
   */
  private static void intraBloat(final Collection liveMethods, final BloatContext context) {

    ClassEditor prevClass = null;
    final Iterator iter = liveMethods.iterator();
    for (int count = 0; iter.hasNext(); count++) {
      MethodEditor live = null;
      ClassEditor ce = null; // Hack to make sure commit happens
      try {
        live = context.editMethod((MemberRef) iter.next());
        ce = context.editClass(live.declaringClass().classInfo());

      } catch (final NoSuchMethodException ex3) {
        BloatBenchmark.err.println("** Could not find method " + ex3.getMessage());
        System.exit(1);
      }

      /* So we can skip classes or packages */
      final String name = ce.type().className();
      final String qual = ce.type().qualifier() + "/*";
      boolean skip = false;
      for (int i = 0; i < BloatBenchmark.SKIP.size(); i++) {
        final String pkg = (String) BloatBenchmark.SKIP.get(i);

        if (name.equals(pkg) || qual.equals(pkg)) {
          skip = true;
          break;
        }
      }

      if (context.ignoreMethod(live.memberRef()) || skip) {
        // Don't display ignored methods, it's misleading.
        context.release(live.methodInfo());
        continue;
      }

      final Runtime runtime = Runtime.getRuntime();
      runtime.gc();

      final Date start = new Date();
      BloatBenchmark.tr(
          "  " + count + ") " + live.declaringClass().name() + "." + live.name() + live.type());
      BloatBenchmark.tr("    Start: " + start);

      try {
        EDU.purdue.cs.bloat.optimize.Main.TRACE = BloatBenchmark.TRACE;
        if (!BloatBenchmark.VERIFY) {
          EDU.purdue.cs.bloat.optimize.Main.VERIFY = false;
        }
        EDU.purdue.cs.bloat.optimize.Main.bloatMethod(live, context);

      } catch (final Exception oops) {
        BloatBenchmark.err.println("******************************************");
        BloatBenchmark.err.println(
            "Exception while BLOATing "
                + live.declaringClass().name()
                + "."
                + live.name()
                + live.type());
        BloatBenchmark.err.println(oops.getMessage());
        oops.printStackTrace(System.err);
        BloatBenchmark.err.println("******************************************");
      }

      // Commit here in an attempt to conserve memory
      context.commit(live.methodInfo());
      context.release(live.methodInfo());

      if (prevClass == null) {
        prevClass = ce;

      } else if (!prevClass.equals(ce)) {
        // We've finished BLOATed the methods for prevClass, commit
        // prevClass and move on
        BloatBenchmark.tr(prevClass.type() + " != " + ce.type());
        context.commit(prevClass.classInfo());
        context.release(prevClass.classInfo());
        // context.commitDirty();
        // tr(context.toString());
        prevClass = ce;

      } else {
        context.release(ce.classInfo());
      }

      final Date end = new Date();
      BloatBenchmark.tr("    Ellapsed time: " + (end.getTime() - start.getTime()) + " ms");
    }

    context.commitDirty();
  }
  public static void main(final String[] args) {
    String CLASSPATH = null;
    String outputDirName = null;
    String lookIn = null;

    // Parse the command line
    for (int i = 0; i < args.length; i++) {
      if (args[i].equals("-trace")) {
        BloatBenchmark.TRACE = true;
        PersistentBloatContext.DB_COMMIT = true;

      } else if (args[i].equals("-calleeSize")) {
        if (++i >= args.length) {
          BloatBenchmark.err.println("** No callee size specified");
          BloatBenchmark.usage();
        }

        try {
          BloatBenchmark.CALLEE_SIZE = Integer.parseInt(args[i]);

        } catch (final NumberFormatException ex33) {
          BloatBenchmark.err.println("** Bad number: " + args[i]);
          BloatBenchmark.usage();
        }

      } else if (args[i].startsWith("-classpath")) {
        if (++i >= args.length) {
          BloatBenchmark.err.println("** No classpath specified");
          BloatBenchmark.usage();
        }

        // If there is more than one -classpath append it to the
        // current one. That way the CLASSPATH reflects the order in
        // which the options came on the command line.
        if (CLASSPATH == null) {
          CLASSPATH = args[i];

        } else {
          CLASSPATH += File.pathSeparator + args[i];
        }

      } else if (args[i].equals("-no-stack-alloc")) {
        Main.STACK_ALLOC = false;

      } else if (args[i].equals("-peel-loops")) {
        if (++i >= args.length) {
          BloatBenchmark.usage();
        }

        final String n = args[i];

        if (n.equals("all")) {
          FlowGraph.PEEL_LOOPS_LEVEL = FlowGraph.PEEL_ALL_LOOPS;

        } else {
          try {
            FlowGraph.PEEL_LOOPS_LEVEL = Integer.parseInt(n);

            if (FlowGraph.PEEL_LOOPS_LEVEL < 0) {
              BloatBenchmark.usage();
            }
          } catch (final NumberFormatException ex) {
            BloatBenchmark.usage();
          }
        }
      } else if (args[i].equals("-no-color")) {
        Liveness.UNIQUE = true;

      } else if (args[i].equals("-no-dce")) {
        Main.DCE = false;

      } else if (args[i].equals("-no-prop")) {
        Main.PROP = false;

      } else if (args[i].equals("-no-pre")) {
        Main.PRE = false;

      } else if (args[i].equals("-no-check")) {
        BloatBenchmark.CHECK = false;

      } else if (args[i].equals("-depth")) {
        if (++i >= args.length) {
          BloatBenchmark.err.println("** No depth specified");
          BloatBenchmark.usage();
        }

        try {
          BloatBenchmark.DEPTH = Integer.parseInt(args[i]);

        } catch (final NumberFormatException ex33) {
          BloatBenchmark.err.println("** Bad number: " + args[i]);
          BloatBenchmark.usage();
        }

      } else if (args[i].equals("-inline")) {
        // Inline calls to static methods
        BloatBenchmark.INLINE = true;

      } else if (args[i].equals("-intra")) {
        BloatBenchmark.INTRA = true;

      } else if (args[i].equals("-lookIn")) {
        if (++i >= args.length) {
          BloatBenchmark.err.println("** No directory specified");
          BloatBenchmark.usage();
        }

        if (lookIn != null) {
          lookIn += File.pathSeparator + args[i];

        } else {
          lookIn = args[i];
        }

      } else if (args[i].equals("-morph")) {
        if (++i >= args.length) {
          BloatBenchmark.err.println("** No morphosity specified");
          BloatBenchmark.usage();
        }

        try {
          BloatBenchmark.MORPH = Integer.parseInt(args[i]);

        } catch (final NumberFormatException ex33) {
          BloatBenchmark.err.println("** Bad number: " + args[i]);
          BloatBenchmark.usage();
        }

      } else if (args[i].equals("-noinline")) {
        // Don't perform inlining, just specialize
        BloatBenchmark.INLINE = false;

      } else if (args[i].equals("-peephole")) {
        // Perform peephole optimizations when doing interprocedural
        // stuff
        BloatBenchmark.PEEPHOLE = true;

      } else if (args[i].equals("-size")) {
        if (++i >= args.length) {
          BloatBenchmark.err.println("** No size specified");
          BloatBenchmark.usage();
        }

        try {
          BloatBenchmark.SIZE = Integer.parseInt(args[i]);

        } catch (final NumberFormatException ex33) {
          BloatBenchmark.err.println("** Bad number: " + args[i]);
          BloatBenchmark.usage();
        }

      } else if (args[i].equals("-specialize")) {
        // Specialize virtual method call sites
        BloatBenchmark.SPECIALIZE = true;

      } else if (args[i].equals("-stats")) {
        if (++i >= args.length) {
          BloatBenchmark.err.println("** No stats file specified");
          BloatBenchmark.usage();
        }

        BloatBenchmark.statsFile = args[i];

      } else if (args[i].equals("-sun")) {
        // Optimize sun packages
        BloatBenchmark.SUN = true;

      } else if (args[i].equals("-times")) {
        BloatBenchmark.TIMES = true;

        if (++i >= args.length) {
          BloatBenchmark.err.println("** No times file specified");
          BloatBenchmark.usage();
        }

        BloatBenchmark.timesFile = args[i];

      } else if (args[i].equals("-no-verify")) {
        BloatBenchmark.VERIFY = false;

      } else if (args[i].equals("-no-opt-stack")) {
        CodeGenerator.OPT_STACK = false;

      } else if (args[i].equals("-no-stack-vars")) {
        Tree.USE_STACK = false;

      } else if (args[i].equals("-skip")) {
        if (++i >= args.length) {
          BloatBenchmark.usage();
        }

        String pkg = args[i];

        // Account for class file name on command line
        if (pkg.endsWith(".class")) {
          pkg = pkg.substring(0, pkg.lastIndexOf('.'));
        }

        BloatBenchmark.SKIP.add(pkg.replace('.', '/'));

      } else if (args[i].equals("-1.1")) {
        // There are some classes that we don't want to be pre-live.
        // They don't exist in JDK1.1.
        BloatBenchmark.USE1_1 = true;
        CallGraph.USE1_2 = false;

      } else if (args[i].equals("-1.2")) {
        CallGraph.USE1_2 = true;

        if (lookIn != null) {
          lookIn += File.separator + "1.2";
        }

      } else if (args[i].startsWith("-")) {
        BloatBenchmark.err.println("** Unrecognized option: " + args[i]);
        BloatBenchmark.usage();

      } else if (i == args.length - 1) {
        outputDirName = args[i];

      } else {
        BloatBenchmark.CLASSES.add(args[i]);
      }
    }

    if (BloatBenchmark.CLASSES.isEmpty()) {
      BloatBenchmark.err.println("** No classes specified");
      BloatBenchmark.usage();
    }

    if (outputDirName == null) {
      BloatBenchmark.err.println("** No output directory specified");
      BloatBenchmark.usage();
    }

    // Make sure the options the user entered make sense
    if (BloatBenchmark.CHECK) {
      BloatBenchmark.checkOptions();
    }

    if (BloatBenchmark.USE1_1) {
      // Don't generate stats for 1.1
      BloatBenchmark.statsFile = null;
    }

    if (lookIn != null) {
      CLASSPATH = lookIn + File.pathSeparator + CLASSPATH;
    }

    final StringBuffer sb = new StringBuffer();
    for (int i = 0; i < args.length; i++) {
      sb.append(args[i] + " ");
    }
    BloatBenchmark.tr("BLOATing with command line: " + sb);

    BloatContext context = null;

    float systemStart = 0.0F;
    float systemDelta = 0.0F;
    float systemEnd = 0.0F;
    float systemTotal = 0.0F;

    float userStart = 0.0F;
    float userDelta = 0.0F;
    float userEnd = 0.0F;
    float userTotal = 0.0F;

    PrintWriter times = null;

    if (BloatBenchmark.TIMES) {
      try {
        times = new PrintWriter(new FileWriter(BloatBenchmark.timesFile), true);

      } catch (final IOException ex) {
        times = new PrintWriter(System.out, true);
      }
    }

    if (BloatBenchmark.INTRA) {
      BloatBenchmark.tr("Intraprocedural BLOAT");

      // First compute the roots of the call graph. Figure out which
      // methods are live.
      context = BloatBenchmark.makeContext(CLASSPATH, null);
      final Collection liveMethods = BloatBenchmark.liveMethods(BloatBenchmark.CLASSES, context);

      // Run intraprocedural BLOAT on the live methods.
      BloatBenchmark.tr(liveMethods.size() + " live methods");
      context = BloatBenchmark.makeContext(CLASSPATH, outputDirName);
      BloatBenchmark.intraBloat(liveMethods, context);

    } else {
      BloatBenchmark.tr("Interprocedural BLOAT");

      if (BloatBenchmark.TIMES) {
        Times.snapshot();
        systemStart = Times.systemTime();
        userStart = Times.userTime();
      }

      // Do the interprocedural BLOATing
      context = BloatBenchmark.makeContext(CLASSPATH, outputDirName);
      BloatBenchmark.liveMethods(BloatBenchmark.CLASSES, context);

      if (BloatBenchmark.TIMES) {
        // Take a measurement
        Times.snapshot();

        systemEnd = Times.systemTime();
        userEnd = Times.userTime();

        systemDelta = systemEnd - systemStart;
        userDelta = userEnd - userStart;

        systemStart = systemEnd;
        userStart = userEnd;

        systemTotal += systemDelta;
        userTotal += userDelta;

        times.println("Call graph construction");
        times.println("  User: "******"  System: " + systemDelta);
      }

      if (BloatBenchmark.SPECIALIZE) {
        BloatBenchmark.specialize(context);
      }

      if (BloatBenchmark.TIMES) {
        // Take a measurement
        Times.snapshot();

        systemEnd = Times.systemTime();
        userEnd = Times.userTime();

        systemDelta = systemEnd - systemStart;
        userDelta = userEnd - userStart;

        systemStart = systemEnd;
        userStart = userEnd;

        systemTotal += systemDelta;
        userTotal += userDelta;

        times.println("Call site specialization");
        times.println("  User: "******"  System: " + systemDelta);
      }

      if (BloatBenchmark.INLINE) {
        BloatBenchmark.inline(context);
      }

      if (BloatBenchmark.TIMES) {
        // Take a measurement
        Times.snapshot();

        systemEnd = Times.systemTime();
        userEnd = Times.userTime();

        systemDelta = systemEnd - systemStart;
        userDelta = userEnd - userStart;

        systemStart = systemEnd;
        userStart = userEnd;

        systemTotal += systemDelta;
        userTotal += userDelta;

        times.println("Method inlining");
        times.println("  User: "******"  System: " + systemDelta);
      }

      if (BloatBenchmark.PEEPHOLE) {
        BloatBenchmark.peephole(context);
      }
    }

    // Commit dirty data
    BloatBenchmark.tr("Committing dirty methods");
    context.commitDirty();

    if (BloatBenchmark.TIMES) {
      // Take a measurement
      Times.snapshot();

      systemEnd = Times.systemTime();
      userEnd = Times.userTime();

      systemDelta = systemEnd - systemStart;
      userDelta = userEnd - userStart;

      systemStart = systemEnd;
      userStart = userEnd;

      systemTotal += systemDelta;
      userTotal += userDelta;

      times.println("Committal");
      times.println("  User: "******"  System: " + systemDelta);
    }

    if (BloatBenchmark.TIMES) {
      times.println("Total");
      times.println("  User: "******"  System: " + systemTotal);
    }

    if (BloatBenchmark.statsFile != null) {
      final InlineStats stats = context.getInlineStats();
      PrintWriter statsOut = null;
      try {
        statsOut = new PrintWriter(new FileWriter(BloatBenchmark.statsFile), true);

      } catch (final IOException ex) {
        statsOut = new PrintWriter(System.out, true);
      }

      stats.printSummary(statsOut);
    }

    BloatBenchmark.tr("Finished");
  }