CS302 Spr'99 Lecture Notes
Lecture 7
Uses of Boolean Expressions

Used to drive conditional execution of program sections.

\begin{code}IF (a < 17) OR (b = 12) THEN
\par WHILE NOT ((x+1) > 39) DO

(In some languages) may be assigned to boolean variables or passed as parameters.

\begin{code}VAR b : BOOLEAN := (a < 17) OR (b = 12);
IF b THEN ... ELSE ...
myproc(b); (* procedure call *)
Boolean Expressions

Two representations may be useful:

$\bullet$ Value Representation.

Encode true and false numerically, e.g., as 1 and 0, and treat boolean expressions like arithmetic expressions.

Pro: Language may support boolean values.
Con: Bad match to hardware.

$\bullet$ Flow-of-control Representation.

Position in generated code represents boolean value.

Pro: Good when ``short-circuit'' evaluation is allowed (or required), e.g., in C expression e1 || e2, e2 should be evaluated only if e1 is false.

Reminder: Some languages mandate short-circuit evaluation; others prohibit it; still others leave it up to the compiler writer.

Pro: Convenient for control statements.

$\bullet$ For PCAT, we'll use flow-of-control approach, and convert to values when necessary.

Sample Productions for Value-based Conditional Evaluation

\begin{code}B := E1 '<' E2
B.place = newtemp()
B.code =
let true = newlabel(...

\begin{code}IF E1 < E2 GOTO L1
T := 0
L1: T := 1
L2: ...\end{code}
More Sample Value-based Productions

\begin{code}B := B1 OR B2
B.place = newtemp()
B.code = B1.code @
B2.code @
[gen(B.place,\vert,B1.place,B2.place)] \end{code}
Here | represents bit-wise OR. Note that this implements NON-short-circuiting form of OR.

\begin{code}S := IF B THEN S1 ELSE S2
S.code = let false = newlabel()
after = ...
...ter,goto,_,_)] @
[gen(false,:,_,_)] @
S2.code @
[gen(after,:,_,_)] \end{code}

\begin{code}IF B = 0 GOTO L1
L1: S2
L2: ...\end{code}

Example Value-based Code

IF (a > 7) OR (b = 5) THEN x = 7 ELSE y = 2;

\begin{code}t1 := addr a
t2 := *t1
t3 := const 7
if t2 > t3 goto L1
t4 := co...
...:= t1 0
goto L6
t12 := const 2
t13 := addr y
*t13 := t12
Basic Control-flow Representation

Idea: Code generated for boolean and relational expressions has true and false ``exits'', i.e., code evaluates expression and then jumps to one place if true and another place if false.

$\bullet$ Relational expressions perform test and jump to true or false exit accordingly.

$\bullet$ Boolean variables and constants jump directly to appropriate true or false exit.

$\bullet$ Boolean expressions simply adjust/combine true/false exits of their sub-expressions.

$\bullet$ Conditional statements define true and false exits of boolean sub-expression to point to appropriate code blocks, e.g., THEN and ELSE branches.

$\bullet$ If boolean-typed expression must deliver a value, true and false exits are defined to point to code that loads the value.

Example (assuming short-circuiting)

\begin{code}IF (a > 7) OR (b = 5) THEN x = 7 ELSE y = 2;
\par t1 := addr a
t2 :...
...t8 := t7
goto L3
t9 := const 2
t10 := addr y
*t10 := t9

Conditional Statements - Semi-Naive Approach

Use control flow representation for boolean-typed expressions; define labels on per-statement basis.

\begin{code}S := IF B THEN S1 ELSE S2
B.true = newlabel();
B.false = newlabel(...
...,goto,_,_)] @
[gen(B.false,:,_,_)] @
S2.code @

\begin{code}IF B GOTO L1
L1: S1
L2: S2

Relational Expressions

Inherit true and false label attributes.

Synthesize code to perform appropriate test and jump to appropriate label.

Code doesn't build a value, so no place attribute.

\begin{code}B := E1 '=' E2
B.code =
E1.code @
E2.code @
... [gen(B.true,if<,E1.place,E2.place),
\par ...

Boolean Expressions

Inherit true and false label attributes. Pass them down to subexpressions, after suitable manipulation; synthesize code attribute.

Again, no place attribute.
\begin{code}B := B1 OR B2
B1.true = B.true
B1.false = newlabel()
B2.true = B....
...r B := NOT B1
B1.true = B.false
B1.false = B.true
B.code = B1.code

Conversions to and from value form

Boolean-typed identifiers (variables, true and false constants) must be ``converted'' to control-flow form when tested.
\begin{code}B := V
B.code = V.code @
(Assuming 0 = false, non-0 = true)

Similarly, must convert other way when a value is needed, generating code to build a value into a place.

\begin{code}E := B
B.true = newlabel()
B.false = newlabel()
E.place = newtemp...
Disadvantages of Naive Approach

$\bullet$ Code for each statement always concludes by ``falling through'' to next statement.

$\bullet$ There is no information flow between code generation for statements.

\begin{code}S := S1 ';' S2
S.code = S1.code @ S2.code \end{code}
This can lead to bad code, e.g.,
\begin{code}WHILE B1 DO (WHILE B2 DO S)
\par {\rm generates}
\par L1: IF B1 GOTO...
...TO L4
GOTO L5 {\rm \lq\lq jump to jump''}
L4: S
L3: \end{code}
We can eliminate problems like this during optimization, but it's easy to avoid some of them in the first place. Idea: Defer Definition of Target Labels

$\bullet$ Give each statement an inherited attribute .next, which says where to transfer control after statement.

$\bullet$ Code generated for each statement guarantees either to transfer control to .next label or to ``fall through.''

\begin{code}S := WHILE B DO S1
B.true = newlabel()
B.false = S.next
S1.next =...
...code @
[gen(B.true,:,_,_)] @
S1.code @

Deferred Label Definition (continued)

\begin{code}S := IF B THEN S1 ELSE S2
B.true = newlabel()
B.false = newlabel()...
...S2.next = S.next
S.code = S1.code @
[gen(S1.next,:,_,_)] @

Now Get Better Code

\begin{code}WHILE B1 DO (WHILE B2 DO S)
\par {\rm now generates}
\par L1: IF B1 GOTO L2
L2: If B2 GOTO L3
L3: S
L?: \end{code}
What about break or exit statements that can cause jumps out of loops? Add a .break inherited attribute!
\begin{code}S := BREAK
S.code = gen(S.break,goto,_,_)
\par S := WHILE B DO S1
... as before, plus:
S1.break = S.next \end{code}
All other statement translations must pass the .break attribute through (unchanged) to their children!


Target label attributes (true,false,next) are inherited, so won't work with one-pass bottom-up code generation.

Solution: Instead, keep lists of locations of gotos that need to be filled in (``backpatched'') when final target is known. These backpatch lists are synthesized attributes.

Example (to fill in): (a > 7) OR (b = 5)
\begin{code}1. t1 := addr a
2. t2 := *t1
3. t3 := const 7
4. if t2 > t3 goto...
.... t5 := *t4
8. t6 := const 5
9. if t5 = t6 goto _____
10. goto _____\end{code}
At reduction for B := B1 OR B2

$\bullet$ Backpatch B1.false list with address of first instruction in B2.

$\bullet$ Merge B1.true B2.true to form B.true.

$\bullet$ Make B2.false into B.false.

Backpatching (Continued)

At reduction for conditional statement, backpatch true and false lists for expression.

E.g.: On reducing if B then S1 else S2, backpatch B.true to location of S1 and B.false to location of S2.

Example (to fill in):
\begin{code}IF (a > 7) OR (b = 5) THEN x := 7 ELSE y := 2;
\par 1. t1 := addr a
... goto _18__
15. t9 := const 2
16. t10 := addr y
17. *t10 := t9
18. ...\end{code}
Case Statements

case $e$\space of
$v_1$\space : $s_1$ \vert $v_2$\space : $s_2$ \vert ...
\vert $v_n$\space : $s_n$ else $s$ end\end{code}
Good code generation for case statement depends on analysis of the values on the case labels
vi .

Options include:

$\bullet$ List of conditional tests and jumps (linear search).

$\bullet$ Binary decision code (binary tree).

$\bullet$ Other search code (e.g., hash table).

$\bullet$ Jump table (constant time).

$\bullet$ Hybrid schemes.

Best option depends on range of values (min and max) and their ``density,'' i.e., what percentage of the values in the range are used as labels.

Jump tables work well for dense value sets (even if large), but waste lots of space for sparse sets. Linear search works well for small value sets.

Andrew P. Tolmach