CS461

Midterm Exam


Instructions

  1. Write your name clearly at the top of each of your answer sheets.
  2. You must answer all the questions on the exam. The exam has 6 problems. Make sure that you have all 10 problems.
  3. You have until the end of the class period to finish the exam. When the proctor calls time you must immediately drop your pen/pencil and hand in your exam.
  4. Ensure that you write clearly and legibly. Failure to do so may result in the deduction of points.
  5. The exam is closed note, closed book, but you are permitted to have one page, front and back, of notes.
  6. No electronic devices are allowed and all electronic devices that you have with you must be turned off.
  7. Good luck!

  1. (20 points) For each question below, fill in the blank with the best term from the following list:
    compiler               flow dependency     bottom up parser
    loader                 anti dependency     recursive descent parser
    linker                 output dependency   predict set
    lexical analyzer       symbol table        first set
    parser                 export table        follow set
    semantic analyzer      import table        registers
    code generation        link table          L1 cache
    code optimizatin       load table          L2 cache
    frame pointer          stack pointer       main memory
    static link            program counter     stack frame
    
    1. ___________________ Part of the compiler that generates a stream of tokens

    2. ___________________ A type of dependency between two statements that occurs when the first one writes a variable and the second one reads the same variable.

    3. ___________________ The data structure used by a compiler to hold information about names so that it can perform type checking, verify that the number of arguments to a function is correct, verify that the types of arguments are correct, etc.

    4. ___________________ Part of the compiler that organizes tokens into syntactic constructs

    5. ___________________ The following type of dependence among two instructions is caused by each instruction writing to the same variable.

    6. ___________________ This type of parser is a predictive parser that must choose a production to expand a non-terminal as soon as it sees the first token in the string generated by that production.

    7. ___________________ Part of the compiler that extracts the "meaning" of a program and performs tasks such as type checking.

    8. ___________________ This type of set is used by a predictive parser to determine which production to expand a non-terminal by when it seens the next token in the program.

    9. ___________________ The fastest type of memory available to the compiler.

    10. ___________________ The name of the pointer used by a compiler to determine the value of a non-local, statically scoped variable at run-time.

  2. (10 points) Answer true or false for the following questions

    1. ________ The number of CPU cycles that must be delayed while awaiting a load from main memory (not cache, but main memory) has been declining over time.
    2. ________ An anti-dependence may be eliminated by renaming registers.
    3. ________ The number of languages expressable by an LL(1) grammar is a strict subset of the languages expressable by an LR(1) grammar
    4. ________ A grammar is LL(1) if for each non-terminal in the grammar, the predict sets for the productions associated with that non-terminal do not intersect
    5. ________ Pointers make it easier to determine the flow, anti and output dependencies between instructions.

  3. (15 points) Short answer. In two sentences or less, describe the functions of each of the following programs:

    1. compiler
    2. linker
    3. loader

  4. (16 points) Consider the following grammar for pre-fix expressions:
        Exp      -> Atom | Opn
        Atom     -> number | id
        Opn      -> ( + Exp_List )
                 |  ( * Exp_List )
        Exp_List -> Exp_List Exp
                 |  Exp
    
    1. Write a leftmost derivation for the string (+ a 23 (* 16 44))
    2. Draw a parse tree for the string of part (a).

  5. (16 points) Suppose you have the following C++ code:
    class Menu {
      protected:
        string selectedItem;
        list *menuItems;
      public:
        getSelectedItem() { return selectedItem }
        Menu(list *items) { menuItems = items; }
        virtual void draw() = 0;
    };
    
    class PulldownMenu : public Menu {
      public:
        void draw() { 
           ...
           code that references selectedItem and menuItems
           ...
        }
    };
    
    Answer the following questions:

    1. Logically, is PulldownMenu lexically nested within Menu? Answer "yes" or "no".

    2. Write down the list of scope ids that get created and which name creates them. I will start your list:
           0: predefined names
           1: global names
           2: Menu
      
      
      
      
      
      
      
      
    3. Based on the LeBlanc-Cook symbol table presented in class, draw a picture of what the symbol table records for the following names might look like:

      1. Menu
      2. selectedItem
      3. draw in PulldownMenu
      For example (this example is from the book--I know that A2 is not declared in the above code):
      -------------------------------------
      | Name     Category    Scope   Type |
      | A2        param        4     int  |
      -------------------------------------
      
      For your answer:
      1. Do not bother with the "other" field and do not draw a symbol table. I only want to see what the records for these three names should look like.
      2. Fill in the type field with the actual type of the name, if it has one.

  6. (23 points) Suppose you have the following assembly pseudo-code:
    (1) r1 = A
    (2) r2 = B
    (3) r2 = r1 + r2
    (4) C = r2
    (5) r1 = D
    (6) r2 = r1 + 5
    (7) E = r2
    
    Assume that loads and stores to memory have a one-cycle delay (i.e., require 2 CPU cycles) and that arithmetic can be done in a single cycle. Answer the following questions:

    1. For each CPU "cycle" shown below, write the number of the instruction that would execute during that cycle or write "delay" if there is a load/store delay. Complete the first row of cycles before starting the second row of cycles. I have intentionally provided more cycles than are required--leave the excess ones blank.

      _______  _______  _______  _______  _______  _______  _______  _______  
      
      _______  _______  _______  _______  _______  _______  _______  _______  
      
    2. List one flow dependence between two instructions (e.g., 7->5)

    3. List one anti-dependence between two instructions (e.g., 7->5)

    4. List one output dependence between two instructions (e.g., 7->5)

    5. Rewrite the above instructions in the same order, but use register renaming to remove the anti and output dependencies.

    6. Using your register renaming, reorder the instructions to produce a more efficient instruction schedule. Rather than writing out the instructions, just show me the new schedule below by filling in the CPU slots with either an instruction or the word "delay". You will not be able to eliminate all the delays (minimally there will be a delay after the last store instruction). More efficient instruction schedules (i.e., instruction schedules that require fewer cycles) will receive more credit than less efficient instruction schedules.
      _______  _______  _______  _______  _______  _______  _______  _______  
      
      _______  _______  _______  _______  _______  _______  _______  _______