Frames

Read Chapter 6 before you proceed. You should be completely comfortable with Section 6.1 before you can even think of proceeding. It is ok if some of the things in Section 6.2 don't make complete sense at this point.

Machine-independent Frame abstraction:

Section 6.2 begins with a frame abstraction that hides machine-independent details. Create a package Frame with three classes:
  1. class Frame: This abstract class specifies the interface that every machine-specific frame object should implement. See the top of page 141 for details.
  2. class Access: an abstract class with no methods or variables. This class abstracts the specific way in which a given variable can be accessed. For each target machine, we'll implement the particular way in which variables can be accessed, for example, via registers or offsets from the stack frame.
  3. class AccessList: implements a list of Access objects. It should have two public variables: head and tail

Machine-specific Frame:

Create a package Sparc if you are compiling for the SPARC, a package Spim if you are compiling for the SPIM, etc. The package will most likely contain the following three classes which just implement the machine-independent Frame abstractions above. Assuming we are compiling for the SPARC, the classes are:
  1. class InFrame extends Frame.Access: this class describes how to access variables that should be stored in the frame. The access is completely described by the integer offset from the frame pointer at which the variable will be stored.
  2. class InReg extends Frame.Access: this class describes how to access variables that should be stored in registers. At this point, registers are given by temporary names (using the class Temp.Temp). See the bottom of p.141 and the top of p.142 for details.
  3. class SparcFrame extends Frame.Frame: this class should describe the internals of the SPARC stack frame and implement the abstract methods in Frame.Frame. Here is the beginning of the class:
    public class SparcFrame extends Frame.Frame {
      private static int WORD_SIZE = 4;
    
      // Offset at which formal arguments should be stored. The first
      // argument goes at offset 68, the second at 68+WORD_SIZE, and so on.
      private static int FORMAL_AREA = 68; 
    
      // Offsets at which local variables should be stored. The first 
      // local variable goes at offset -4, the second at offset -4-WORD_SIZE,
      // and so on.
      private static int LOCALS_AREA = -4;
    
      // The method newFrame should just call the constructor
      public Frame.Frame newFrame (Label name, Util.BoolList formals) {
        return new SparcFrame (name, formals);
      }
    
      // The constructor should initialize the instance variables:
      //   Label name, and
      //   Frame.AccessList formals
      // When creating a SparcFrame, we are given a list describing the 
      // formals arguments. A "true" says that the formal variable must
      // go in the stack frame (use an InFrame access); a "false" says that 
      // the formal variable may go in a register (use an InReg access).
      public SparcFrame (Label name, Util.BoolList formals) {
         ...
      }
    
      // The next method allocates space for a local variable. As described
      // above, a "true" means that the variable must be allocated in the
      // frame and a "false" means that the variable may go in a register.
      // See pp.143-144.
      public Frame.Access allocLocal (boolean escape) {
        ...
      }
    }
    

Optional phase; do it for extra credit!!!

Calculating escapes (see pp.144-146): Create a package FindEscape with the following classes:
  1. The classes Escape, FormalEscape, and VarEscape on p.145. (By the way the class Escape should be abstract since it has an abstract method.)
  2. The class FindEscape whose beginnings are given on p.145. This is really a simple pass over the abstract syntax tree that sets the escape flag for a variable only if it used in a nested function. If you omit it, your compiler will still work, but it will store all variables in the stack frame.

Levels:

The package Translate will augment frames and accesses with knowledge about scope. A frame describes how to access an escaping variable using an offset from the frame pointer. But if a nested function (residing in a different frame) needs to access that variable, then we need to navigate from one frame to another. The new notion of Access is implemented by the package Translate. There are three new classes that need to be implemented:
  1. class Access: (no relation to Frame.Access). The class is defined as follows: (See bottom of p.148)
    package Translate;
    
    public class Access {
      Level home;
      Frame.Access acc;
    
      Access (Level home, Frame.Access acc) {
        this.home = home;
        this.acc = acc;
      }
    }
    
  2. class AccessList: (no relation to Frame.AccessList). This class is just a container for a list of Access objects; use two public variables head and tail.
  3. class Level: This class describes the runtime information associated with a Tiger function. This information includes the frame, and the parent's Level. The parent's Level describes the function in which the current function is nested; it could be null. The Level class delegates most operations to its frame, but handles everything that has to do with scope. (See p.147 for details.)

The semantic analysis module:

  1. You will need to modify the VarEntry and FunEntry structures stored in the environment as described in p.148. Your actual environment will have to change accordingly. To initialize the environment, you should arrange for the constructor to receive the outermost level structure in which all the library functions will be defined.
  2. class Semant will have to modified following the directions on pp.149-150. Please save periodic copies of Semant. You will have to make frequent changes. Most of the methods will remain unchanged. The main things that have to change are:
  3. Putting things together: The Main class that drives the compiler should have something like:
        ...
        // create a new frame with no formals for the top level
        Frame.Frame frame = new Sparc.SparcFrame(new Temp.Label("main"), null);
        // use above frame to create the top level. This is the level
        // in which the library functions are declared. The Tiger
        // program itself will be defined in a level that is nested
        // within the top level one.
        Translate.Level outermost = new Translate.Level(frame);
    
        // create and call the semantic analysis module
        Semant analyze = new Semant(errorMsg, outermost);
        analyze.transProg(absyn);
        if (errorMsg.anyErrors) throw new Error("Type Error");
        ...
    

Page visited times since September 14, 1998.

sabry@cs.uoregon.edu