3.4. Function Structure and Calling Conventions

The C/C++ compiler imposes a strict set of rules on function calls. Except for special run-time support functions, any function that calls or is called by a C/C++ function must follow these rules. Failure to adhere to these rules can disrupt the C/C++ environment and cause a program to fail.

The following sections use this terminology to describe the function-calling conventions of the C/C++ compiler:

  • Argument block. The part of the local frame used to pass arguments to other functions. Arguments are passed to a function by moving them into the argument block rather than pushing them on the stack. The local frame and argument block are allocated at the same time.

  • Register save area. The part of the local frame that is used to save the registers when the program calls the function and restore them when the program exits the function.

  • Save-on-call registers. Registers R0-R3 and R12 (alternate names are A1-A4 and V9). The called function does not preserve the values in these registers; therefore, the calling function must save them if their values need to be preserved.

  • Save-on-entry registers. Registers R4-R11 and R14 (alternate names are V1 to V8 and LR). It is the called function’s responsibility to preserve the values in these registers. If the called function modifies these registers, it saves them when it gains control and preserves them when it returns control to the calling function.

The following figure illustrates a typical function call. In this example, arguments are passed to the function, and the function uses local variables and calls another function. The first four arguments are passed to registers R0-R3. This example also shows allocation of a local frame and argument block for the called function. Functions that have no local variables and do not require an argument block do not allocate a local frame.

../../_images/stack_func_pnu151.png

Figure 3.6 Use of the Stack During a Function Call

3.4.1. How a Function Makes a Call

A function (parent function) performs the following tasks when it calls another function (child function).

  1. The calling function (parent) is responsible for preserving any save-on-call registers across the call that are live across the call. (The save-on-call registers are R0-R3 and R12 (alternate names are A1-A4 and V9).)

  2. If the called function (child) returns a structure, the caller allocates space for the structure and passes the address of that space to the called function as the first argument.

  3. The caller places the first arguments in registers R0-R3, in that order. The caller moves the remaining arguments to the argument block in reverse order, placing the leftmost remaining argument at the lowest address. Thus, the leftmost remaining argument is placed at the top of the stack.

  4. If arguments were stored onto the argument block in step 3, the caller reserves a word in the argument block for dual-state support.

3.4.2. How a Called Function Responds

A called function (child function) must perform the following tasks:

  1. If the function is declared with an ellipsis, it can be called with a variable number of arguments. The called function pushes these arguments on the stack if they meet both of these criteria:

    • The argument includes or follows the last explicitly declared argument.

    • The argument is passed in a register.

  2. The called function pushes register values of all the registers that are modified by the function and that must be preserved upon exit of the function onto the stack. Normally, these registers are the save-on-entry registers (R4-R11 and R14 (alternate names are V1 to V8 and LR)) and the link register (R14) if the function contains calls. If the function is an interrupt, additional registers may need to be preserved. For more information, see Interrupt Handling.

  3. The called function allocates memory for the local variables and argument block by subtracting a constant from the SP. This constant is computed with the following formula:

    size of all local variables + max = constant

    The max argument specifies the size of all parameters placed in the argument block for each call.

  4. The called function executes the code for the function.

  5. If the called function returns a value, it places the value in R0 (or R0 and R1 values).

  6. If the called function returns a structure, it copies the structure to the memory block that the first argument, R0, points to. If the caller does not use the return value, R0 is set to 0. This directs the called function not to copy the return structure.

    Structures and unions are always passed by reference.

    In this way, the caller can be smart about telling the called function where to return the structure. For example, in the statement s = f(x), where s is a structure and f is a function that returns a structure, the caller can simply pass the address of s as the first argument and call f. The function f then copies the return structure directly into s, performing the assignment automatically.

    You must be careful to properly declare functions that return structures, both at the point where they are called (so the caller properly sets up the first argument) and at the point where they are declared (so the function knows to copy the result).

  7. The called function deallocates the frame and argument block by adding the constant computed in Step 3.

  8. The called function restores all registers that were saved in Step 2.

  9. The called function ( _f) loads the program counter (PC) with the return address.

The following example is typical of how a called function responds to a call:

                            ; called function entry point
STMFD SP!, {V1, V2, V3, LR} ; save V1, V2, V3, and LR
SUB SP, SP, #16             ; allocate frame
...                         ; body of the function
ADD SP, SP, #16             ; deallocate frame
LDMFD SP!, {V1, V2, V3, PC} ; restore V1, V2, V3, and store LR
                            ; in the PC, causing a return

3.4.3. C Exception Handler Calling Convention

The C library provides no built-in support for C exception handling.

If a C exception handler calls other functions, the following must take place:

  • The handler must set its own stack pointer.

  • The handler saves all of the registers not preserved by the call: R0-R3, R-12, LR (R8-R12 saved by hardware for FIQ)

  • Re-entrant exception handlers must save SPSR and LR.

See C++ Exception Handling for information about C++ exception handling.

3.4.4. Accessing Arguments and Local Variables

A function accesses its local nonregister variables indirectly through the stack pointer (SP or R13) and its stack arguments indirectly through the argument pointer (AP). If all stack arguments can be referenced with the SP, they are, and the AP is not reserved. The SP always points to the top of the stack (the most recently pushed value) and the AP points to the leftmost stack argument (the one closest to the top of the stack). For example:

LDR  A2 [SP, #4]            ; load local var from stack
LDR  A1 [AP, #0]            ; load argument from stack

Since the stack grows toward smaller addresses, the local and argument data on the stack for the C/C++ function is accessed with a positive offset from the SP or the AP register.