CS 202 - Notes 2018-10-19

x86-64 Assembly: Control Flow

Control Flow

Control flow is governed by four control flags in the processor

CF - carry flag :This is set when the operation has a carry out of the last bit. It indicates an overflow if the numbers were unsigned

ZF - zero flag :The most recent operation yielded a 0

SF - sign flag :The result of the most recent operation was negative

OF - overflow flag :Set if there was two’s compliment overflow (positive or negative)

These can be set explicitly or implicitly. They are set implicitly by almost every arithmetic operation we perform. The exception is the lea function, which was meant for address computation

we can set them explicitly using:

  • cmp - compare two values (essentially subtracts the first from the second)
  • test - ANDs the two arguments together. If they are the same, this will be a quick way to check if it is 0 or negative

if statements

C code:

	int simpleConditional(int x, int y){
	  int result;
	
	  if (x > y){
		result = x - y;
	  }else{
		result = y - x;
	  }
	
	  result *= 3;
	  
	  return result;
	}

Assembly:

	cmpl	%esi, %edi
	jg	.L4
	subl	%edi, %esi
.L3:
	leal	(%rsi,%rsi,2), %eax
	ret
.L4:
	subl	%esi, %edi
	movl	%edi, %esi
	jmp	.L3

do while loops

As we observed in class, the do while loop really is the simplest loop since it requires a single conditional branch at the bottom of the loop to return to the top of the condition is met.

C code:

	int doWhile(int x, int y){
	
	  int result;
	
	  do {
		result += x;
		y--;
	  }while (y > 0);
	
	  return result;
	}

Assembly:

	movl	$0, %eax
.L2:
	addl	%edi, %eax
	subl	$1, %esi
	testl	%esi, %esi
	jg	.L2
	rep ret

As an interesting aside, if we turn on optimization, the compiler will actually detect what this function is trying to do (multiplication) and remove the loop in favor of the multiplication.

Assembly (-O2)

	testl	%esi, %esi
	movl	$1, %eax
	cmovg	%esi, %eax
	imull	%edi, %eax
	ret

while loops

There is a very little difference between do while and while loops. A do while loop goes: init, body, update, test, body, update, test, body, update, test, etc... A while loop goes: init, test, body, update, test, body, update, test, body, update, etc... The difference is that the while loop does a test before it starts, or to put that another way, the do while does an iteration of body and update before it starts.

There are a couple of ways that we can translate a while loop into a do while loop.

jump to the middle : We start by unconditionally jumping down to the the test

guarded do : We just add a test before the do while loop that jumps over the loop

Different levels of optimization or different loops may result in one or the other of these approaches, but we will largely see the first.

C code:

int whileLoop(int x, int y){
  int result;

  while (y > 0) {
    result += x;
    y--;
  }

  return result;
}

Assembly :

	movl	$0, %eax
	jmp	.L2
.L3:
	addl	%edi, %eax
	subl	$1, %esi
.L2:
	testl	%esi, %esi
	jg	.L3
	rep ret

Comparing this to the do while loop, you will see that the only difference is the addition of a single line (jmp .L2).

for loops

This is basically pretty syntax for the same behavior as a while loop, so it can be converted the same way.


for (init; test; update){
  body;
}

init;
while(test){
  body; 
  update;
}

C code:

int forLoop(int x, int y){
  int result = 0;

  for (; y > 0; y--){
	result +=x;
  }

  return result;
}

Assembly :

	movl	$0, %eax
	jmp	.L2
.L3:
	addl	%edi, %eax
	subl	$1, %esi
.L2:
	testl	%esi, %esi
	jg	.L3
	rep ret

If you compare that to the assembly for the while loop, you will see it is identical.

Supporting Procedures

We started to look at procedures today.

Basic concerns:

  • Data management

    • need to be able to pass arguments
    • Need to return a value
  • Control flow

    • Need to be able to jump to the procedure
    • Need to be able to return to the instruction immediately after the call when we are done
  • Memory management

    • Need to be able to create local variables
    • Need local variables to go away when we are done (talk to someone who knows some JavaScript for some more complicated forms of this)
    • Local variables of the caller need to be preserved
    • Functions must be re-entrant (we need to be able to enter a second time before the first invocation is complete -- see recursion)

    We observed that we know how to do some of these things.

    Values from functions are return in %rax (or %eax, etc depending on data size)

    Parameters are passing into function in registers (%rdi, %rsi, etc...). However, this is only a partial solution since we only have six designated registers.

    We saw the ret command, but we don't know how it works yet.

Last Updated: 10/22/2018, 3:45:21 PM