Lecture 08 Conditional Control Flow

Joseph Haugh

University of New Mexico

Summary

  • History of Intel processors and architectures
    • C, assembly, machine code
      • New forms of visible state: program counter, registers, …
      • Compiler must transform statements, expressions, procedures into low-level instruction sequences
  • Assembly Basics: Registers, operands, move
    • The x86-64 move instructions cover wide range of data movement forms
  • Arithmetic
    • C compiler will figure out different instruction combinations to carry out computation

Learning Objectives

  • Describe condition codes (sec. 3.6.1)
  • Summarize how to set and use condition codes
  • Describe how are conditional branches implemented in assembly code (If-then-else statements)
  • Describe and practice the translation of loops and the switch statement into assembly code

Processor State (x86-64, Partial)

  • Information about currently executing program
    • Temporary data ( %rax, … )
    • Location of runtime stack ( %rsp )
    • Location of current code control point ( %rip, … )
    • Status of recent tests (CF, ZF, SF, OF)

Implicitly Setting Condition Codes

  • Single bit registers:
    • CF, carry flag (unsigned) | SF, sign flag (signed)
    • ZF, zero flag | OF, overflow flag (signed)
  • Implicitly set (think of it as side effect) by arithmetic operations
  • Example: addqSrc, Dest ↔︎ t = a + b
    • CF set if carry out from most significant bit (unsigned overflow)
    • ZF set if t == 0
    • SF set if t < 0 (as signed)
    • OF set if two’s-complement (signed) overflow
      • (a>0 && b>0 && t<0) || (a<0 && b<0 && t>=0)
  • Not set by leaq instruction

Explicitly Setting Condition Codes

  • Two instructions:
    • cmp_
    • test_
    • Full set of 8 instructions is in fig 3.13

Compare Instruction

  • Explicit Setting by Compare Instruction:
    • cmpq b,a like computing a-b without setting destination
  • CF set if carry out from most significant bit (used for unsigned comparisons)
  • ZF set if a == b
  • SF set if (a-b) < 0 (as signed)
  • OF set if two’s-complement (signed) overflow
    • (a>0 && b<0 && (a-b)<0) || (a<0 && b>0 && (a-b)>0)

Test Instruction

  • Explicit Setting by Test instruction:
  • testq b,a like computing a&b without setting destination
  • Sets condition codes based on value of b&a
  • Useful to have one of the operands be a mask
  • ZF set when a&b == 0
  • SF set when a&b < 0

Reading Condition Codes

  • Set Instructions:
    • Set low-order byte of destination to 0 or 1 based on combinations of condition codes
    • Does not alter remaining 7 bytes
SetX Condition Description
sete ZF Equal / Zero
setne ~ZF Not Equal / Not Zero
sets SF Negative
setns ~SF Nonnegative
setg ~(SF^OF)&~ZF Greater (Signed)
setge ~(SF^OF) Greater or Equal (Signed)
setl (SF^OF) Less (Signed)
setle (SF^OF)|ZF Less or Equal (Signed)
seta ~CF&~ZF Above (Unsigned)
setb CF Below (Unsigned)

Learning Objectives

  • Describe condition codes (sec. 3.6.1)
  • Summarize how to set and use condition codes
  • Describe how are conditional branches implemented in assembly code (If-then-else statements)
  • Describe and practice the translation of loops and the switch statement into assembly code

Reading Condition Codes

  • SetX Instructions:
    • Set single byte based on combination of condition codes
  • One of addressable byte registers
    • Does not alter remaining bytes
    • Typically use movzbl to finish job
      • 32-bit instructions also set upper 32 bits to 0
Register Use
%rdi Argument x
%rsi Argument y
%rax Return value
int gt (long x, long y)
{
  return x > y;
}
cmpq   %rsi, %rdi   # Compare (x-y)
setg   %al          # Set when >
movzbl %al, %eax    # Zero rest of %rax
ret

Practice: Problem 3.13 A

int comp(data_t a, data_t b) {
  return a COMP b;
}

Suppose a is in some portion of %rdi and b in %rsi, which data type data_t and which comparison COMP could cause the compiler to generate this code?

cmpl %esi, %edi
setl %al

Practice: Problem 3.13 A

int comp(data_t a, data_t b) {
  return a COMP b;
}

Suppose a is in some portion of %rdi and b in %rsi, which data type data_t and which comparison COMP could cause the compiler to generate this code?

cmpl %esi, %edi  # a - b
setl %al         # if a < b, 
                 # set first byte of %rax
  • data_t is Int
  • COMP is <

Practice: Problem 3.13 B

int comp(data_t a, data_t b) {
  return a COMP b;
}

Suppose a is in some portion of %rdi and b in %rsi, which data type data_t and which comparison COMP could cause the compiler to generate this code?

cmpw  %si, %di
setge %al

Practice: Problem 3.13 B

int comp(data_t a, data_t b) {
  return a COMP b;
}

Suppose a is in some portion of %rdi and b in %rsi, which data type data_t and which comparison COMP could cause the compiler to generate this code?

cmpw  %si, %di # a -b
setge %al      # if a >= b, 
               # set first byte of %rax
  • data_t is Short
  • COMP is >=

Practice On Your Own

  • Rest of 3.13
  • 3.14
    • Note that 3.14 D has an error so check the errata

Summary: Condition Codes

  • Ultimate goal of the condition code (CC) registers: implement conditional branches in the assembly code
  • So, instead of reading them directly, they are used to:
    • set a single byte to 0 or 1, depending on a combination of CCs (sec. 3.6.2)
    • conditional jump to some other part of the program (sec. 3.6.3 to 3.6.5)
    • conditional transfer (move) of data (sec. 3.6.6)

Summary: Condition Codes

  • Four single-bit condition code registers
    • CF, ZF, SF, and OF
  • These are set after an arithmetic or logical operation is executed (applies to the most recent)
  • Except for leaq all instructions in fig. 3.10 p. 192 set the condition codes
  • Special cases:
    • Logical ops. set CF and OF to zero
    • shift ops. CF set to the last bit shifted out and OF to 0
    • INC and DEC ops. set OF and ZF, leave CF unchanged

Jumping

  • j instructions (fig. 3.15)
    • Jump to different parts of the code depending on condition codes
jX Condition Description
jmp 1 Unconditional
je ZF Equal / Zero
jne ~ZF Not Equal / Not Zero
js SF Negative
jns ~SF Nonnegative
jg ~(SF^OF)&~ZF Greater (Signed)
jge ~(SF^OF) Greater or Equal (Signed)
jl (SF^OF) Less (Signed)
jle (SF^OF)|ZF Less or Equal (Signed)
ja ~CF&~ZF Above (Unsigned)
jb CF Below (Unsigned)

Direct vs Indirect Jumps

  • A direct jump is a jump to a label in your code

      movq $0,%rax      # Set %rax to 0
      jmp .L1           # Goto .L1
      movq (%rax), %rdx # Null pointer dereference (skipped)
    .L1:
      popq %rdx         # Jump target
  • An indirect jump is a jump

    • Uses a * before the argument
    jmp *%rax   # jump to the value in %rax
    jmp *(%rax) # jump to the value in memory at the address in %rax

Jump Instruction Encoding

  • How jump instructions are encoded will become very important in chapter 7
  • Jumps can be encoded in two different ways:
    • Absolute: the address to jump to is given directly using 4 bytes
    • PC Relative: the address to jump to is encoded using the difference between the target address and the address of the next instruction

PC Relative

  • True jump target encoded as: jmp target + next address
1	  movq	%rdi, %rax
2	  jmp	.L2
3	.L3:
4	  sarq	%rax
5	.L2:
6	  testq	%rax, %rax
7	  jg	.L3
8	  rep; ret
1	0:	48 89 f8	mov	%rdi,%rax
2	3:	eb 03		jmp	8 &lt;loop+0x8&gt;
3	5:	48 d1 f8	sar	%rax
4	8:	48 85 c0	test %rax,%rax
5	b:	7f f8		jg	5 &lt;loop+0x5&gt;
6	d:	f3 c3		repz retq
  • First jump: 0x03 + 0x05 = 0x08
  • Second jump: 0xf8 + 0x0d = 0x05

Practice: Problem 3.15

  • What is the target of the je instruction below?
4003fa: 74 02   je XXXXXX
4003fc: ff d0   callq *%rax

Practice: Problem 3.15

  • What is the target of the je instruction below?
    • 4003fc + 2 = 4003fe
4003fa: 74 02   je 4003fe
4003fc: ff d0   callq *%rax

Practice: Problem 3.15

  • What is the target of the je instruction below?
40042f: 74 f4    je XXXXXX        
400431: 5d       pop  %rbp

Practice: Problem 3.15

  • What is the target of the je instruction below?
    • 400431 + f4 = 400425
    • 0xf4 = -12
    • 400431 - C
4003fa: 74 02   je 400425
4003fc: ff d0   callq *%rax

Practice: Problem 3.15

  • What is the address of the je and pop instruction below?
XXXXXX: 77 02   je 400547
YYYYYY: 5d      pop %rbp

Practice: Problem 3.15

  • What is the target of the je instruction below?
    • YYYYYY + 2 = 400547 thus, YYYYYY = 400545
    • XXXXXX = 400545 - 2 = 4000543 (why - 2?)
XXXXXX: 77 02   je 400547
YYYYYY: 5d      pop %rbp

Learning Objectives

  • Describe condition codes (sec. 3.6.1)
  • Summarize how to set and use condition codes
  • Describe how are conditional branches implemented in assembly code (If-then-else statements)
  • Describe and practice the translation of loops and the switch statement into assembly code

Conditional Branching

  • Generating:

    b146> gcc –Og -S –fno-if-conversion control.c
long absdiff (long x, long y)
{
  long result;
  if (x > y)
    result = x-y;
  else
    result = y-x;
  return result;
}
absdiff:
  cmpq    %rsi, %rdi # x - y
  jle     .L2
  movq    %rdi, %rax
  subq    %rsi, %rax
  ret
.L2:                 # x <= y
  movq    %rsi, %rax
  subq    %rdi, %rax
  ret

Expressing With Goto

  • C has a goto statement
  • Jump to position designated by label
long absdiff (long x, long y)
{
  long result;
  if (x > y)
    result = x-y;
  else
    result = y-x;
  return result;
}
long absdiff_j (long x, long y)
{
    long result;
    int ntest = x <= y;
    if (ntest) goto Else;
    result = x-y;
    goto Done;
 Else:
    result = y-x;
 Done:
    return result;
}

If-Then-Else Translation

  • An if statement of the form:
if (test-expr) {
  then-stmts
}
else {
  else-stmts
}
  • Translates to:
  t = test-expr;
  if (!t)
    goto False;
  then-stmts
  goto Done;
False: 
  else-stmts
Done:
  ...

Practice Problems

  • Problem 3.16
  • Problem 3.17
  • Problem 3.18

Alternative Translation

  • When we are doing an if to conditionally set a variable, we can instead opt to use a conditional move
  • Fig. 3.18 lists conditional move instructions
  • Should only be used in restricted cases as it can have undesirable results. See bad cases slide for more details

General Conditional Expression Translation: Using Branches

val = test-expr ? then-expr : else-expr;
  t = !test-expr
  if (t)
    goto Else;
  val = then-expr;
  goto Done;
Else:
  val = else-expr;
Done:
  ...

General Conditional Expression Translation: Using Conditional Moves

val = test-expr ? then-expr : else-expr;
  • Conditional Move Instructions
    • Instruction supports:
    • if (Test) Dest Src
    • Supported in post-1995 x86 processors
    • GCC tries to use them
      • But, only when known to be safe
result = then-expr;
eval = else-expr;
nt = !test-expr
if (nt) result = eval;
return result;
  • Why use them?
    • Branches are very disruptive to instruction flow through pipelines
    • Conditional moves do not require control transfer

Conditional Move Example

long absdiff (long x, long y)
{
    long result;
    if (x > y)
        result = x-y;
    else
        result = y-x;
    return result;
}
Register Use
%rdi Argument x
%rsi Argument y
%rax Return Value
absdiff:
   movq    %rdi, %rax  # x
   subq    %rsi, %rax  # result = x-y
   movq    %rsi, %rdx
   subq    %rdi, %rdx  # eval = y-x
   cmpq    %rsi, %rdi  # x:y
   cmovle  %rdx, %rax  # if <=, result = eval
   ret

Bad Cases For Conditional Move

  • Expensive Computations:

    val = Test(x) ? Hard1(x) : Hard2(x);
    • Makes sense when computations are simple
  • Risky Computations

    val = p ? *p : 0;
    • May have undesirable effects
  • Computations With Side Effects

    val = x > 0 ? x *= 7 : x += 3;
    • Must be side effect free

Practice: Problem 3.21