UP | HOME

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

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 of je (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
  • 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

Author: Paul Gazzillo

Created: 2024-04-04 Thu 10:32

Validate