CS 202 - Notes 2018-10-19
More x86-64 Assembly
Dealing with pointers
We looked at the swap()
function we wrote when we were first learning about pointers.
swap.c
void swap(int * xp, int * yp){
int t0 = *xp;
int t1 = *yp;
*xp = t1;
*yp = t0;
}
When we compile this (with -Og), we get the following (for these examples, I am going to trim out the assembler directives for clarity)
movl (%rdi), %eax
movl (%rsi), %edx
movl %edx, (%rdi)
movl %eax, (%rsi)
ret
We can walk through this line by line looking at the state of the variables and registers. Let us say that we have two variables x and y, and that the original call to our function looked like this:
int x = 42;
int y = 56;
swap(&x, &y);
Recalling that %rdi
is the first argument to the function and %rsi
is the second, this is what the sequence would look like:
instruction | x | y | %rdi | %rsi | %eax | %edx | description |
---|---|---|---|---|---|---|---|
42 | 56 | &x | &y | ? | ? | the function has been called | |
movl (%rdi), %eax | 42 | 56 | &x | &y | 42 | ? | %eax = Mem[%rdi] |
movl (%rsi), %edx | 42 | 56 | &x | &y | 42 | 56 | %edx = Mem[%rsi] |
movl %edx, (%rdi) | 56 | 56 | &x | &y | 42 | 56 | Mem[%rdi] = %edx |
movl %eax, (%rsi) | 56 | 42 | &x | &y | 42 | 56 | Mem[%rsi] = %eax |
Notice that putting parentheses around the register name treats its value as a pointer. So movl %eax, (%rsi)
is the exact equivalent of *yp = t0
in C.
Control flow
Control flow is based on a series of conditional and unconditional jumps (or branches, as we called them in the ∑niac). There are four control codes that we care about

We then have a collection of different jump instructions

Example:
Here is our simple conditional example
simpleConditional.c
int simpleConditional(int x, int y){
int result;
if (x > y){
result = x - y;
}else{
result = y -x;
}
result *= 3;
return result;
}
When compiled with -Og, we get:
cmpl %esi, %edi
jg .L4
subl %edi, %esi
.L3:
leal (%rsi,%rsi,2), %eax
ret
.L4:
subl %esi, %edi
movl %edi, %esi
jmp .L3
Okay, let's walk through calling this function like simpleConditional(15,8)
:
simpleConditional(5,2)
:%edi
is set to 15 and%esi
is set to 8cmpl %esi, %edi
: Subtracts%edi - %esi
, so we get 15 - 8. The result is 7, but it isn't stored. The flags are set: CF - 0, SF - 0, ZF - 0, OF - 0.jg .L4
: Forjg
, the condition flags must read~(SF ^ OF) & ~ZF
. We can break this down a little~ZF
means "not zero".(SF ^ OF)
can be read as "is negative". In more detail, "the result was negative and we didn't overflow so that is valid, or the result was positive, but we overflowed so it would have been negative". Negating that (~(SF ^ OF)
) gives us "is positive". Thus, the whole thing can be read as "the result was positive and not zero". In this instance, we have~(0 ^ 0) & ~0
=~(0) & ~0
=1 & 1
=1
. So,%edi
is greater than%esi
and we do jump (this is what I muffed in lecture).subl %esi, %edi
: Subtract%edi = %edi - %esi
.%esi
is now 7.movl %edi, %esi
:%esi = %edi
.%esi
is now equal to 7.jmp .L3
: Jump to line.L3
leal (%rsi,%rsi,2), %eax
:%eax = %rsi + 2 * %rsi
.%eax
is now equal to 21.ret
: Return from the function. (remember that%eax
is our return register)
If, on the other hand, we had called the function as simpleConditional(2, 7)
:
simpleConditional(2, 7)
:%edi
is set to 2 and%esi
is set to 7cmpl %esi, %edi
: Subtracts%edi - %esi
, so we get 2 - 7. The result is -5, but it isn't stored. The flags are set: CF - 0, SF - 1, ZF - 0, OF - 0.jg .L4
: We look at~(SF ^ OF) & ~ZF
. This time we have~(1 ^ 0) & ~0
=~(1) & ~0
=0 & 1
=0
. So we don't jumpsubl %edi, %esi
: Subtract%esi = %esi - %edi
.%esi
is now 7-2 = 5.leal (%rsi,%rsi,2), %eax
:%eax = %rsi + 2 * %rsi
.%eax
is now equal to 15.ret
: Return from the function.