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):%ediis set to 15 and%esiis 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~ZFmeans "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,%ediis greater than%esiand we do jump (this is what I muffed in lecture).subl %esi, %edi: Subtract%edi = %edi - %esi.%esiis now 7.movl %edi, %esi:%esi = %edi.%esiis now equal to 7.jmp .L3: Jump to line.L3leal (%rsi,%rsi,2), %eax:%eax = %rsi + 2 * %rsi.%eaxis now equal to 21.ret: Return from the function. (remember that%eaxis our return register)
If, on the other hand, we had called the function as simpleConditional(2, 7):
simpleConditional(2, 7):%ediis set to 2 and%esiis 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.%esiis now 7-2 = 5.leal (%rsi,%rsi,2), %eax:%eax = %rsi + 2 * %rsi.%eaxis now equal to 15.ret: Return from the function.