Mirror of the BLOAT repository
https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
637 lines
16 KiB
637 lines
16 KiB
/**
|
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and
|
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
|
* Research Foundation of Purdue University. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
package EDU.purdue.cs.bloat.diva;
|
|
|
|
import java.io.*;
|
|
import java.util.*;
|
|
|
|
import EDU.purdue.cs.bloat.cfg.*;
|
|
import EDU.purdue.cs.bloat.codegen.*;
|
|
import EDU.purdue.cs.bloat.context.*;
|
|
import EDU.purdue.cs.bloat.editor.*;
|
|
import EDU.purdue.cs.bloat.file.*;
|
|
import EDU.purdue.cs.bloat.reflect.*;
|
|
import EDU.purdue.cs.bloat.ssa.*;
|
|
import EDU.purdue.cs.bloat.tbaa.*;
|
|
import EDU.purdue.cs.bloat.trans.*;
|
|
import EDU.purdue.cs.bloat.tree.*;
|
|
|
|
/**
|
|
* Performs a number of analyses on the methods of some specified classes.
|
|
* However, it does not perform some of the optimizations that optimize.Main
|
|
* does.
|
|
*
|
|
* @see EDU.purdue.cs.bloat.optimize.Main
|
|
*/
|
|
public class Main {
|
|
static boolean DEBUG = false;
|
|
|
|
static boolean VERBOSE = false;
|
|
|
|
static boolean FORCE = false;
|
|
|
|
static boolean CLOSURE = false;
|
|
|
|
static boolean PRE = true;
|
|
|
|
static boolean DCE = true;
|
|
|
|
static boolean PROP = true;
|
|
|
|
static boolean FOLD = true;
|
|
|
|
static boolean STACK_ALLOC = false;
|
|
|
|
static boolean COMPACT_ARRAY_INIT = true;
|
|
|
|
static boolean ANNO = true;
|
|
|
|
static String[] ARGS = null;
|
|
|
|
static List SKIP = new ArrayList();
|
|
|
|
static List ONLY = new ArrayList();
|
|
|
|
static String METHOD = null;
|
|
|
|
static BloatContext context = null;
|
|
|
|
static ClassFileLoader loader = null;
|
|
|
|
public static void main(final String[] args) {
|
|
try {
|
|
Main.loader = new ClassFileLoader();
|
|
|
|
List classes = new ArrayList(args.length);
|
|
boolean gotdir = false;
|
|
|
|
Main.ARGS = args;
|
|
|
|
for (int i = 0; i < args.length; i++) {
|
|
if (args[i].equals("-v") || args[i].equals("-verbose")) {
|
|
Main.VERBOSE = true;
|
|
Main.loader.setVerbose(true);
|
|
} else if (args[i].equals("-debug")) {
|
|
Main.DEBUG = true;
|
|
Main.loader.setVerbose(true);
|
|
ClassFileLoader.DEBUG = true;
|
|
CompactArrayInitializer.DEBUG = true;
|
|
ClassEditor.DEBUG = true;
|
|
FlowGraph.DEBUG = true;
|
|
DominatorTree.DEBUG = true;
|
|
Tree.DEBUG = true;
|
|
CodeGenerator.DEBUG = true;
|
|
Liveness.DEBUG = true;
|
|
SSA.DEBUG = true;
|
|
SSAGraph.DEBUG = true;
|
|
PersistentCheckElimination.DEBUG = true;
|
|
ValueNumbering.DEBUG = true;
|
|
ValueFolding.DEBUG = true;
|
|
ClassHierarchy.DEBUG = true;
|
|
TypeInference.DEBUG = true;
|
|
SSAPRE.DEBUG = true;
|
|
StackPRE.DEBUG = true;
|
|
ExprPropagation.DEBUG = true;
|
|
DeadCodeElimination.DEBUG = true;
|
|
} else if (args[i].equals("-help")) {
|
|
Main.usage();
|
|
} else if (args[i].equals("-noanno")) {
|
|
Main.ANNO = false;
|
|
} else if (args[i].equals("-anno")) {
|
|
Main.ANNO = true;
|
|
} else if (args[i].equals("-preserve-debug")) {
|
|
MethodEditor.PRESERVE_DEBUG = true;
|
|
} else if (args[i].equals("-nouse-stack-vars")) {
|
|
Tree.USE_STACK = false;
|
|
} else if (args[i].equals("-use-stack-vars")) {
|
|
Tree.USE_STACK = true;
|
|
} else if (args[i].equals("-nocompact-array-init")) {
|
|
Main.COMPACT_ARRAY_INIT = false;
|
|
} else if (args[i].equals("-compact-array-init")) {
|
|
Main.COMPACT_ARRAY_INIT = true;
|
|
} else if (args[i].equals("-nostack-alloc")) {
|
|
Main.STACK_ALLOC = false;
|
|
} else if (args[i].equals("-stack-alloc")) {
|
|
Main.STACK_ALLOC = true;
|
|
} else if (args[i].equals("-peel-loops")) {
|
|
if (++i >= args.length) {
|
|
Main.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) {
|
|
Main.usage();
|
|
}
|
|
} catch (final NumberFormatException ex) {
|
|
Main.usage();
|
|
}
|
|
}
|
|
} else if (args[i].equals("-color")) {
|
|
Liveness.UNIQUE = false;
|
|
} else if (args[i].equals("-nocolor")) {
|
|
Liveness.UNIQUE = true;
|
|
} else if (args[i].equals("-only-method")) {
|
|
if (++i >= args.length) {
|
|
Main.usage();
|
|
}
|
|
|
|
Main.METHOD = args[i];
|
|
} else if (args[i].equals("-print-flow-graph")) {
|
|
FlowGraph.PRINT_GRAPH = true;
|
|
} else if (args[i].equals("-classpath")) {
|
|
if (++i >= args.length) {
|
|
Main.usage();
|
|
}
|
|
|
|
final String classpath = args[i];
|
|
Main.loader.setClassPath(classpath);
|
|
} else if (args[i].equals("-skip")) {
|
|
if (++i >= args.length) {
|
|
Main.usage();
|
|
}
|
|
|
|
final String pkg = args[i].replace('.', '/');
|
|
Main.SKIP.add(pkg);
|
|
} else if (args[i].equals("-only")) {
|
|
if (++i >= args.length) {
|
|
Main.usage();
|
|
}
|
|
|
|
final String pkg = args[i].replace('.', '/');
|
|
Main.ONLY.add(pkg);
|
|
} else if (args[i].equals("-nodce")) {
|
|
Main.DCE = false;
|
|
} else if (args[i].equals("-noprop")) {
|
|
Main.PROP = false;
|
|
} else if (args[i].equals("-nopre")) {
|
|
Main.PRE = false;
|
|
} else if (args[i].equals("-dce")) {
|
|
Main.DCE = true;
|
|
} else if (args[i].equals("-prop")) {
|
|
Main.PROP = true;
|
|
} else if (args[i].equals("-pre")) {
|
|
Main.PRE = true;
|
|
} else if (args[i].equals("-closure")) {
|
|
Main.CLOSURE = true;
|
|
} else if (args[i].equals("-relax-loading")) {
|
|
ClassHierarchy.RELAX = true;
|
|
} else if (args[i].equals("-f")) {
|
|
Main.FORCE = true;
|
|
} else if (args[i].startsWith("-")) {
|
|
Main.usage();
|
|
} else if (i == args.length - 1) {
|
|
final File f = new File(args[i]);
|
|
|
|
if (f.exists() && !f.isDirectory()) {
|
|
System.err.println("No such directory: " + f.getPath());
|
|
System.exit(2);
|
|
}
|
|
|
|
if (!f.exists()) {
|
|
f.mkdirs();
|
|
}
|
|
|
|
if (!f.exists()) {
|
|
System.err.println("Couldn't create directory: "
|
|
+ f.getPath());
|
|
System.exit(2);
|
|
}
|
|
|
|
Main.loader.setOutputDir(f);
|
|
gotdir = true;
|
|
} else {
|
|
classes.add(args[i]);
|
|
}
|
|
}
|
|
|
|
if (!gotdir) {
|
|
Main.usage();
|
|
}
|
|
|
|
if (classes.size() == 0) {
|
|
Main.usage();
|
|
}
|
|
|
|
boolean errors = false;
|
|
|
|
final Iterator iter = classes.iterator();
|
|
|
|
while (iter.hasNext()) {
|
|
final String name = (String) iter.next();
|
|
|
|
try {
|
|
Main.loader.loadClass(name);
|
|
} catch (final ClassNotFoundException ex) {
|
|
System.err.println("Couldn't find class: "
|
|
+ ex.getMessage());
|
|
errors = true;
|
|
}
|
|
}
|
|
|
|
if (errors) {
|
|
System.exit(1);
|
|
}
|
|
|
|
Main.context = new CachingBloatContext(Main.loader, classes,
|
|
Main.CLOSURE);
|
|
|
|
if (!Main.CLOSURE) {
|
|
final Iterator e = classes.iterator();
|
|
|
|
while (e.hasNext()) {
|
|
final String name = (String) e.next();
|
|
Main.editClass(name);
|
|
}
|
|
} else {
|
|
classes = null;
|
|
|
|
final Iterator e = Main.context.getHierarchy().classes()
|
|
.iterator();
|
|
|
|
while (e.hasNext()) {
|
|
final Type t = (Type) e.next();
|
|
|
|
if (t.isObject()) {
|
|
Main.editClass(t.className());
|
|
}
|
|
}
|
|
}
|
|
} catch (final ExceptionInInitializerError ex) {
|
|
ex.printStackTrace();
|
|
System.out.println(ex.getException());
|
|
}
|
|
}
|
|
|
|
private static void usage() {
|
|
System.err
|
|
.println("Usage: java EDU.purdue.cs.bloat.optimize.Main"
|
|
+ "\n [-options] classes dir"
|
|
+ "\n"
|
|
+ "\nwhere options include:"
|
|
+ "\n -help print out this message"
|
|
+ "\n -v -verbose turn on verbose mode"
|
|
+ "\n -debug display a hideous amount of debug info"
|
|
+ "\n -classpath <directories separated by colons>"
|
|
+ "\n list directories in which to look for classes"
|
|
+ "\n -f optimize files even if up-to-date"
|
|
+ "\n -closure recursively optimize referenced classes"
|
|
+ "\n -relax-loading don't report errors if a class is not found"
|
|
+ "\n -skip <class|package.*>"
|
|
+ "\n skip the given class or package"
|
|
+ "\n -only <class|package.*>"
|
|
+ "\n skip all but the given class or package"
|
|
+ "\n -preserve-debug try to preserve debug information"
|
|
+ "\n -[no]anno insert an annotation in the contant pool"
|
|
+ "\n -[no]stack-alloc try to push locals onto the operand stack"
|
|
+ "\n -peel-loops <n|all>"
|
|
+ "\n peel innermost loops to enable code hoisting"
|
|
+ "\n (n >= 0 is the maximum loop level to peel)"
|
|
+ "\n -[no]pre perform partial redundency elimination"
|
|
+ "\n -[no]dce perform dead code elimination"
|
|
+ "\n -[no]prop perform copy and constant propagation"
|
|
+ "");
|
|
System.exit(0);
|
|
}
|
|
|
|
private static void editClass(final String className) {
|
|
ClassFile classFile;
|
|
|
|
try {
|
|
classFile = (ClassFile) Main.loader.loadClass(className);
|
|
} catch (final ClassNotFoundException ex) {
|
|
System.err.println("Couldn't find class: " + ex.getMessage());
|
|
return;
|
|
}
|
|
|
|
if (!Main.FORCE) {
|
|
final File source = classFile.file();
|
|
final File target = classFile.outputFile();
|
|
|
|
if ((source != null) && (target != null) && source.exists()
|
|
&& target.exists()
|
|
&& (source.lastModified() < target.lastModified())) {
|
|
|
|
if (Main.VERBOSE) {
|
|
System.out.println(classFile.name() + " is up to date");
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (Main.DEBUG) {
|
|
classFile.print(System.out);
|
|
}
|
|
|
|
final ClassEditor c = Main.context.editClass(classFile);
|
|
|
|
boolean skip = false;
|
|
|
|
final String name = c.type().className();
|
|
final String qual = c.type().qualifier() + "/*";
|
|
|
|
// Edit only classes explicitly mentioned.
|
|
if (Main.ONLY.size() > 0) {
|
|
skip = true;
|
|
|
|
// Only edit classes we explicitly don't name.
|
|
for (int i = 0; i < Main.ONLY.size(); i++) {
|
|
final String pkg = (String) Main.ONLY.get(i);
|
|
|
|
if (name.equals(pkg) || qual.equals(pkg)) {
|
|
skip = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Don't edit classes we explicitly skip.
|
|
if (!skip) {
|
|
for (int i = 0; i < Main.SKIP.size(); i++) {
|
|
final String pkg = (String) Main.SKIP.get(i);
|
|
|
|
if (name.equals(pkg) || qual.equals(pkg)) {
|
|
skip = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (skip) {
|
|
if (Main.VERBOSE) {
|
|
System.out.println("Skipping " + c.type().className());
|
|
}
|
|
|
|
Main.context.release(classFile);
|
|
return;
|
|
}
|
|
|
|
if (Main.VERBOSE) {
|
|
System.out.println("Optimizing " + c.type().className());
|
|
}
|
|
|
|
final MethodInfo[] methods = c.methods();
|
|
|
|
for (int j = 0; j < methods.length; j++) {
|
|
final MethodEditor m;
|
|
|
|
try {
|
|
m = Main.context.editMethod(methods[j]);
|
|
} catch (final ClassFormatException ex) {
|
|
System.err.println(ex.getMessage());
|
|
continue;
|
|
}
|
|
|
|
if ((Main.METHOD != null) && !m.name().equals(Main.METHOD)) {
|
|
Main.context.release(methods[j]);
|
|
continue;
|
|
}
|
|
|
|
if (Main.DEBUG) {
|
|
m.print(System.out);
|
|
}
|
|
|
|
if (m.isNative() || m.isAbstract()) {
|
|
Main.context.release(methods[j]);
|
|
continue;
|
|
}
|
|
|
|
if (Main.COMPACT_ARRAY_INIT) {
|
|
CompactArrayInitializer.transform(m);
|
|
|
|
if (Main.DEBUG) {
|
|
System.out.println("---------- After compaction:");
|
|
m.print(System.out);
|
|
System.out.println("---------- end print");
|
|
}
|
|
}
|
|
|
|
FlowGraph cfg;
|
|
|
|
try {
|
|
cfg = new FlowGraph(m);
|
|
} catch (final ClassFormatException ex) {
|
|
System.err.println(ex.getMessage());
|
|
Main.context.release(methods[j]);
|
|
continue;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
// Do copy propagation first to get rid of all the extra copies
|
|
// inserted for dups. If they're left it, it really slows down
|
|
// value numbering.
|
|
if (Main.PROP) {
|
|
if (Main.DEBUG) {
|
|
System.out.println("-------Before Copy Propagation-------");
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (Main.DCE) {
|
|
if (Main.DEBUG) {
|
|
System.out.println("-----Before Dead Code Elimination----");
|
|
}
|
|
|
|
final DeadCodeElimination 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.DEBUG) {
|
|
System.out.println("---------Doing type inference--------");
|
|
}
|
|
|
|
TypeInference.transform(cfg, Main.context.getHierarchy());
|
|
|
|
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---------");
|
|
}
|
|
|
|
(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-----------");
|
|
}
|
|
|
|
final SSAPRE pre = new SSAPRE(cfg, Main.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---------");
|
|
}
|
|
|
|
(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-------");
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (Main.DCE) {
|
|
if (Main.DEBUG) {
|
|
System.out.println("-----Before Dead Code Elimination----");
|
|
}
|
|
|
|
final DeadCodeElimination 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);
|
|
}
|
|
}
|
|
|
|
(new PersistentCheckElimination()).transform(cfg);
|
|
(new InductionVarAnalyzer()).transform(cfg);
|
|
|
|
/*
|
|
* 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); } }
|
|
*/
|
|
|
|
cfg.commit();
|
|
|
|
Peephole.transform(m);
|
|
|
|
Main.context.commit(methods[j]);
|
|
}
|
|
|
|
if (Main.ANNO) {
|
|
String s = "Optimized with: EDU.purdue.cs.bloat.diva.Main";
|
|
|
|
for (int i = 0; i < Main.ARGS.length; i++) {
|
|
if ((Main.ARGS[i].indexOf(' ') >= 0)
|
|
|| (Main.ARGS[i].indexOf('\t') >= 0)
|
|
|| (Main.ARGS[i].indexOf('\r') >= 0)
|
|
|| (Main.ARGS[i].indexOf('\n') >= 0)) {
|
|
s += " '" + Main.ARGS[i] + "'";
|
|
} else {
|
|
s += " " + Main.ARGS[i];
|
|
}
|
|
}
|
|
|
|
System.out.println(s);
|
|
// c.constants().addConstant(Constant.UTF8, s);
|
|
}
|
|
|
|
Main.context.commit(classFile);
|
|
}
|
|
}
|
|
|