25
What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope C Flow Control David Chisnall February 1, 2011

2 Program Structure

  • Upload
    savio77

  • View
    220

  • Download
    0

Embed Size (px)

Citation preview

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

C Flow Control

David Chisnall

February 1, 2011

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Outline

What the CPU Sees

Basic Flow Control

Conditional Flow Control

Structured Flow Control

Functions and Scope

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Disclaimer!

• These slides contain a lot of x86 assembly!

• If you don’t understand the details, don’t worry

• They are there to give you an idea of how the compiler works

• You aren’t expected to remember the details

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Machine Code Execution

1. CPU reads an instruction from address in program counterregister

2. CPU increments program counter

3. CPU executes instruction

4. Repeat from step 1

Author's Note
Comment
In the simplest case, the CPU is just reading instructions in the same way that you'd read instructions in a recipe - read one, do what it says, then read the next one. The next instruction is (by default) the one in memory immediately after the current one.

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Nonlinear Code Execution

• Instruction modifies program counter as part of operation

• Simplest case: jump instruction, just sets program countervalue.

• Exposed in C as goto statement.

Author's Note
Comment
In a nontrivial program, you have branches. These work by having some instructions that, as a side effect of execution, set the address from which to read the next instruction. At the CPU level, this is often all of the flow control that you have - all flow control constructs in any language are built on top of this.

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

C goto Statement

�// This loops forever

int main(void)

{

// Jump target

start:

goto start;

return 0;

} � �• The label defines a location in the instruction stream

• The goto statement says ‘jump to this label’

Author's Note
Comment
The goto statement is the simplest possible flow control construct, it unconditionally makes execution jump to some other place in the program. In this example, it jumps to just before the goto statement, so it infinite loops.

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Compiling goto

�.globl _main

_main: # Export main function

L2: # Define a label

jmp L2 # Jump to the label � �• Structure of C is obvious in generated assembly

• Each C line maps to exactly one line of assembly (in this case)

Important Difference: The jmp instruction can take any value. Thegoto statement can only take a label defined in this scope.

Author's Note
Comment
This listing is the C source listing from the previous slide, compiled or x86. Note that the goto statement has been compiled to a single instruction. The C code directly maps to the assembly, and the assembly maps trivially to machine instructions.

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Conditional Jumps

• Similar to a jump instruction

• Sets the program counter, but only if the condition is met

Author's Note
Comment
Goto is not useful by itself, for anything nontrivial you need code that branches - does different things based on some input data. Most CPUs have a small set of conditional jump instructions. These are similar to the jump instructions used for goto, but they only set the program counter if some condition is true, typically a condition flag set as the result of the last arithmetic instruction.

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Conditional Jumps

�int b = 0;

if (a > 0)

{

b = a;

} � �• if statement defines a conditional.

• The next statement is only executed if the condition is true

• The next ‘statement’ is often a block of code

• Another way of thinking about if: Jump over this statement ifthe condition if false

Author's Note
Comment
The if statement in C is very similar to the Java version. The only difference is that it takes an integer or pointer expression, and regards any value other than 0 as true, while Java has an explicit boolean type. The implementation of the if statement is typically the inverse of what is written - rather than executing the block if the condition is true, it skips it if the condition is false. These are equivalent, but the former makes more sense to humans and the later more sense to computers.

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Compiling if

�movl $0, -12(%ebp) # Store 0 in b

cmpl $0, 8(% ebp) # Compare 0 to a

jle L2 # Jump if <=

movl 8(% ebp), %eax # Load a into register

movl %eax , -12(%ebp)# Store register into b

L2: � �• Compare instruction sets condition flags.

• Conditional jump sets program counter if some of these flagsare set

Note: Assignment is two instructions, load value into a register,then store it into memory. C lets you avoid having to think aboutregister allocation.

Author's Note
Comment
The %ebp stuff is how x86 does stack-relative addressing. The two variables are both on the stack, and are identified by their offsets from the stack pointer. Again, this maps trivially from C to assembly. You can easily see the structure of the C in the assembly.

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Loops

• for, while, and do...while loops

• All implemented as test-and-jump-to-start

• switch statements for choices

• Implemented jump table or as nested conditionals

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Functions

• Decompose programs into subprograms

• One entry point, one or more exits

• Take parameters, return at most one value

• Java equivalent: Methods

Author's Note
Comment
Functions / procedures / subroutines are needed for structured programming. They allow you to decompose a program into smaller subprograms. Like a program, they have a single entry point and one or more exit points.

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

How Functions Work

1. Save current instruction pointer

2. Jump to address of new function

3. Do stuff in that function

4. Load old instruction pointer

Author's Note
Comment
Some CPUs combine some of these steps into single instructions. Implementing functions is quite simple - you need to provide the function arguments and return address somewhere that the function can access them (typically in registers or on the stack), then jump to the start of the function. The function then processes the arguments, and jumps back to the return address.

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Emulating Functions With gotoDon’t ever do this in real code!�

void *stack [1024]; // Call stack

int stackDepth = 0; // Stack pointer

stack[stackDepth ++] = &&ret1; // Store

return

goto function; // Call function

ret1:

printf("Returned\n");

return 0;

function:

printf("Called Function\n");

stack[stackDepth ++] = &&ret2;

goto function2;

ret2:

goto *stack[--stackDepth ];

function2:

printf("Called Function2\n");

goto *stack[--stackDepth ]; � �

Author's Note
Comment
If C didn't have functions, you'd end up with code that looks like this. This implements a simple call stack in C using GCC's address-of-label extension. It's horrible and unreadable, which is why functions are important. Without them, all code would look this bad!

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

And This is Why goto is Considered Harmful!

• The goto statement reflects how the machine works

• Humans don’t really think like that though...

• Structured programming makes it much easier to understandthe program design

Author's Note
Comment
Modern compilers do things like trace-based optimisation, where they combine paths through several function calls into a single stream of instructions, but

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Function Syntax

�// Returns int , takes two ints as arguments

int foo(int a, int b)

{

return a + b;

}

// Returns int , takes nothing as an argument

int bar(void)

{

return foo(1, 2);

} � �

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Compiling a Function�_foo: # Entry point

pushl %ebp # Store frame pointer

movl %esp , %ebp # Set frame pointer

subl $8 , %esp # Allocate 8 bytes on

stack

movl 12(% ebp), %eax # load a

addl 8(% ebp), %eax # add b to a

leave # restore stack

ret # return to caller

_bar:

pushl %ebp # Store frame pointer

movl %esp , %ebp # Set frame pointer

subl $24 , %esp # Allocate 24 bytes

movl $2 , 4(% esp) # Store 2 on stack

movl $1 , (%esp) # Store 1 on stack

call _foo # Call foo() � �

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

x86 Peculiarities

• Many architectures don’t have ret and leave instructions.

• Stack is explicitly managed

• Return address pushed onto the stack (or stored in register,e.g. SPARC)

• jmp instruction used to return

• On some architectures, leaf functions are faster

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Allocation on the Stack

• Very fast!

• Just change a value in a register

• Only for small amounts of data

• Typical stack size is under 1MB

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Stack and Cache

• Reading data from main memory takes 150+ cycles

• Reading data from cache takes 2-10 cycles

• The top of the stack is (almost) always in cache

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Stack Allocations and Scope

�int foo(int arg)

{

int a = 12;

if (arg > a)

{

int b = arg;

// c is not valid yet

...

int c; // c is valid from here

...

} // b and c are invalid after this point

} // a is invalid after here � �

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Scope and Lifecycle

• Variables are bits of memory

• The memory for stack variables is freed when they go out ofscope

• What about pointers?

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Pointers are (still) Just Numbers

�{

// Allocate space for 100 ints

int a* = malloc (100* sizeof(int));

} // a goes out of scope � �• A pointer going out of scope frees the pointer

• It does not free the pointee

• This is an example of a memory leak

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Another Example

�int *a = NULL;

if (someCondition)

{

// b is on the stack

int b = someCalculation ();

// a points to b

a = &b;

} // b goes out of scope

// a now points to an invalid pointer � �• Freeing the pointee does not change the pointer

• This is an example of a dangling pointer

What the CPU Sees Basic Flow Control Conditional Flow Control Structured Flow Control Functions and Scope

Summary

• All C flow control is a thin wrapper around machineinstructions

• The stack is a hardware construct, used for storing returnaddresses and temporary state