CS243: Program Analysis and Optimization Winter 2012
Assignment 4: Finding Redundant Null Checks in JoeQ
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.
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.
We have setup an environment for the programming assignments found here, very similar to homework 2. Download and unpack this archive on your own machine, or in your home directory on a Stanford machine like myth (you can use elaine, but don't test on it: its gcj produces different bytecode for the test cases).
Framework Contents
The top level directory structure of the optimize environment is:
optimize/ |
Root optimize environment for our JoeQ distribution |
optimize/bin/ |
Contains the optimize scripts to set up the environment and launch analyses |
optimize/lib/ |
Contains the Joeq JAR. Unzip the JAR to see the JoeQ source. |
optimize/src/ |
Contains examples, as well as stubs for your code |
optimize/Makefile |
Compiles all the src .java files into a jar, ready to be run using the optimize script |
The src directory contains the source files that you'll be using and creating for this programming assignment. This includes the following subdirectories/packages:
/src/optimize/ |
Contains example code and stubs for your assignment |
/src/optimize/FindRedundantNullChecks.java |
Fill this in with analyses to find redundant null checks |
/src/optimize/Optimize.java |
Fill this in with transformations to optimize the given classes for extra credit |
/src/optimize/test/ |
Contains the programs you will find null checks in, and the correct answers against which you can test your code. |
Your Task
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, T1 String 3 MOVE T2 String, T1 String 4 NULL_CHECK T-1 , T0 String 5 NULL_CHECK T-1 , T1 String 6 NULL_CHECK T-1 , 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 subset of the quad ids of redundant NULL_CHECKs. For example:
myth1:~/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.SkipList (optimize.test.QuickSort is not used for redundant null checks). 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/SkipList.basic.out.
Extra Credit
Perform any other sound optimization that speeds up the optimize.test.SkipList program (the skip list implementation is from here) and optimize.test.QuickSort 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(Quad q) 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:
myth1:~/optimize$ bin/parun optimize.OptimizeHarness --optimize optimize.test.SkipList --run-main optimize.test.SkipList --run-param 20 14 6 21 ... 28 14 17 Result of interpretation: Returned: null (null checks: 29376 quad count: 111014)
applies your optimizations to optimize.test.SkipList, and then
interprets optimize.test.SkipList with parameter 20. The
interpreter prints out the number of quads executed.
The following is an example output of optimize.test.QuickSort:
myth1:~/optimize$ bin/parun optimize.OptimizeHarness --optimize optimize.test.QuickSort --run-main optimize.test.QuickSort --run-param 200 10 18 20 ... 2838 2844 2878 Result of interpretation: Returned: null (null checks: 35263 quad count: 139456)
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 src/optimize directory.
Submission
We ONLY want the java files in your src/optimize directory, do not submit the rest of the framework.
To submit your assignment, please follow the following steps:
- Copy your hw4's
src/optimizedirectory to myth.stanford.edu (keep it in it's own directory) - Login to myth.stanford.edu
- cd to the directory containing only the java files you want to submit
- run
/usr/class/cs243/optimize/submit_hw4.shin this directory.
OR, if you're working with a partner, type:/usr/class/cs243/optimize/submit_hw4.sh partner_SUID - (ex: ./submit_hw4.sh jongsoo)
Only one submission is required per pair.
If you discover a bug after submitting (and before the due date), simply run the submission script again. We'll take the latest version.
Hints
Again, get started early. First, think about what are the biggest optimization opportunities. No matter how sophisticated your optimization is, if the maximum speedup from that is 1%, spending time on this is probably not worth it (Remember Amdahl's law). To do that, it may be a good idea to look at the code of SkipList and QuickSort (both Java source code and Quad representation generated by Joeq).
Compared to HW2 and the first part of HW4, you need to transform your code instead of just doing some analyses. Transforming code (especially, if you want to modify control flows) is much trickier and may involve more Joeq Javadoc reading.