<< >> Title Contents Index Home Help

A Run-time Environment

This appendix describes the programming model supported for compiler code generation, including register conventions, and common object file formats.

A.1 Programming Model

This section defines compiler and assembly language conventions for the use of certain aspects of the processor. These standards must be followed to guarantee that compilers, applications programs, and operating systems written by different people and organizations will work together. The conventions supported by the PGCC ANSI C compiler implement the application binary interface (ABI) as defined in the System V Application Binary Interface: Intel Processor Supplement and the System V Application Binary Interface, listed in the section "Related Publications," in the Preface.

A.2 Function Calling Sequence

This section describes the standard function calling sequence, including the stack frame, register usage, and parameter passing.

Register Assignment

Table A-1 defines the standard for register allocation. The Intel Architecture (IA) provides a number of registers. All the integer registers and all the floating-point registers are global to all procedures in a running program.

Table A-1 Register Allocation






integer return value


dividend register (for divide operations)


count register (shift and string operations)


local register variable


stack frame pointer


local register variable


local register variable


stack pointer



floating-point stack top, return value


floating-point next to stack top



floating-point stack bottom

In addition to the registers, each function has a frame on the run-time stack. This stack grows downward from high addresses. Table A-2 shows the stack frame organization.

Table A-2 Standard Stack Frame




4n+8 (%ebp)

argument word n


8 (%ebp)

argument word 0

4 (%ebp)

return address

0 (%ebp)

caller's %ebp


-4 (%ebp)

n bytes of local

-n (%ebp)

variables and temps

Several key points concerning the stack frame:

All registers on a IA system are global and thus visible to both a calling and a called function. Registers %ebp, %ebx, %edi, %esi, and %esp belong to the calling function. In other words, a called function must preserve these registers' values for its caller. Remaining registers belong to the called function. If a calling function wants to preserve such a register value across a function call, it must save a value in its local stack frame.

Some registers have assigned roles in the standard calling sequence:

The stack pointer holds the limit of the current stack frame, which is the address of the stack's bottom-most, valid word. At all times, the stack pointer should point to a word-aligned area.
The frame pointer holds a base address for the current stack frame. Consequently, a function has registers pointing to both ends of its frame. Incoming arguments reside the previous frame, referenced as positive offsets from %ebp, while local variables reside in the current frame, referenced as negative offsets from %ebp. A function must preserve this register value for its caller.
Integral and pointer return values appear in %eax. A function that returns a struct or union value places the address of the result in %eax. Otherwise, this is a scratch register.
%esi and %edi
These local registers have no specified role in the standard calling sequence. Functions must preserve their values for the caller.
%ecx and %edx
Scratch registers have no specified role in the standard calling sequence. Functions do not have to preserve their values for the caller.
Floating-point return values appear on the top of the floating-point register stack; there is no difference in the representation of single or double-precision values in floating point registers. If the function does not return a floating point value, then this register must be empty.
%st(1) through %st(7)

Floating point scratch registers have no specified role in the standard calling sequence. These registers must be empty before entry and upon exit from a function.
The flags register contains the system flags, such as the direction flag and the carry flag. The direction flag must be set to the "forward" (i.e. zero) direction before entry and upon exit from a function. Other user flags have no specified role in the standard calling sequence and are not reserved.
Floating Point Control Word

The control word contains the floating-point flags, such as the rounding mode and exception masking. This register is initialized at process initialization time and its value must be preserved.

Signals can interrupt processes. Functions called during signal handling have no unusual restriction on their use of registers. Moreover, if a signal handling function returns, the process resumes its original execution path with registers restored to their original values. Thus, programs and compilers may freely use all registers without danger of signal handlers changing their values.

A.3 Functions Returning Scalars or No Value

A function that returns an integral or pointer value places its result in register %eax.

A function that returns a long long integer value places its result in the registers %edx and %eax. The most significant word is placed in %edx and the least significant word is placed in %eax.

A floating-point return value appears on the top of the fpu register stack. The caller must then remove the value from the fpu stack, even if it doesn't use the value. Failure of either side to meet its obligations leads to undefined program behavior. The standard calling sequence does not include any method to detect such failures nor to detect return value type mismatches. Therefore the user must declare all functions properly. There is no difference in the representation of single-, double- or extended-precision values in floating-point registers.

Functions that return no value (also called procedures or void functions) put no particular value in any register.

A call instruction pushes the address of the next instruction ( the return address) onto the stack. The return instruction pops the address off the stack and effectively continues execution at the next instruction after the call instruction. A function that returns a scalar or no value must preserve the caller's registers as described above. Additionally, the called function must remove the return address from the stack, leaving the stack pointer (%esp) with the value it had before the call instruction was executed.

Functions Returning Structures or Unions

If a function returns a structure of union, then the caller provides space for the return value and places its address on the stack as argument word zero. In effect, this address becomes a hidden first argument. Having the caller supply the return objects spaces allows re-entrancy.

A function that returns a structure or union also sets %eax to the value of the original address of the caller's area before it returns. Thus when the caller receives control again, the address of the returned object resides in register %eax and can be used to access the object. Both the calling and the called functions must cooperate to pass the return value successfully:

Failure of either side to meet its obligation leads to undefined program behavior. The standard function calling sequence does not include any method to detect such failures nor to detect structure and union type mismatches. Therefore the user must declare the function properly.

Table A-3 illustrates the stack contents when the function receives control, after the call instruction, and when the calling function again receives control, after the ret instruction.

Table A-3 Stack Contents for Functions Returning struct/union


After Call

After Return


4n+8 (%esp)

argument word n

argument word n

4n-4 (%esp)

8 (%esp)

argument word 1

argument word 1

0 (%esp)

4 (%esp)

value address


0 (%esp)

return address

The following sections of this appendix describe where arguments appear on the stack. The examples are written as if the function prologue described above had been used.

A.4 Integral and Pointer Arguments

As mentioned, a function receives all its arguments through the stack; the last argument is pushed first. In the standard calling sequence, the first argument is at offset 8 (%ebp), the second argument is at offset 12 (%ebp), etc. Functions pass all integer-valued arguments as words, expanding or padding signed or unsigned bytes and halfwords as needed.

Table A-4 Integral and Pointer Arguments



Stack Address

g(1, 2, 3, (void *)0);


8 (%ebp)


12 (%ebp)


16 (%ebp)

(void *) 0

20 (%ebp)

A.5 Floating-Point Arguments

The stack also holds floating-point arguments: single-precision values use one word, and double-precision use two. The example below uses only double-precision arguments.

Table A-5 Floating-point Arguments



Stack Address

h(1.414, 1, 2.998e10);

word 0, 1.414

8 (%ebp)

word 1, 1.414

12 (%ebp)


16 (%ebp)

word 0 2.998e10

20 (%ebp)

word 1, 2.998e10

24 (%ebp)

A.6 Structure and Union Arguments

As described in the data representation section, structures and unions can have byte, halfword, or word alignment, depending on the constituents. An argument's size is increased, if necessary, to make it a multiple of words. This may require tail padding, depending on the size of the argument. To ensure that data in the stack is properly aligned, the stack pointer should always point to a double word boundary. Structure and union arguments are pushed onto the stack in the same manner as integral arguments, described above. This provides call-by-value semantics, letting the called function modify its arguments without affecting the calling functions object.

Table A-6 Structure and Union Arguments



Stack Address



8 (%ebp)

word 0, s

12 (%ebp)

word 1, s

16 (%ebp)



Implementing a Stack

In general, compilers and programmers must maintain a software stack. Register %esp is the stack pointer. Register %esp is set by the operating system for the application when the program is started. The stack must be a grow-down stack.

A separate frame pointer enables calls to routines that change the stack pointer to allocate space on the stack at run-time (e.g. alloca). Some languages can also return values from a routine allocated on stack space below the original top-of-stack pointer. Such a routine prevents the calling function from using %esp-relative addressing to get at values on the stack. If the compiler does not call routines that leave %esp in an altered state when they return, a frame pointer is not needed and is not used if the compiler option -Mnoframe is specified.

Although not required, the stack should be kept aligned on 8-byte boundaries. Each routine should use stack space in multiples of 8 bytes. PGI's compilers allocate stack space in multiples of 8 bytes.

Variable Length Parameter Lists

Parameter passing in registers can handle a variable number of parameters. The C language uses a special method to access variable-count parameters. The stdarg.h and varargs.h files define several functions to access these parameters. A C routine with variable parameters must use the va_start macro to set up a data structure before the parameters can be used. The va_arg macro must be used to access the successive parameters.

C Parameter Conversion

In C, for a called prototyped function, the parameter type in the called function must match the argument type in the calling function. If the called function is not prototyped, the calling convention uses the types of the arguments but promotes char or short to int, and unsigned char or unsigned short to unsigned int and promotes float to double, unless you use the -Msingle option. For more information on the -Msingle option, refer to Chapter 7, Command-line Options. If the called function is prototyped, the unused bits of a register containing a char or short parameter are undefined and the called function must extend the sign of the unused bits when needed.

Calling Assembly Language Programs

/* File: testmain.c  */

long l_para1 = 0x3f800000;
float f_para2 = 1.0;
double d_para3 = 0.5;

float f_return;

extern float sum_3 (
long para1,
float para2,
double para3);

f_return = sum_3(l_para1, f_para2, d_para3);
printf("Parameter one, type long = %08x\n", l_para1);
printf("Parameter two, type float = %f\n", f_para2);
printf("Parameter three, type double = %g\n", d_para3);
printf("The sum after conversion = %f\n", f_return);

// File: sum_3.s

// Computes ( para1 + para2 ) + para3

/ PGC Rel 1.1 -opt 1
.align 4
.long .EN1-sum_3+0xc8000000
.align 16
.globl sum
subl $12,%esp
movl %ebp,8(%esp)
leal 8(%esp),%ebp
fildl 8(%ebp)
fadds 12(%ebp)
faddl 16(%ebp)
fsts 12(%ebp)
fstps -4(%ebp)
flds -4(%ebp)
movl %ebp,%esp
popl %ebp
.type sum_3,@function

Example A-1 C Program Calling an Assembly-language Routine

<< >> Title Contents Index Home Help