Skip to content

Latest commit

 

History

History

PhiFunctions

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

Static Single-Assignment Form

Static Single-Assignment (SSA) Form is a program representation similar to three-address code, but with a fundamental difference: each variable is assigned at only one program location. SSA form programs enjoy an important property: the definition site of a variable dominates every use of that variable. In practical terms, SSA is more of a notation than an actual program implementation; however, it is still possible to interpret these programs. In this exercise, we shall do exactly this: write an interpreter for SSA-form programs. We shall try two approaches: first, we will give some semantics to phi-functions, and then we will see that we, in fact, need more: to correctly implement the semantics of phi-functions, we need to evaluate them as parallel copies. This lab refers to the class on static single-assignment form, which is part of the material covered in the classroom.

The Assignment

This assignment has two parts. In the first part, we shall implement a "tentative" semantics for phi-functions. I say tentative because it will only work for a very special flavor of SSA-form programs: the so-called programs in Conventional Static Single-Assignment (CSSA) Form. In the second part, we shall implement the "correct" semantics of phi-functions, using a notion of 'phi-block'. All these implementations must be done in lang.py. This file has a few TODO comments that mark the places where interventions are expected.

Conventional Static Single-Assignment Form

CSSA-form programs have the following definition, which we quote from the paper SSA Elimination after Register Allocation: A program is in CSSA form if no two variables related by phi-functions have overlapping live ranges. Figure 1 below explains the notion of phi-related variables with examples.

Example of CSSA-form programs

This tentative semantics for phi-functions will be implemented as follows: given a phi-function such as a0 = phi(a1, ..., an), we shall find, in the environment, the latest binding for any of the variables in the set {a1, ..., an}, and then assign this value to a0. The class Phi is already implemented for you in lang.py. Notice that this class invokes a method on the environment called get_from_list. In this assignment, you will have to implement this method. To help you understand how this semantics works, take a look into Figure 2 below:

Running the factorial function

This "pick-last" semantics will work fine for CSSA-form programs. And it is rather elegant: we do not need to keep track of the path used to reach a phi-function: the last binding within its list of uses will be always the correct assignment (want to know why? Remember that the definition of a variable dominates its uses in an SSA-form program!).

The Swap Problem

However, the pick-last semantics will collapse for non-CSSA form programs. And these programs do exist. Most algorithms that convert a program to SSA form will in fact create CSSA-form programs. However, some compiler optimizations might propagate copies, breaking the conventional property. That is what happened in Figure 1-d. In this case, we start having issues like the infamous "swap problem" and the "lost-copy problem" which Preston Briggs described in the early days of SSA design. Figure 3 illustrates the swap problem.

The Swap Problem

Thus, to correctly implement the semantics of phi-functions, all the phi-functions at some join point in a program must be evaluated together in two steps:

  1. We read the variables in the incoming path that leads to the phi-functions.
  2. We assign the variables that are defined by the phi-functions.

Figure 4 illustrates how this semantics works:

The Semantics of Phi-Functions

In the second part of this lab, we shall implement the correct semantics of Phi-Functions. To this effect, you must finish the implementation of the class PhiBlock. A phi-block groups a number of phi-functions as a matrix. Once a phi-block is evaluated, all the values in a given column of this matrix are read and saved, and then the definitions are updated --- all in parallel. To see a more detailed explanation of this semantics, please, refer to Section 3 of the paper 'SSA Elimination after Register Allocation'. Most of the implementation of the PhiBlock class is done for you; there will be only three small parts that you need to complete. Additionally, there is one small change that must be performed in lang.py: you must update the interp function, so that it passes the identifier of the last instruction to a phi-block. The phi-block will use this identifier as a selector to choose the right parallel assignment to implement. The doctests will guide you through this process through interactive examples.

Uploading the Assignment

Students enrolled in DCC888 have access to UFMG's grading system, via Moodle. You must upload three python files to have your assignment graded: driver.py, lang.py, and program.py. Remember to click on "Avaliar" to have your assignment graded.

Testing without Moodle

As in the previous labs, all the files in this exercise contain doctest comments. You can easily test your implementation by doing, for instance:

python3 -m doctest lang.py

This lab also provides a folder with some test cases. To simulate automatic grading, you can run drive.py directly, e.g.:

python3 driver.py < tests/fib0.txt

In this exercise, the driver runs particular programs (implemented using our three-address SSA-form language) with the inputs in the text file.

Further Reading

Static-Single Assignment form exists since the late eighties. One of the most well-known papers on this subject is Cytron's. That paper introduces the algorithm that we saw in the classroom to insert phi-functions in the program. However, today, compilers usually use Sreedhar's Algorithm instead. In fact, it was Sreedhar and his colleagues that introduced the notion of Conventional Static Single-Assignment form. The "Conventional" property would often emerge in the context of register allocation: a way to remove phi-functions is to allocate all the phi-related variables to the same register. But if these variables have overlapping live ranges... The paper that we quote in this lab to explain CSSA form exists exactly in this context: it shows how to allocate the same register or memory location to all the variables in a phi-function; that's how to do "SSA Elimination after Register Allocation".