Flow-of-Control Statements
Introduction: We now consider the translation of boolean expressions into three-address code in the context of statements such as those generated by the following grammar:
S if ( B ) Si
S -» if ( B ) Si else S2
S -> while ( 5 ) Si
In these productions, nonterminal B represents a boolean expression and nonterminal S represents a statement.
This grammar generalizes the running example of while expressions that we introduced in Example 5.19. As in that example, both B and S have a synthesized attribute code, which gives the translation into three-address instructions. For simplicity, we build up the translations B.code and S.code as strings, using syntax-directed definitions. The semantic rules defining the code attributes could be implemented instead by building up syntax trees and then emitting code during a tree traversal, or by any of the approaches outlined in Section 5.5.
The translation of if (B) Si consists of B.code followed by Si.code, as illustrated in Fig. 6.35(a). Within B.code are jumps based on the value of B. If B is true, control flows to the first instruction of Si.code, and if B is false, control flows to the instruction immediately following Si. code.
The labels for the jumps in B.code and S.code are managed using inherited attributes. With a boolean expression B, we associate two labels: B.true, the label to which control flows if B is true, and B.false, the label to which control flows if B is false. With a statement S, we associate an inherited attribute S.next denoting a label for the instruction immediately after the code for S. In some cases, the instruction immediately following S.code is a jump to some label L. A jump to a jump to L from within S.code is avoided using S.next.
The syntax-directed definition in Fig. 6.36-6.37 produces three-address code for boolean expressions in the context of if-, if-else-, and while-statements.
We assume that newlabelQ creates a new label each time it is called, and that label(L) attaches label L to the next three-address instruction to be generated.8
A program consists of a statement generated by P -> S. The semantic rules associated with this production initialize S.next to a new label. P.code consists of S.code followed by the new label S.next. Token assign in the production
S —> assign is a placeholder for assignment statements. The translation of assignments is as discussed in Section 6.4; for this discussion of control flow, S.code is simply assign.code.
In translating S ->• if (B) Si, the semantic rules in Fig. 6.36 create a new label B.true and attach it to the first three-address instruction generated for the statement Si, as illustrated in Fig. 6.35(a). Thus, jumps to B.true within the code for B will go to the code for Si. Further, by setting B.false to S.next, we ensure that control will skip the code for Si if B evaluates to false.
In translating the if-else-statement S if (B) Si else S2 , the code for the boolean expression B has jumps out of it to the first instruction of the code for 51 if B is true, and to the first instruction of the code for S2 if B is false, as illustrated in Fig. 6.35(b). Further, control flows from both Si and S2 to the three-address instruction immediately following the code for S — its label is given by the inherited attribute S.next. An explicit goto S.next appears after the code for Si to skip over the code for S2 . No goto is needed after S2 , since Si.next is the same as S.next.
The code for S —>• while (B) Si is formed from B.code and Si.code as shown in Fig. 6.35(c). We use a local variable begin to hold a new label attached to the first instruction for this while-statement, which is also the first instruction for B. We use a variable rather than an attribute, because begin is local to the semantic rules for this production. The inherited label S.next marks the instruction that control must flow to if B is false; hence, B.false is set to be S.next. A new label B.true is attached to the first instruction for Si; the code for B generates a jump to this label if B is true. After the code for Si we place the instruction goto begin, which causes a jump back to the beginning of the code for the boolean expression. Note that Si.next is set to this label begin, so jumps from within Si.code can go directly to begin.
The code for S -» Si S2 consists of the code for Si followed by the code for S2. The semantic rules manage the labels; the first instruction after the code for Si is the beginning of the code for S 2; and the instruction after the code for 52 is also the instruction after the code for S.