Content:

* Function call
* Function execution (prologue and epilogue)
* Speed up the virtual machine


* Function call

Function arguments are pushed on the stack in the reverse order (right to left)

/* a function prototype */
string func(int arg1, float arg2, int arg3);

/* a function call */
{
	int ret;
	...
	ret = test(a, 2.1, 15);
}

The sequence of assembler instructions to put arguments on the stack.

	push	$15
	push	$2.1
	load	a


     0xffffffff
   -------------
  |             |
   -------------
  |     arg3    |
   -------------
  |     arg2    |   
   -------------
  |     arg1    |   
   ------------- <---- %sp
  |		|
   -------------
     0x00000000

Call a function after putting arguments.

	call test

     0xffffffff
   -------------
  |		|
   ------------- 
  |     arg3    |
   -------------
  |     arg2    |
   -------------
  |     arg1    |
   -------------
  | ret address |
   ------------- <---- %sp
  |		|
   -------------
     0x00000000

A call instruction puts the returning address on the stack and change the %pc
register with a function address.



* Function execution (prologue and epilogue)

Each function starts en execution with a prologue. A prologue saves the
previous value of %fp register and fixes a new value of %fp. When we will fix
a new %fp we can address all variables and arguments in the function and don't
care about %sp. We known only that %sp points to the last operation result.

	push	%fp
	push	%sp
	pop	%fp

     0xffffffff
   -------------
  |             |
   ------------- 
  |     arg3    |
   ------------- 
  |     arg2    |
   ------------- 
  |     arg1    |
   -------------
  | ret address	|
   -------------
  |	%fp	| - previous frame pointer
   ------------- <---- %sp (new fixed %fp, %fp == %sp)
  |		|
   -------------
     0x00000000

At this point we shrink the stack size to  prepare a place for function
variables. We shrink and grow the stack size each time when come to the new
scope or leave it respectively. Since the stack grows to the less addresses
we need to shrink it and vice versa.

In our example we need place for three 4-bytes variables.

	push	%sp
	push	$12 /* size of all variables */
	sub
	pop	%sp
	

     0xffffffff
   -------------
  |             |
   -------------
  |	arg3	|
   -------------	+16(%fp)
  |	arg2	|
   -------------	+12(%fp)
  | 	arg1	|
   -------------	+8(%fp)
  | ret address |
   ------------- 	+4(%fp)
  |	%fp	|
   ------------- <---- %fp
  | local var1  |
   -------------        -4(%fp)
  | local var2  |
   -------------  	-8(%fp)
  | local var3  |
   -------------	-12(%fp)
  |		|
   -------------
     0x00000000

Parts described above are common for all functions. After that a function is
executed by a virtual machine. Most instructions use the stack as a holder for
their operands. The stack in the virtual machine is balanced it means that it
can't be overflowed. Most instrutions pick operands, poke the result and change
%sp value.


If a function returns a value we need to move it to the right place at the
end of the function execution just before a prologue. A calling code except
putting all function arguments prepares a place for a return value.


     0xffffffff
   -------------
  |             |
   -------------
  | return value|
  |   place     |
  |             |
   -------------	+20(%fp)
  |	arg3	|
   -------------        +16(%fp)
  |	arg2	|
   -------------        +12(%fp)
  |	arg1	|
   -------------	+8(%fp)
  | ret address | 
   -------------        +4(%fp)
  |	%fp	|
   ------------- <---- %fp 
  | local var1  |
   -------------        -4(%fp)
  | local var2  |
   -------------  	-8(%fp)
  | local var3  |	-12(%fp)
   ------------- <---- %sp (in case of void return value)
  | last oper.	|
  |	data	|
  |		|
  |		|
   ------------- <---- %sp 
  |		|
   -------------
     0x00000000


If a function don't return a value (void data type) we don't generate this
code. Otherwise we need to place the return value below all arguments with one
instruction
	
	load	20(%fp)
	    or
	strload	20(%fp)

dependes on the return data type. On the stack top we have the result of the
last operations so it is a return value for us.


Every function finishes with an epilogue. It makes oposite actions to a
prologue.

	push    %fp
        pop     %sp
        pop     %fp
	ret

     0xffffffff
   -------------
  | return value|
  |		|
  |		|
   -------------
  |     arg3    |
   -------------
  |     arg2    |
   -------------
  |	arg1	|
   ------------- <--- %sp
  |             |
   -------------
     0x00000000

The last instruction in the function 'ret'. It takes a return value address
from the stack top and changes the %pc to return to the calling code.

A calling code grows a stack to remove all arguments.

	push	%sp
	push	$12
	add
	pop	%sp

There is only a return value on the stack top now.

     0xffffffff
   -------------
  | return value|
  |             |
  |             |
  |             |
   ------------- <---- %sp
     0x00000000




* Speed up the virtual machine

Some of code blocks can be atomic for the virtual machine:

- epilogue;
- prologue;
- grow stack size;
- shrink stack size.


So this code blocks can be replaced with single instructions. It helps to
increase the virtual machine execution speed.

Epilogue:
        push    %fp
        push    %sp	--->	ent
        pop     %fp

Prologue:
	push    %fp
        pop     %sp	--->	leave
        pop     %fp
	ret

Grow stack size:
	push    %sp
	push    $12	--->	grow	$12
	add
	pop     %sp

Shrink stack size:
	push    %sp
	push    $12	--->	shrink	$12
	sub	
	pop     %sp