CS 208 s22 — x86-64 Control Flow: Conditionals

Table of Contents

1 Review

What effect do these instructions

:
    leaq (%rdx,%rcx,4), %rax
    movq (%rdx,%rcx,4), %rbx
    leaq (%rdx), %rdi
    movq (%rdx), %rsi

have on register values give the initial state below?1

topic-10-review-initial.png

2 208 Reference Sheet

3 Jump Instructions

  • x86 uses a dedicated register to store the address of the next instruction to execute
    • called the instruction pointer or %rip
    • When an instruction executes, %rip is updated to be the address of the following instruction
  • Operations in C that jump to a different line of code (e.g., conditionals, loops, function calls) all involve changing which instruction will execute next—this is done by changing the value in %rip
  • class of jump instructions move program execution to specified instruction (figure 3.15, p. 206)
  • direct jumps encode the jump target in the instruction
  • indirect jumps read the jump target from register or memory
    • written with a * preceding the operand
  • conditional jumps (can only be direct) jump or continue the subsequent instruction based on some combination of condition codes
Instruction     (op) s, d test a, b cmp a, b
jmp   Unconditional      
je sete "Equal" d (op) s == 0 b & a == 0 b == a
jne setne "Not equal" d (op) s != 0 b & a != 0 b != a
js sets "Sign" (negative) d (op) s < 0 b & a < 0 b - a < 0
jns setns (non-negative) d (op) s >= 0 b & a >= 0 b - a >= 0
jg setg "Greater" d (op) s > 0 b & a > 0 b > a
jge setge "Greater or equal" d (op) s >= 0 b & a >= 0 b >= a
jl setl "Less" d (op) s < 0 b & a < 0 b < a
jle setle "Less or equal" d (op) s <= 0 b & a <= 0 b <= a
ja seta "Above" (unsigned >) d (op) s > 0U b & a > 0U b - a > 0U
jb setb "Below" (unsigned <) d (op) s < 0U b & a < 0U b - a < 0U

3.1 Implementing Conditional Branches with Conditional Control

if (<text-expr>)
    <then-statement>
else
    <else-statement>

becomes

    t = <test-expr>;
    if (!t)
        goto false;
    <then-statement>
    goto done;
false:
    <else-statement>
done:

3.1.1 Example

long absdiff(long x, long y) {
    long result;
    if (x > y)
        result = x - y;
    else
        result = y - x;
    return result;
}
Register Use
%rdi 1st argument (x)
%rsi 2nd argument (y)
%rax return value
absdiff:
        cmpq %rsi, %rdi   // perform x - y
        jle .L4           // if condition codes indicate x <= y, jump to L4
                          // x > y:
        movq %rdi, %rax
        subq %rsi, %rax
        ret
    .L4:                  // x <= y:
        movq %rsi, %rax
        subq %rdi, %rax
        ret

3.2 (OPTIONAL) Implementing Conditional Branches with Conditional Moves

  • Reading: CSPP section 3.6.6
  • class of cmov instructions copy source operand to destination register when the condition holds
    • same conditions as set and jump instructions
  • can be more efficient than conditional jumps because the sequence of intructions is predictable
    • memory may or may not be moved, but the subsequent instruction is executed regardless
    • processors pipeline instructions, meaning they process them in a series of stages (fetching instruction from memory, determining instruction type, reading from memory, performing arithmetic operation, writing to memory, updating program counter)
    • significant performance benefits from overlapping different stages of sequential instructions
    • pipelining in the presence of conditional jumps requires sophisticated branch prediction (modern processors aim for 90% accuracy)
  • turn on optimization for previous exercise (-O1 compiler flag) to see an example

4 Conditional Exercise

Translate this C code to assembly2

long wacky(long x, long y) {
    long result;
    if (x + y > 7) {
        result = x;
    } else {
        result = y + 2;
    }
    return result;
}

4.1 Jump Instruction Encodings

If we compile wacky and disassemble it, we can see how jump instructions are encoded into machine code3:

0000000000400497 <wacky>:
  400497:       48 8d 04 37             lea    (%rdi,%rsi,1),%rax
  40049b:       48 83 f8 07             cmp    $0x7,%rax
  40049f:       7f 05                   jg     4004a6 <wacky+0xf>
  4004a1:       48 8d 46 02             lea    0x2(%rsi),%rax
  4004a5:       c3                      retq
  4004a6:       48 89 f8                mov    %rdi,%rax
  4004a9:       c3                      retq
  • jump target encoded compactly with instruction pointer relative representation
    • specify offset to the address of the immediately following instruction
    • 7f is the encoding for the jg instruction
    • 05 is the jump target, meaning it will add 0x5 to %rip (the instruction pointer) if it jumps
    • when we execute the jg instruction, %rip is set to the address of the next instruction, 0x4004a1. Adding 0x5 to this will result in executing the mov instruction at 0x4004a6 instead.
    • objdump, the disassembler I used to produce the above example, computes this for us and displays 0x4004a6 as the jump target even though in the machine code the target is encoded as 0x5

5 Condition Codes

  • condition codes are the basis for control flow within a program (i.e., determining which instruction gets executed next)
    • special %rip register stores instruction pointer
  • most useful codes:
    • CF: carry flag. Set when an operation generated a carry out of the most significant bit. Used to detect unsigned overflow
    • ZF: zero flag. Set when an operation yields 0
    • SF: sign flag. Set when an operation yields a negative value
    • OF: overflow flag. Set when an operation causes a two's complement overflow, negative or positive
  • all the standard arithmetic instructions set these flags except for leaq
  • cmp and test instructions set condition codes
    • cmp subtracts first operand from second
    • test ands operands (usually the same quantity is both operands, check if its zero, positive, negative)

jumps.png

5.1 Accessing the Condition Codes

  • class of set instructions to set a single byte to 1 or 0 based on one or more condition codes (figure 3.14, p. 203)
    • in the case where cmp b, a was executed
    • when \(a=b\), then \(a-b=0\), so ZF will be set
    • when \(a < b\), then \(a - b < 0\) (SF set) or \(a - b\) overflows so the result is positive and OF is set
      • but both SF and OF set would indicate \(a\) is positive and \(b\) is negative such that \(a-b\) would overflow to a negative value
      • hence, less than is detected when SF ^ OF
    • for unsigned \(a < b\), \(a - b\) would cause a carry out of the most significant bit, setting CF

5.2 Example

5.2.1 Translate conditional C code to assembly

long val_or_default(long *x, long y) {
    if (x == NULL) {
        return y;
    }
    return *x;
}
Register Use
%rdi 1st argument (x)
%rsi 2nd argument (y)
%rax return value
val_or_default:
        testq   %rdi, %rdi
        je      .L4
        movq    (%rdi), %rax
        ret
.L4:
        movq    %rsi, %rax
        ret

6 movz and movs

  • when moving data from a smaller source to a larger destination, can specify zero extension or sign extension
    • destination must be a register
  • movzSD / movsSD
    • S is the size of the source (b or w)
    • D is the size of the destination (w, l, or q)

movz-example.png

7 Which condition codes indicate \(a > b\)?

  • not less than and not equal
  • ~(SF ^ OF) and ~ZF
  • ~(SF ^ OF) & ~ZF

8 Practice

CSPP practice problems 3.16 (p. 212) and 3.18 (p. 213)

Footnotes:

1

topic-10-review-answers.png

2
wacky:
        leaq    (%rdi,%rsi), %rax
        cmpq    $7, %rax
        jg      .L3
        leaq    2(%rsi), %rax
        ret
.L3:
        movq    %rdi, %rax
        ret
3

Perform this disassembly via an option on godbolt.org

godbolt-compile-to-binary.png

or, by putting the code for wacky and an empty main in wacky.c and running these commands:

gcc -Og -no-pie -o wacky wacky.c
objdump -d wacky

objdump will print out a bunch of boilerplate assembly that's part of any program, so you'll need to locate the definition of wacky within it.