You are on page 1of 2

6.

7: Backpatching
Our intermediate code uses symbolic labels. At some point these must be translated
into addresses of instructions. If we use quads all instructions are the same length so
the address is just the number of the instruction. Sometimes we generate the jump
before we generate the target so we can't put in the instruction number on the fly.
Indeed, that is why we used symbolic labels. The easiest method of fixing this up is to
make an extra pass (or two) over the quads to determine the correct instruction
number and use that to replace the symbolic label. This is extra work; a more efficient
technique, which is independent of compilation, is called backpatching.

6.8: Switch Statements


Evaluate an expression, compare it with a vector of constants that are viewed as labels
of the arms of the switch, and execute the matching arm (or a default).

The C language is unusual in that the various cases are just labels for a
giant computed goto at the beginning. The more traditional idea is that you execute
just one of the arms, as in a series of
if
else if
else if
...
end if

6.8.1: Translation of Switch-Statements

1. Simplest implementation to understand is to just transform the switch into the


series if else if's above. This executes roughly k jumps (worst case) for k cases.
2. Instead you can begin with jumps to each case. This again executes roughly k
jumps.
3. Create a jump table. If the constant values lie in a small range and are dense,
then make a list of jumps one for each number in the range and use the value
computed to determine which of these jumps to jump to. This executes 2 jumps
to get to the code to execute and one more to jump to the end of the switch.

6.8.2: Syntax-Directed Translation of Switch-Statements

The class grammar does not have a switch statement so we won't do a detailed SDD.
An SDD for the second method above could be organized as follows.

1. When you process the switch (E) ... production, call newlabel() to generate
labels for next and test which are put into inherited and synthesized attributes
respectively.

2. Then the expression is evaluated with the code and the address synthesized up.

3. The code for the switch has after the code for E a goto test.

4. Each case begins with a newlabel(). The code for the case begins with this label
and then the translation of the arm itself and ends with a goto next. The
generated label paired with the value for this case is added to an inherited
attribute representing a queue of these pairs—actually this is done by some
production like
      cases → case cases | ε
As usual the queue is sent back up the tree by the epsilon production.

5. When we get to the end of the cases we are back at the switch production which
now adds code to the end. Specifically, the test label is gen'ed and then a series
of
      if E.addr = Vi goto Li
statements, where each Li,Vi pair is from the generated queue.

You might also like