Skip to main content

IAR Embedded Workbench for RH850 3.20.x

Calling convention

In this section:

A calling convention is the way a function in a program calls another function. The compiler handles this automatically, but, if a function is written in assembler language, you must know where and how its parameters can be found, how to return to the program location from where it was called, and how to return the resulting value.

It is also important to know which registers an assembler-level routine must preserve. If the program preserves too many registers, the program might be inefficient. If it preserves too few registers, the result would be an incorrect program.

This section describes the calling convention used by the compiler. These items are examined:

At the end of the section, some examples are shown to describe the calling convention in practice.

Function declarations

In C, a function must be declared in order for the compiler to know how to call it. A declaration could look as follows:

int MyFunction(int first, char * second);

This means that the function takes two parameters: an integer and a pointer to a character. The function returns a value, an integer.

In the general case, this is the only knowledge that the compiler has about a function. Therefore, it must be able to deduce the calling convention from this information.

Using C linkage in C++ source code

In C++, a function can have either C or C++ linkage. To call assembler routines from C++, it is easiest if you make the C++ function have C linkage.

This is an example of a declaration of a function with C linkage:

extern "C"
{
  int F(int);
}

It is often practical to share header files between C and C++. This is an example of a declaration that declares a function with C linkage in both C and C++:

#ifdef __cplusplus
extern "C" 
{
#endif

int F(int);

#ifdef __cplusplus
}
#endif

Preserved versus scratch registers

The general RH850 CPU registers are divided into three separate sets, which are described in this section.

Scratch registers

Any function is permitted to destroy the contents of a scratch register. If a function needs the register value after a call to another function, it must store it during the call, for example, on the stack.

Any of the registers R1 and R6R19, and the return address registers, can be used as a scratch register by the function. (The registers R15R19 are not available if they have been locked with the ‑‑lock_10_regs option.)

Preserved registers

Preserved registers, on the other hand, are preserved across function calls. The called function can use the register for other purposes, but must save the value before using the register and restore it at the exit of the function.

The registers R20R29 (and R30 if the EP is not used for short addressing), but not including the return address registers, are preserved registers. Note that registers R20R24 are not available if one of the options ‑‑lock_10_regs or ‑‑lock_global_pointer_regs has been specified.

Special registers

For some registers, you must consider certain prerequisites:

  • R0 will act as a normal processor register with the exception that the value of the register is always zero.

  • R2 is reserved for use by an operating system.

  • Some registers might possibly be unavailable due to register locking. For details, see --lock_10_regs, page 282 and --lock_global_pointer_regs, page 283.

  • The stack pointer register must at all times point to the topmost element on the stack. If an interrupt occurs, everything on the other side of the point the stack pointer points to can be destroyed.

  • The brel base pointer registers GP and TP (pointing to data areas that are addressed with indexed addressing modes) must never be changed.

  • The link register holds the return address at the entrance of the function.

Function entrance

Parameters can be passed to a function using one of these basic methods:

  • In registers

  • On the stack

It is much more efficient to use registers than to take a detour via memory, so the calling convention is designed to use registers as much as possible. Only a limited number of registers can be used for passing parameters—when no more registers are available, the remaining parameters are passed on the stack.

Hidden parameters

In addition to the parameters visible in a function declaration and definition, there can be hidden parameters:

  • If the function returns a structure, the memory location where the structure is to be stored is passed as an extra parameter in register R6. Note that it is always treated as the first parameter.

  • If the function is a non-static C++ member function, then the this pointer is passed as the first parameter in register R6 (but placed after the return structure pointer, if there is one). The reason why the member function must be non-static is that static member methods do not have a this pointer.

Register parameters

The registers available for passing parameters are R6R9:

Parameters

Passed in registers

8-, 16-, or 32-bit values

R6R9

64-bit values

R6:R7, R7:R8, R8:R9

96-bit values

R6:R7:R8, R7:R8:R9

128-bit values

R6:R7:R8:R9

Table 67. Registers used for passing parameters 


Note

Scalar parameters—integers and pointers—require one register. The same is true for float values. On the other hand, double and long long values require two registers.

The assignment of registers to parameters is a straightforward process. The first parameter is assigned to the first available register, the second parameter to the second available register, etc. Should there be no more available registers, the parameter is passed on the stack.

If a struct or an argument is passed and there are still parameter registers available, the beginning of the struct is passed to those registers.

If a double parameter is passed in registers, it can only be passed using a register pair listed in the table. If, for example, R6 has been assigned to a scalar parameter, the next available register pair is R8:R9 (assuming 8-byte alignment).

Stack parameters and layout

Stack parameters are stored in the main memory, starting at the location pointed to by the stack pointer. Below the stack pointer (toward low memory) there is free space that the called function can use. The first stack parameter is stored at the location pointed to by the stack pointer. The next one is stored at the next location on the stack that is divisible by four, etc. (For more complex parameters, see also the Renesas ABI specification.)

This figure illustrates how parameters are stored on the stack:

Stack_image_70percent.png

Function exit

A function can return a value to the function or program that called it, or it can have the return type void.

The return value of a function, if any, can be scalar (such as integers and pointers), floating-point, or a structure.

Registers used for returning values

Scalar and float values are returned using register R10. double and long long values use the register pair R10:R11.

If a structure is returned, the caller of the function is responsible for allocating memory for the return value. A pointer to the memory is passed as a “hidden” first parameter that is always allocated to register R10. The called function must return the value of the location in register R10.

Stack layout at function exit

It is the responsibility of the called function to clean the stack before function exit, in other words, be responsible for cleaning the pushed parameters before the function returns.

Return address handling

A function written in assembler language should, when finished, return to the caller. At a function call, the return address is stored in the return address register or registers. This is register LP.

Typically, a function returns by using the JMP instruction, for example:

        jmp    [LP]

If a function is to call another function, the original return address must be stored somewhere. This is normally done on the stack, for example:

        name    call
        section .text:CODE(2)
        extern  _func
        code

        prepare     {lp},0
        jarl        _func,lp

        ; Do something here.

        dispose     0,{lp},[lp]

        end

Examples

The following section shows a series of declaration examples and the corresponding calling conventions. The complexity of the examples increases toward the end.

Example 1

Assume this function declaration:

int add1(int);

This function takes one parameter in the register R6, and the return value is passed back to its caller in the register R10.

This assembler routine is compatible with the declaration; it will return a value that is one number higher than the value of its parameter:

            name    return
            section .text:CODE(2)
            code
            add     1,r6
            mov     r6,r10
            jmp     [lp]

            end
Example 2

The function below will return a structure of type struct MyStruct.

struct MyStruct
{ 
  int mA[20];
};

struct MyStruct MyFunction(int x);

It is the responsibility of the calling function to allocate a memory location for the return value and pass a pointer to it as a hidden first parameter. The pointer to the location where the return value should be stored is passed in R6. The caller assumes that these registers remain untouched. The parameter x is passed in R7.

Assume that the function instead was declared to return a pointer to the structure:

struct MyStruct *MyFunction(int x);

In this case, the return value is a scalar, so there is no hidden parameter. The parameter x is passed in R6, and the return value is returned in R10.