Skip to main content

IAR Embedded Workbench for RH850 3.20.x

Mixing C and assembler

In this section:

The IAR C/C++ Compiler for RH850 provides several ways to access low-level resources:

  • Modules written entirely in assembler

  • Intrinsic functions (the C alternative)

  • Inline assembler.

It might be tempting to use simple inline assembler. However, you should carefully choose which method to use.

Intrinsic functions

The compiler provides a few predefined functions that allow direct access to low-level processor operations without having to use the assembler language. These functions are known as intrinsic functions. They can be useful in, for example, time-critical routines.

An intrinsic function looks like a normal function call, but it is really a built-in function that the compiler recognizes. The intrinsic functions compile into inline code, either as a single instruction, or as a short sequence of instructions.

For more information about the available intrinsic functions, see Intrinsic functions.

Mixing C and assembler modules

It is possible to write parts of your application in assembler and mix them with your C or C++ modules.

When an application is written partly in assembler language and partly in C or C++, you are faced with several questions:

  • How should the assembler code be written so that it can be called from C?

  • Where does the assembler code find its parameters, and how is the return value passed back to the caller?

  • How should assembler code call functions written in C?

  • How are global C variables accessed from code written in assembler language?

  • Why does not the debugger display the call stack when assembler code is being debugged?

The first question is discussed in Calling assembler routines from C. The following two are covered in Calling convention.

For information about how data in memory is accessed, see Memory access methods.

The answer to the final question is that the call stack can be displayed when you run assembler code in the debugger. However, the debugger requires information about the call frame, which must be supplied as annotations in the assembler source file. For more information, see Call frame information.

The recommended method for mixing C or C++ and assembler modules is described in Calling assembler routines from C, and Calling assembler routines from C++, respectively.

Note

To comply with the Renesas ABI, the compiler generates assembler labels for symbol and function names by prefixing an underscore. You must remember to add this extra underscore when you access C symbols from assembler. For example, main must be written as _main.

Similarly, when referencing an external assembly module from C, an underscore will be added to the symbol used in the C module, so the name of the assembly module must start with an added underscore.

Inline assembler

Inline assembler can be used for inserting assembler instructions directly into a C or C++ function. Typically, this can be useful if you need to:

  • Access hardware resources that are not accessible in C (in other words, when there is no definition for an SFR or there is no suitable intrinsic function available).

  • Manually write a time-critical sequence of code that if written in C will not have the right timing.

  • Manually write a speed-critical sequence of code that if written in C will be too slow.

An inline assembler statement is similar to a C function in that it can take input arguments (input operands), have return values (output operands), and read or write to C symbols (via the operands). An inline assembler statement can also declare clobbered resources, that is, values in registers and memory that have been overwritten.

Limitations

Most things you can to do in normal assembler language are also possible with inline assembler, with the following differences:

  • Alignment cannot be controlled—this means, for example, that DC32 directives might be misaligned.

  • In general, assembler directives will cause errors or have no meaning. However, data definition directives will work as expected.

  • Resources used (registers, memory, etc) that are also used by the C compiler must be declared as operands or clobbered resources.

  • If you do not want to risk that the inline assembler statement is optimized away by the compiler, you must declare it volatile.

  • Accessing a C symbol or using a constant expression requires the use of operands.

  • Dependencies between the expressions for the operands might result in an error.

Risks with inline assembler

Without operands and clobbered resources, inline assembler statements have no interface with the surrounding C source code. This makes the inline assembler code fragile, and might also become a maintenance problem if you update the compiler in the future. There are also several limitations to using inline assembler without operands and clobbered resources:

  • The compiler’s various optimizations will disregard any effects of the inline statements, which will not be optimized at all.

  • Inlining of functions with assembler statements without declared side-effects will not be done.

  • The inline assembler statement will be volatile and clobbered memory is not implied. This means that the compiler will not remove the assembler statement. It will simply be inserted at the given location in the program flow. The consequences or side-effects that the insertion might have on the surrounding code are not taken into consideration. If, for example, registers or memory locations are altered, they might have to be restored within the sequence of inline assembler instructions for the rest of the code to work properly.

Warning

The following example—demonstrates the risks of using the asm keyword without operands and clobbers:

extern __near volatile char UART1_SR;
#pragma required=UART1_SR

static __near char sFlag;

void Foo(void)
{
  while (!sFlag)
  {
    asm("LD.BU       _UART1_SR[r0],r1\n"
        "ST.B        r1,_sFlag[r0]");
  }
}

Note

Because using symbols from inside the inline assembler code is not properly visible to all parts of the compiler, you must use #pragma required when you reference an external or module-local symbol only from inline assembler code. If you do not, you can get an undefined symbol error when compiling. See required.

In this example:

  • The function Add assumes that values are passed and returned in registers in a way that they might not always be, for example, if the function is inlined.

  • The add instruction updates the condition flags, which should be specified using the cc clobber operand. Otherwise, the compiler will assume that the condition flags are not modified.

Inline assembler without using operands or clobbered resources is therefore often best avoided. The compiler will issue a remark for them.