Skip to main content

IAR Embedded Workbench for RX 5.20

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.

Note

The calling convention complies with the RX ABI standard.

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 RX 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 R1R5 or R14R15 can be used as a scratch register by the function.

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 R6R13 are preserved registers.

Special registers

The stack pointer register (R0) must at all times point to or below the last element on the stack. In the eventuality of an interrupt, everything below the point the stack pointer points to, will be destroyed.

The register R14 is used by veneers to extend the range of calls, so it can be destroyed between the calling point and the entry point of the called 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. The parameters are also passed on the stack in these cases:

  • Aggregate types (structures, unions and arrays) larger than 16 bytes, or with a lower alignment than 4

  • Unnamed parameters to variable length (variadic) functions; in other words, functions declared as foo(param1,...), for example printf.

Note

Interrupt functions cannot take any parameters.

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 that does not fit into a register, the memory location where the structure will be stored is passed as the last function parameter.

  • Hidden parameters are passed in register R15.

Register parameters

The registers available for passing parameters are R1R4:

Parameters

Passed in registers

8- to 32-bit values

R1R4

64-bit values

R2R1, R3R2, R4R3

Aggregate values

R1R4

Table 67. Registers used for passing parameters 


Small aggregate types are only passed in registers R1R4 if they:

  • are 16 bytes or smaller

  • have an alignment of 4 or more.

Aggregate types that do not fit these two requirements will use a hidden parameter.

The assignment of registers to parameters is a straightforward process. Traversing the parameters in strict order from left to right, the first parameter is assigned to the available register or registers. Should there be no suitable register available, the parameter is passed on the stack. This process continues until there are no more parameter registers available or until all parameters have been passed.

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.

This figure illustrates how parameters are stored on the stack:

Stack_image_70percent.png

Objects on the stack should be aligned to 4 bytes at function entry, regardless of their size.

When passed in registers, aggregate types follow the setting of the byte order option --endian, but scalar types are always little-endian. On the stack, all parameters are stored according to the byte order setting.

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

The registers available for returning values are:

Return values

Passed in registers

8- and 16-bit scalars

R1

32-bit values

R1

64-bit values

R2R1

Aggregate values

R1R4

Table 68. Registers used for returning values  


Stack layout at function exit

It is the responsibility of the caller to clean the stack after the called function has returned.

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 on the stack.

Typically, a function returns by using the RTS or RTSD instruction.

Restrictions for special function types

Interrupt functions save all used registers. Task functions save no registers at all, and monitor functions save the interrupt status.

An interrupt function returns by using the RTE instruction. Task functions and monitor functions return by using the RTS or RTSD instruction, depending on whether they need to deallocate a stack frame or not.

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 R1, and the return value is passed back to its caller in the register R1.

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
            code
            add     #1,R1
            rts
            end
Example 2

This example shows how structures are passed on the stack. Assume these declarations:

struct MyStruct 
{ 
  short a;
  short b;
  short c;
  short d;
  short e;
};

int MyFunction(struct MyStruct x, int y);

The calling function must reserve 20 bytes on the top of the stack and copy the contents of the struct to that location. The integer parameter y is passed in the register R1. The return value is passed back to its caller in the register R1.

Example 3

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

struct MyStruct
{ 
  int mA;
  int mB;
};

struct MyStruct MyFunction(int x);

In this case, the struct is small enough to fit in registers, so it is returned in R2R1.

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 R1, and the return value is returned in R1.