When generating quads out of Java bytecodes, safety checks are
explicitly inserted into the control flow graph. In particular, every
register is NULL_CHECKed before it is deferenced.
These safety checks are necessary to ensure memory safety, but cause
substantial runtime overhead. In this assignment, you will design
sound optimizations to eliminate redundant checks and to optimize the program
in general.
Copy /usr/class/cs243/optimize/optimize.tar.gz from a
Stanford machine to your local machine. The optimize package
contains a monotone dataflow framework, several client
analyses, and additional utilies that may be useful.
You may only modify classes in the optimize.* package except
for optimize.OptimizeHarness.
In addition, you must adhere to the
interfaces of the methods optimize.Optimize.optimize and
optimize.FindRedundantNullChecks.main as specified in the comments
preceding each method declaration.
Write an analysis to find all redundant NULL_CHECKs.
A NULL_CHECK
on register x is redundant at point p if x successfully passed a
NULL_CHECK along all possible paths to p.
For example, in the following code, your analysis must find quad 5
to be redundant but does not need to find any other quad to be redundant.
1 MOVE T1 String, T0 String
2 NULL_CHECK T-1 <g>, T1 String
3 MOVE T2 String, T1 String
4 NULL_CHECK T-1 <g>, T0 String
5 NULL_CHECK T-1 <g>, T1 String
6 NULL_CHECK T-1 <g>, T2 String
In other words, your analysis
needs to find that a NULL_CHECK is redundant on a register
only if that particular register
was NULL_CHECK'ed along all possible paths to that
NULL_CHECK.
The analysis does not have to reason about copies of values to or from other
previously
or subsequently NULL_CHECK'ed registers.
The optimize.FindRedundantNullChecks.main(String[]) method takes
an array of names of classes that should be analyzed for redundant null checks.
Fill in the
optimize.FindRedundantNullChecks.main(String[]) method so that it
prints exactly
one line for each method that contains the method name and a superset
of the quad ids of redundant NULL_CHECKs.
For example:
elaine:~/optimize$ ./bin/parun optimize.FindRedundantNullChecks optimize.test.SomeTest main 4 17 19 sample 5 <init>
means that NULL_CHECKs with quad ids 4, 17, and 19 are
redundant in main, quad ids 5 are redundant in
sample, and no quads are redundant in
<init>.
The optimize.test package contains two test classes named
optimize.test.NullTest
and optimize.test.TowersOfHanoi.
The outputs that should be generated by running
optimize.FindRedundantNullChecks.main(String[]) over these
two classes are in
src/optimize/test/NullTest.basic.out and
src/optimize/test/TowersOfHanoi.basic.out.
Perform any other sound optimization that speeds up the
optimize.test.TowersOfHanoi program.
The extra credit points awarded will range from 0 to 100. The number of points
will depend on the number of quads executed by the optimized program, and
will be applied after all grades are curved. If you work in a group of
two, the same extra credit score is assigned to both members.
All optimizations must be sound!
For example, if you remove even one
necessary null or bounds check, or falsely copy a constant, you will
receive no extra credit.
Your extra credit optimizations must transform the code so that the
grading script can interpret the optimized program to determine the number
of quads actually executed.
The two easiest transforms are the removal and addition of quads using
the QuadIterator's
remove() and
add(Object o) methods which will remove the
most recently returned quad or insert a quad after it, respectively.
To change the values of Operands, the
Operator class contains
static methods to set the appropriate argument.
For example, Move has methods setDest and
setSrc.
To modify the ControlFlowGraph, use
ControlFlowGraph.createBasicBlock to
construct a BasicBlock and use the add methods in
BasicBlock to modify the
lists of quads. See the joeq javadocs for details.
The optimize.OptimizeHarness.main(String[]) method
takes an list of names of classes that should be optimized,
a run class that contains a static main(String[]) method, and
a list of run parameters to be passed to the main method. For example:
elaine:~/optimize$ ./bin/parun optimize.OptimizeHarness --optimize optimize.test.TowersOfHanoi --run-main optimize.test.TowersOfHanoi --run-param 10 Result of interpretation: Returned: null (null checks: 37162 quad count: 129224)
applies your optimizations to optimize.test.TowersOfHanoi, and
then interprets optimize.test.TowersOfHanoi with parameter 10.
The interpreter prints out the number of quads executed.
The optimize.OptimizeHarness.main(String[]) method invokes
the optimize.Optimize.optimize(List<String>) method
which should load the classes to be optimized and apply the control flow
graph transformations. The transformed control flow graphs should
automatically be stored by joeq.Compiler.Quad.CodeCache. Read
joeq.Compiler.Quad.CodeCache and joeq.Main.Helper
carefully to understand how control flow graphs are cached.
The optimize.OptimizeHarness.main(String[]) method then interprets
the run class with respect to the list of run parameters using the
CodeCacheed control flow graphs.
Fill in the
optimize.Optimize.optimize(List<String>) method so that it
applies your optimizations to the classes named by the list of strings parameter.
Describe the design of your extra credit optimizations in the
design.txt file in the root directory.
Tarball your entire Q: Joeq inserts a A: Yes, it is safe to eliminate this optimize directory and copy it to
elaine:/usr/class/cs243/area/students/<sunetid>/hw4, where
<sunetid> is your SUNetID. For example:
elaine:~$ tar cvf optimize.tar optimize
elaine:~$ gzip optimize.tar
elaine:~$ cp optimize.tar.gz /usr/class/cs243/area/students/<sunetid>/hw4
Each person must submit a tarball in their own directory even if they
worked in a group.
Frequently Asked Questions
NULL_CHECK on the value returned from the new operator. Is it safe to eliminate this NULL_CHECK?
NULL_CHECK because new will always either return a non-null value or throw an exception.