The goal of this assignment is to optimize loops by hoisting loop-invariant code out of the loop.
Implementation: Implement this algorithm as an LLVM loop pass.
Input: An LLVM loop L.
Output: The loop, with loop-invariant computations hoisted out of that loop and its inner loops as far as possible.
Preconditions: The loop simplification pass should have been performed on the function where the loop is present. This ensures that every loop has a preheader. It does not ensure that a “while” loop is converted into a do-while loop nested inside an IF; you should not assume that. You will also need information about natural loops and about dominators. (Another pass that can make LICM more effective is reaassociation, but that is not requred for the algorithm and is not included here; you should run it yourself before your pass). The complete getAnalysisUsage()
method for your pass should look like this:
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesCFG();
AU.addRequiredID(LoopSimplifyID); // Preheader and other transforms
AU.addRequired<LoopInfoWrapperPass>(); // Identify natural loops
AU.addRequired<DominatorTreeWrapperPass>(); // Need dominators for code motion
}
Algorithm: The goal of this algorithm is to hoist as many loop-invariant computations out of loops as possible. We focus only on register-to-register LLVM computations, i.e., those that do not read or write memory. We do not try to move out computations that have no uses within the loop: these could be moved after the loop (at all loop exits), but that requires patching up SSA form. We say 1it is safe to hoist a computation out of a loop only if it is executed at least once in the original loop (when the loop is entered); this condition is checked using dominator information. The full list of criteria are given below.
LICM(L) {
// Each Loop object also gives you a preheader block for the loop .
for (each basic block BB dominated by loop header, in preorder on dominator tree) {
if (BB is immediately within L) { // not in an inner loop or outside L
for (each instruction I in BB) {
if (isLoopInvariant(I) && safeToHoist(I))
move I to pre-header basic block;
}
}
}
}
isLoopInvariant(I):
An instruction is loop-invariant if both of the following are true:
binary operator, shift, select, cast, getelementptr.
terminators, phi, load, store, call, invoke, malloc, free, alloca, vanext, vaarg
.safeToHoist(I):
An instruction is safe to hoist if either of the following is true:
isSafeToSpeculativelyExecute()
(you can find it in llvm/Analysis/ValueTracking.h
).Comments on the safety conditions:
Unlike the conditions discussed in class (and in lecture notes):
Please go through the above guide thoroughly. It has all the details to setup LLVM and write/run your own LLVM passes.
Simple test case with comments
The above test case reasons about the loop invariant code at a high-level. This description is just to think about the cases to consider.
You are responsible for writing test cases to test your pass. Write test cases such that you can test various aspects of your pass; start off with simple ones and then add more complex ones. Feel free to use LLVM test programs. We can use any program for testing your pass. Your program must be robust (no crashes or undefined behavior) and precise (correct output). So, please test your code thoroughly.
You can compare the behavior of your pass with that of LLVM’s built-in version of LICM (use -licm in opt). Note that the built-in version of LICM is more complex than what you are required to implement.
IMPORTANT: DO NOT READ ~/llvm/llvm/lib/Transforms/Scalar/LICM.cpp
You are not allowed to copy source code from anywhere, including other LLVM source files. You can include the header files and call those functions if you think it does precisely what you want. If you are not sure whether you can use something or if you think some LLVM source code is making the assignment trivial, then please let us know on Piazza (you can use a private note).
For each loop, print to stderr the instructions that were hoisted out of the loop. The instructions should be seperated by a new line. Please follow the i/o guideline since the evaluation is done using scripts. The order of the instructions printed to stderr does not matter. Hence, you can print the instruction as soon as you decide to hoist it.
The following is performed during evaluation:
~/llvm/llvm/lib/Transforms
folder, which will contain a CMakeLists.txt that adds your directory as a sub-directory.~/llvm/llvm/lib/Transforms
folder.opt
as mentioned in the LLVM guide to test your pass on a few input programs. Your program should not crash.Please note that 10 points out of 100 points are assigned for code quality. Code quality will include the following: 1. making sure that your submission conforms to the submission guidelines mentioned below, 2. proper code formatting to make your code readable, and 3. proper code documentation to ensure ease of navigation. Ensuring good code quality is always appreciated throughout the software industry. You can use this opportunity to learn more about the good coding practices and incorporating them in your submission.
licmpass.zip:
LLVM pass directory containing ONLY your pass should be uploaded. For instance, ~/llvm/llvm/lib/Transforms/SG12345
directory. The name of this directory should be your EID in the upper case. Please ensure that the naming is correct. Do not just rename the directory. The name of the pass is used in multiple places including CMakeLists and inside source files. Refer to the LLVM guide to know the exact places where renaming is necessary.README_LICM.md:
The README file should mention all the materials that you have read or used for this assignment, including LLVM documentation and source files.This is an adaptation of an assignment in Vikram Adve’s Compiler Construction course at UIUC.