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

Control codes

We then have a collection of different jump instructions

Control codes

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 8
  • cmpl %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 : For jg, 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 7
  • cmpl %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 jump
  • subl %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.
Last Updated: 10/19/2018, 12:38:46 PM