Code Generation: Control Flow
Lecture 12
Table of Contents
If Statements
Example
x = 2; if (cond) { x = 3; } // what is the value of x?
Informal Semantics
- Evaluate the condition
- Evaluate the body only if condition is true
Compare and jump in assembly
- Jump ops change the program counter
- Compare operator sets flag register
Jump ops check flag before jumping
cmp $0, %rax je .L0 ; additional instructions .L0: ; target of the jump
If statement template in assembly
; evaluate the condition expression ; store result in %rax cmp $0, %rax ; compare to "false" je .L0 ; jump only if false ; contents of the if branch .L0: ; first instruction after the if statement
Complete example
- (Code sample)
- (Diagram of AST)
Adding an else branch
Comparison to if statements
- Two branches instead of one
- Compare and jump to else branch instead
- Jump at end of if branch to avoid running else branch
Template
; evaluate the condition expression ; store result in %rax cmp $0, %rax ; compare to "false" je .L0 ; jump only if false to else branch ; contents of the if branch jmp .L1 ; unconditionally jump past else branch .L0: ; contents of the else branch .L1: ; first instruction after the if statement
The else-if idiom
else-if
if (cond) { } else if (cond2) { }
This is actually a nested if statement in C.
The goto fail
bug
Always put curly braces, with the exception of the else-if idiom.
While loops
- If statements with an unconditional jmp at the end
Example
cnt = 3; while (cnt) { cnt = cnt - 1; }
What does this code do?
Equivalent version ("desugared")
cnt = 3; head: if (cnt) { cnt = cnt - 1; goto head; }
Condition is always reevaluated. Can be tricky to reason about when the head has side-effects, e.g., cnt++, cnt = 2, a function call etc.
Assembly code template
.L0: ; head of the loop ; evaluate the condition expression ; store result in %rax cmp $0, %rax ; compare to "false" je .L1 ; jump only if false ; contents of the while body jmp .L0 ; unconditionally jump to loop head .L1: ; first instruction after the while statement
Notice that the head label ensures the expression is re-evaluated
Code generation tips
- Remember: compilers generate (not evaluate) code
- Generate assembly that has equivalent behavior when run
Need to generate fresh labels
- Just use a counter, .L0, .L1, etc
Generating code for expressions and statement
- codegen_ifstmt just generates the template
- Calls out to existing codegen_expr and codegen_stmt at appropriate times
Boolean operators
Overview
- Encode false as zero, true as non-zero
- One implementation strategy: use compare-and-branch
Conjunction (and)
Note that
if (A && B) { // body }
is equivalent to
if (A) { if (B) { // body } }
Implementing conjunction
; evaluate the left operand, store in %rax ; evaluate the right operand, store in %rbx cmp $0, %rax ; test left operand je .L0 ; jump only if false cmp $0, %rbx ; test right operand je .L0 ; jump only if false mov $1, %rax ; set result (%rax) to true jmp .L1 .L0: mov $0, %rax ; set result (%rax) to false .L1: ; save the result of the expression, i.e., push onto stack
Note that the result can only be set to true if both operands are not zero.
Also note the short-circuiting behavior of C Boolean expressions.
rax = A rbx = B if (rax) { if (rbx) { rax = 1 goto end; } } rax = 0; end:
Disjunction (or)
Note that
if (A || B) { // body }
is equivalent to
if (A) { goto body; } else if (B) { body: // body }
Either branch will do to execute the body
Implementing disjunction
- use
jne
(jump is not equal) instead ofje
(jump if equal)
; evaluate the left operand, store in %rax ; evaluate the right operand, store in %rbx cmp $0, %rax ; test left operand jne .L0 ; jump only if NOT false cmp $0, %rbx ; test right operand jne .L0 ; jump only if NOT false mov $0, %rax ; set result (%rax) to false jmp .L1 .L0: mov $1, %rax ; set result (%rax) to true .L1: ; first instruction after the "or" operation
Note that the result can only be set to true if both operands are not zero
Note the new opcode, jne
This implementation is using de Morgan's law to use the same implementation as conjunction: (A || B) => !(!A && !B)
Short-circuting
- C doesn't bother evaluating
Implementing negation
- How would negation work?
- Left as an exercise
Implementing relationship operators
cmp
can take two registers- Use a different jump instruction (
jge
, etc) - Can be easier to negate the op, e.g.,
(A < B)
becomes!(A >= B)
- As with
if
, we only jump if the condition is false - True-branch will then appear first in the assembly code
- As with
- x86 instructions: https://www.felixcloutier.com/x86/
Code generation tips
- Same general idea as implementing control-flow constructs
- But these are expression
- Evaluate both operands
- Store result for parent construct's use