EAC 2nd ed. cover

Compiler Terminology

One field of Computer Science is Optimizing Compilers. Starting with the first high-level programming language Fortran in 1954, the field has a long history, and understandably, its terminology has become overloaded (no longer the Fortran Franca days). Compiler students and even established researchers today often use the same terms for different meanings. Part of the confusion is that the original wording, while appropriate in the past, becomes misleading in modern compilers. This post gives the traditional definitions for common terms and their sources, intermixed with background context based on my teaching of an advanced compiler course at URCS.

In Engineering a Compiler by Keith Cooper and Linda Torczon, a standard compiler textbook, the section (§8.3) on Scope of Optimization gives the following definitions:

  • Local methods “operate over a single-basic block.” In a basic block, “statements are executed sequentially” and “if any statement executes, the entire block executes.”
  • Global methods “use an entire procedure as context.” A typical representation of a procedure is a Control Flow Graph (CFG). In between local and global are regions based on particular properties, e.g., a loop nest, an Extended Basic Block (cycle-free), or a dominator tree. Global methods are also called intraprocedural.
  • Interprocedural methods “consider scopes larger than a single procedure.” They are sometimes called whole-program methods.

I used the Cooper-Torczon book when I created the advanced compiler course at URCS in the early 2000s, right when the first edition was published, and taught it for years. The first technique to teach students was local value numbering, with the original example from the 1971 paper by John Cocke (’87 Turing awardee) and Frances Allen (’06 Turing awardee, first woman to receive the award) titled “A Catalogue of Optimizing Transformations” and used the example from the EAC book to progress from local value numbering to regional methods based on EBBs and dominators. Students liked the material. Chris Stewart, now a professor at OSU, conceived a new idea during a lecture; students in later years came to know it as the Stewart extension

A compiler finds properties of program code, i.e., properties that hold on all its executions. This is known as the meet-over-all-path solution. When we consider a cyclic CFG, the number of possible paths becomes infinite. This is the course transition to data flow analysis.

  • Data flow analysis was pioneered by Kam and Ullman (2020 Turing awardee). It is the foundation of iterative analysis used by almost all compilers. An analogy is solving a recursive equation in math. Think of an iterative solution as the way to find a fixed-point solution. The key questions are convergence, speed of convergence, and the uniqueness of the solution. Their 1977 paper was titled “Monotone Data Flow Analysis Frameworks” and gave the monotone and distributive conditions for iterative analysis. Their earlier paper named the technique “Global Data Flow Analysis.”
  • Def-use chains are what some people think when they say “data flow”. Def-use analysis may be local or global. The resulting graph was called def-use chains in compiler papers.

Modern compilers, starting with LLVM, have converged on the intermediate representation called static-single assignment (SSA). Cytron, Ferrante, Rosen, Wegman, and Zadeck published the breakthrough technique in POPL’89 and TOPLAS’91. SSA was a direct improvement of def-use chains. Def-use chains may be quadratic in size in the worst case, SSA is linear. More useful is that SSA’s phi-functions more directly encode control flow information and makes data flow analysis simpler and more effective.

The SSA paper authors did not implement the technique. Keith Cooper, Ken Kennedy and their students were the first to test it out in the Rice compiler (SSA co-inventor F. Kenneth Zadeck was a student of Ken’s, graduated in 1983). EAC, first edition in 2003, is the first compiler textbook that teaches SSA and SSA-based compiler optimization. At the time, people questioned whether it was too advanced for undergraduate students. Chris Lattner, then a graduate student, created LLVM IR based on SSA. I don’t think it is coincidence that his advisor, Vikram Adve, was a researcher scientist at Rice at the time when SSA was first implemented. LLVM is now the most widely used compiler in the world.

My favorite data flow analysis in the Cooper-Torczon book is partial redundancy elimination (PRE). The classic formulation uses four coupled data flow equations. One year when taking my course, Linxiang Xiang implemented SSA-based PRE in Ruby. It was a most impressive compiler project.

In early days of CS, a PhD thesis might be just a new data flow analysis technique. In those days, everyone knew data flow to be the iterative compiler analysis. Now, people may use the term for its literal meaning but create confusion because it conflicts with the usage in the past. This triggered a lively discussion at Friday systems meeting and an email followup. I agree with my colleague Michael Scott (I have his Programming Language Pragmatics book right next to EAC on my shelf) who said “Yes, it’s kinda confusing. But if you try to redefine the terms it’s going to be even more confusing.

Evolution of the Compiler Course

A motto in compiler teaching is learning a compiler by building one. As an undergrad at PKU, I took the compiler course taught by Sun Jiasu (孙家驯,北大70本) in sophomore year and then compiler practice by Professor Ding the next semester, which had no lecture and just one assignment: implement a compiler for a Pascal-like language (nested procedures, static scoping). As an MS student at Michigan, I took Steve Carr’s course and wrote an optimizer using Rice’s compiler base code. This was why as a PhD student at Rice, I sat in on Keith Cooper’s course but did not take it officially. My buddy Vijay Pai (later a tenured professor at Purdue ECE) took the course with me, vowing to beat the CS compiler wizard in the compiler project — but he couldn’t.

At the turn of the millennium, Rochester and Rice were the two smallest (with about 12 faculty members each) in the top 20 CS programs in the National Academy rankings. Rice was a compiler powerhouse, with many courses in computer systems but maybe one in AI (which I didn’t take). Rochester was an AI department with courses in computer vision, natural-language processing, robotics etc but mainly just two in systems: computer systems and programming systems. The latter teaches both compilers and programming languages, forming the basis of Michael’s popular PLP book, still the most adopted textbook on the subject among top 30 CS departments. After joining Rochester in July 2000, I taught the programming systems course for several years and then created the advanced compiler course as the follow-up course. When I teach either one, I require students to implement a compiler.

For teaching, I wrote a compiler IR in a handful of Java classes and asked students to write compiler passes on this compact IR. A kindred soul was Chris Lattner, then a PhD student attending the first ACM SIGPLAN MSP workshop Trishul Chilimbi and I co-organized with PLDI in Berlin in 2002. He told me that he was implementing an object-oriented compact IR, too, in C++.

Early students and TAs included my first two students, Yutao Zhong (who has taught at George Mason in DC for many years) and Xipeng Shen (now directing the intelligent software center at Purdue). Bill Scherer (later research faculty at Rice) wrote a test suite for the teaching compiler. One program files a tax return (the 1040 form), and another draws the Mandelbrot set. The Scherer tests were used for many years to score student compilers by how much they reduce program instructions. When Ben Van Durme (now faculty at Johns Hopkins and research manager at Microsoft) took the course, he told me every day that he knew the exact way to remove two more instructions from one test program but it was too much pain for too little gain. The morning after the deadline, he ran into me, full of joy and pride, saying “I worked overnight and did it!”

Starting in the 1980s, GCC has been the foundation of open-source software. It was a real compiler but too complex for classroom use. In 2005, GCC 4.0 adopted a compact IR called GIMPLE. I invited its API designer, Diego Novillo, to visit Rochester (from Toronto) to give a guest lecture. Shortly after, I gave students the option and preparation to complete the compiler project in GCC, which at the time meant adding their code and compiling it alongside the other two million lines of source code. Tongxin Bai, then a PhD student, wrote GCC passes and Linux kernel modules (after taking the OS course from Sandhya Dwarkadas) as routine, had so much fun, and made money from his Google internship, he didn’t think about publishing and graduating. John Pershing (featured in the 2025 newsletter) was part of a group of undergrads who chose the GCC option for the compiler project and made the compiler work. As LLVM became widely adopted, it also became an option for the compiler project. Here is a project assignment from Rahman Lavaee in 2016. One year later, Dong Chen changed the two options to LLVM and Rust.

Xipeng Shen came to Rochester in 2001 as an AI student and switched to become my student after taking the compiler course. When he graduated in 2006, he knew more about computer systems than all AI professors and more about machine learning than all systems professors. His first job was at William and Mary where he taught two courses: compilers and AI. Many of his students have become professors. When I taught CS 258 in 2023, a student showed me the compiler course slides he found online by Yufei Ding, then a professor at UC Santa Barbara and now at UCSD. Some of the material had come full circle, after starting its life years ago right here in Rochester.

CS 255/455 Spring 2018

CSC 255/455 Software Analysis and Improvement (Spring 2017)

Lecture slides, reading, later assignments, and other material will be distributed through Blackboard.


Assignments:


 Course description

With the increasing diversity and complexity of computers and their applications, the development of efficient, reliable software has become increasingly dependent on automatic support from compilers & other program analysis and translation tools. This course covers principal topics in understanding and transforming programs by the compiler and at run time. Specific techniques include data flow and dependence theories and analyses; type checking and program correctness, security, and verification; memory and cache management; static and dynamic program transformation; and performance analysis and modeling.

Course projects include the design and implementation of program analysis and improvement tools.  Meets jointly with CSC 255, an undergraduate-level course whose requirement includes a subset of topics and a simpler version of the project.

 Instructor and grading

Teaching staff: Chen Ding, Prof., Wegmans Hall Rm 3407, x51373;  Fangzhou Liu, Grad TA;  Zhizhou Zhang, Undergrad TA.

Lectures: Mondays and Wednesdays, 10:25am-11:40am, Hylan 202

Office hours: Ding, Fridays 11am to noon (and Mondays for any 15 minute period between 3:30pm and 5:30pm if pre-arranged).

TA Office hours: Zhizhou, Mondays 2 to 3pm, open area outside the elevator, third floor Wegmans Hall.  Jerry, Tuesdays 3 to 4pm, 3407 Wegmans Hall.

Grading (total 100%)

  • midterm and final exams are 15% and 20% respectively
  • the projects total to 40% (LVN 5%, LLVM trivial 5%, loop+index 10%, dep 10%, par 10%)
  • written assignments are 25% (trivial 1%; 4 assignments 6% each)

 Textbooks and other resources (on reserve at Carlson)

Optimizing Compilers for Modern Architectures (UR access through books24x7), Randy Allen and Ken Kennedy, Morgan Kaufmann Publishers, 2001. Chapters 1, 2, 3, 7, 8, 9, 10, 11. lecture notes from Ken Kennedy. On-line Errata

Engineering a Compiler, (2nd edition preferred, 1st okay), Keith D. Cooper and Linda Torczon, Morgan Kaufmann Publishers. Chapters 1, 8, 9, 10, 12 and 13 (both editions). lecture notes and additional reading from Keith Cooper. On-line Errata

Compilers: Principles, Techniques, and Tools (2nd edition), Alfred V. Aho, Monica S. Lam, Ravi Sethi, and Jeffrey D. Ullman, Pearson.

Static Single Assignment Book, Rastello et al. (in progress)

Introduction to Lattices and Order,  Davey and Priestley, Cambridge University Press.

CS 255/455 Spring 2017

CSC 255/455 Software Analysis and Improvement (Spring 2017)

Lecture slides (when used), demonstration programs, and some of the reading material will be distributed through Blackboard.  Assignments and projects will be listed here.


Assignments:

  • Trivia assignment.  Search slashdot.org for the posts on GCC, LLVM, RUST, Scala or Haskell.  Select two posts to read the posts and all discussions.  Write a summary with 200 or more words for each of the two posts.  The summary should include at least a precise fact on the topic as well as an opinion with all supporting and disagreeing arguments pulled from the discussions.  Print and submit a paper copy Monday January 23rd at the start of the class.  Then see me in one of my office hours for feedback on the summary.   Meet me on or before February 3rd.  The grade is assigned after the meeting.  Bring a copy of your paper to the meeting (in addition to the one you submit).

 Course description

With the increasing diversity and complexity of computers and their applications, the development of efficient, reliable software has become increasingly dependent on automatic support from compilers & other program analysis and translation tools. This course covers principal topics in understanding and transforming programs by the compiler and at run time. Specific techniques include data flow and dependence theories and analyses; type checking and program correctness, security, and verification; memory and cache management; static and dynamic program transformation; and performance analysis and modeling.

Course projects include the design and implementation of program analysis and improvement tools.  Meets jointly with CSC 255, an undergraduate-level course whose requirement includes a subset of topics and a simpler version of the project.

 

 Instructor and grading

Teaching staff: Chen Ding, Prof., CSB Rm 720, x51373;  Dong Chen, Grad TA;  Jacob Bisnett, Undergrad TA.

Lectures: Mondays and Wednesdays, 10:25am-11:40am, CSB 601

Office hours: Ding, Fridays 11am to noon (and Mondays for any 15 minute period between 3:30pm and 5:30pm if pre-arranged).

TA Office hours: Dong Chen, Tuesdays 3:30pm to 4:30, CSB 720. Jacob Bisnett, Thursday 1:00 pm to 1:50 pm, CSB 720.

Grading (total 100%)

  • midterm and final exams are 15% and 20% respectively
  • the projects total to 40% (LVN 5%, GCC/LLVM/RUST 5%, local opt 10%, global opt 10%, final phase 10%)
  • written assignments are 25% (trivial 1%; 3 assignments 8% each)

 Textbooks and other resources

Optimizing Compilers for Modern Architectures (UR access through books24x7), Randy Allen and Ken Kennedy, Morgan Kaufmann Publishers, 2001. Chapters 1, 2, 3, 7, 8, 9, 10, 11. lecture notes from Ken Kennedy. On-line Errata

Engineering a Compiler, (2nd edition preferred, 1st okay), Keith D. Cooper and Linda Torczon, Morgan Kaufmann Publishers. Chapters 1, 8, 9, 10, 12 and 13 (both editions). lecture notes and additional reading from Keith Cooper. On-line Errata

Compilers: Principles, Techniques, and Tools (2nd edition), Alfred V. Aho, Monica S. Lam, Ravi Sethi, and Jeffrey D. Ullman, Pearson.

Static Single Assignment Book, Rastello et al. (in progress)

Assignment 6 (Dead Code Elimination)

This assignment is due Monday April 18th at 11:59pm.

For this assignment you are expected to identify and eliminate dead code. Dead code is any instruction that does not affect the output of the program (a function, in particular). For DCE (dead code elimination), you will need to use def-use chains to reason about instructions that do not affect the outputs. Remember that the output of a function is not limited to what it is returning.

IMPORTANT NOTE:

Those who implemented assignment 4 in LLVM, and have relied on the SSA property to find def-use chains with a linear scan of the LLVM-IR, are expected to implement a further requirement for this assignment, whether they are choosing URCC or LLVM for this assignment. That is Common Subexpression Elimination (CSE). An example of this optimization has been illustrated below.

CSE-example

Adopted from [1]

 

For CSE, you must first do the AVAIL analysis and then reason about expressions that are redundant.

Note 1: Since LLVM does not permit copy instructions, you may need to use the “ReplaceAllUsesWith” method to replace all uses of c with t. Also, since LLVM uses the SSA representation, you might need to insert new phi nodes.

Note 2: You do not need to implement partial redundancy elimination (PRE) for this assignment.

Testing

Your are expected to test your optimization pass by counting the number executed instructions. To do so, you are provided with a test script. Download this script into your URCC repository.

If you are using URCC for this assignment, you can easily run “./test.rb all” to run all the tests. You can also run an individual test by running “./test.rb #{program_name}”.

If you are using LLVM for this assignment, you will need to reimplement Test.test method in the script above by following the current implementation of that method. This is where you need to compile a program and apply your optimization, and instrumentation. You are allowed to use your peers’ test scripts.

The “./test.rb all” command will give you a “urcc_test_results.txt” file that includes the program outputs (along with your dynamic instruction count. Remember to print the instruction count as a single line “INST COUNT: x”, where x is the number of total executed instructions). Include this file in your report, along with your test script (if modified) and instructions on how to run the tests. Do not forget to give a thorough explanation about your implementation.

Note: URCC appears to be failing on generating a correct executable for two of the test cases: sort.c and tax.c. You can exclude these test cases by removing them from the test directory.

Assignment 5: Def-Use Chains

This assignment is due on Wednesday March 30th at 11:59pm.

In this assignment, you are expected to build the def-use chains for a program. You can either use your own CFG pass (if you are sure about its correctness), or adopt any correct CFG pass that is available to you.

Your output must have the following format. For every definition of a variable a, and for every use of a which is reachable from that definition (without any intervening def), you must output

[a, def, use],

where def and use are the program statements corresponding to the definition and use of a.

Run your analysis on the test cases provided in the URCC test directory and report the results. Explain your implementation and findings in a readme file. Archive everything and submit on blackboard.

Assignment 4: LVN in URCC/LLVM

For this assignment you are expected to implement the local value numbering optimization in URCC or LLVM. The requirements are the same as in assignment 1, except that the redundancy elimination part is now required. For this assignment, you don’t need to implement Stewart extension.

Test your optimization pass on URCC test cases. Report your implementation details and your findings in a readme file and submit on blackboard.

If you are working on LLVM, remember to turn off optimizations by using the “-O0” flag when emitting LLVM bitcode (The base project directory in /home/hoover/u1/cs255/cs255-llvm has been modified accordingly).

This assignment is due Sunday March 20th at 11:59pm.

Assignment 3 (CFG in URCC/LLVM)

In this assignment you will implement a pass to build a program’s CFG from its linear code. This assignment is due on Sunday March 6th at 11:59pm.

For this assignment, you can choose to use either the URCC compiler or LLVM.

Instructions for URCC

  1. Checkout and install the URCC compiler (follow the instructions in the README file). Make sure you are using a version of clang between 3.3 and 3.6 or URCC may fail. If you decide to use the CSUG machines, you can use clang 3.6 that is installed in /u/cs255/build-llvm-36/bin.
  2. Follow the algorithm in section 5.3.4 of the Cooper & Torczon book (page 241) to write a pass that builds the CFG of the program and visualizes it using graphViz (instructions on how to write a URCC pass are included in the README file).
  3. Test your compiler pass on test cases included in the test directory.
  4. Explain your implementation in a Readme file. Report all test case failures.
  5. Archive your code and the Readme file and submit on Blackboard.

Note: A CFG pass is already implemented in the URCC compiler. You shall not look at that implementation.

Instructions for LLVM

  1. Following the same instructions as in assignment 2, create an LLVM project in your CSUG account.
  2. Implement a pass that builds the CFG of the program and visualizes it using graphviz. If you need to iterate over instructions in a function, you must use the following code:
    #include "llvm/IR/InstIterator.h"
    
    // F is a pointer to a Function instance
    for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I)
      errs() << *I << "\n";
  3. You can use BGL (Boost Graph Library), which is already installed on CSUG filesystem. Graphviz is included in this library.
  4. Test your compiler pass on test cases included in URCC’s repository (https://github.com/dcompiler/urcc/tree/master/tests).
  5. Explain your implementation in a Readme file. Report all test case failures.
  6. Archive your code and the Readme file and submit on Blackboard.

Note: A CFG pass is already implemented in LLVM. You shall not look at that implementation.

 

Assignment 2 (GCC/LLVM)

In this assignment, you will implement and test a compiler pass that instruments a program to report the number of intermediate-level executed instructions. You can choose to implement this pass either in gcc or llvm. For your convenience, these two compilers are already installed on the csug network.

The idea is to insert appropriate calls in the program (instrumentation).

*********************************Instructions on GCC****************************************
——————————————————————————————–
Log in to your csug account.
$ cp -r /u/cs255/cs255-gcc YOURUSERNAME-cs255-gcc
$ cd YOURUSERNAME-cs255-gcc

Included files:
———————–
* inst-cnt-plugin.cc
You must implement your compiler pass here.
This file already provides the skeleton and a related example.

* inst-cnt-rt.c
This file implements the runtime functions that you need for the instrumentation.

* test.c
This is a simple program to test your pass.

After implementing the pass, compile it by running “make” and test it using “make check”. This gives you the instrumented program “test”. Run it on the string “cs255” and report your output. Make sure to explain your implementation and findings in a readme file.

Submission guideline:
Archive your working directory using the following command line, and submit on Blackboard.
tar -czvf YOURUSERNAME-cs255-gcc.tar.gz YOURUSERNAME-cs255-gcc/

Note: you can use the gcc option -fdump-tree-all to inspect your instrumentation at the GIMPLE level. After running “make check” with this option, look for the file which has your pass name as a suffix in its filename.

*********************************Instructions on LLVM**************************************
——————————————————————————————–
Log in to your csug account.
$ cp -r /u/cs255/cs255-llvm YOURUSERNAME-cs255-llvm
$ cd YOURUSERNAME-cs255-llvm

Included files:
———————–
* lib/InstCounter.cpp
You must implement your compiler pass here.
This file already provides the skeleton and a related example.

* runtime/InstCounting.c
This file implements the runtime functions that you need for the instrumentation.

* test/test.c
This is a simple program to test your pass.

After implementing your pass, compile it by running “make” in your top-level directory. Then cd into the “test” directory and run “make check” to test your pass. This gives you the instrumented program “test”. Run it on the string “cs255” and report your output. Make sure to explain your implementation and findings in a readme file.

Submission guideline:
Archive your working directory using the following command line, and submit on Blackboard.
tar –exclude=’.svn’ –exclude=’autoconf’ -czvf YOURUSERNAME-cs255-llvm.tar.gz YOURUSERNAME-cs255-llvm/

Note: you can use the llvm-dis tool (/u/cs255/build-llvm-38/bin/llvm-dis) to check your instrumentation at IR level. Run this tool on the llvm bitcode file that is generated by your pass:
/u/cs255/build-llvm-38/bin/llvm-dis <test.bc.opt> test.bc.opt.ll
———————————————————————————————