ARM Technical Support Knowledge Articles

How does inline in the C99 standard behave differently?

Applies to: DS-5, RealView Development Suite (RVDS)

Answer

At low optimisation levels the ARM linker might throw error message L6218E when building the following code built for C99, for example:

-----test.c------
inline int inc(int a) {return ++a;}

int a = 0;

int foo(void)
{
  a = inc(a);
  a = inc(a);
  return a;
}

int main()
{
  foo();
  return 0;
}
-----end---------

$ armcc test.c -o test.axf -O0 --c99
  Error: L6218E: Undefined symbol inc (referred from test.o).

This error is expected and caused by the special C99 specification for the inline keyword. When building for C90 (using __inline keyword instead of inline) or C++, no error appears. In C99, a function that is marked with inline is an inline function.  An inline function in C99 has an inline definition. An external definition (out-of-line copy of the function definition) is never generated. Whereas in C90 and C++ an external definition of the function is generated.    

Note
It is a little bit ambiguous in the C99 standard about how the compiler tools should treat the inline functions.  Section 6.7.4p6 of the C99 Standard states the following: “An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.

In C99 (-c99), armcc does not provide an external definition for an inline function. However, the compiler might decide not to inline the function.  For example, in the case above, when building at optimisation level -O0, the function will be called explicitly.  To resolve the relocation to the function inc, the linker requires an external definition.  However, no external definition exists, hence the linker complains there is no external definition of inc.  When a higher compiler optimization level is used (-O1/O2/O3), the compiler is likely to inline the function, and not to generate explicit calls to inc, so the external definition of inc is not required. As the assembly code below shows, when -O0 --c99 is used, the compiler generates explicit calls to inc, therefore an external definition of inc is required by the linker, which an inline function does not provide:

foo
     0x00000000: e92d4010 .@-. PUSH {r4,lr}
     0x00000004: e59f003c <... LDR r0,[pc,#60] ; [a = 0x48] = 0
     0x00000008: e5900000 .... LDR r0,[r0,#0]
     0x0000000c: ebfffffe .... BL inc ;<-- explicit call to ‘inc’
     0x00000010: e59f1030 0... LDR r1,[pc,#48] ; [a = 0x48] = 0
     0x00000014: e5810000 .... STR r0,[r1,#0]
     0x00000018: e2810000 .... ADD r0,r1,#0
     0x0000001c: e5900000 .... LDR r0,[r0,#0]
     0x00000020: ebfffffe .... BL inc ;<-- explicit call to ‘inc’
     0x00000024: e59f101c .... LDR r1,[pc,#28] ; [a = 0x48] = 0
     0x00000028: e5810000 .... STR r0,[r1,#0]
     0x0000002c: e2810000 .... ADD r0,r1,#0
     0x00000030: e5900000 .... LDR r0,[r0,#0]
     0x00000034: e8bd8010 .... POP {r4,pc}

When a higher optimization level is used, the call to inc is inlined by the compiler, and no external definition is needed:

foo
     0x00000000: e59f101c .... LDR r1,[pc,#28] ; [0x24] = 0
     0x00000004: e5910000 .... LDR r0,[r1,#0]
     0x00000008: e2800002 .... ADD r0,r0,#2
     0x0000000c: e5810000 .... STR r0,[r1,#0]
     0x00000010: e12fff1e ../. BX lr

A high optimization setting does not guarantee the function will be inlined by the compiler. Using the combination of the inline keyword and compiler option --force_inline will force the compiler to inline the function if possible.

Also, a C99 inline function is treated differently to a static inline or an extern inline function. A static inline function is only visible from within the translation unit where it is defined (it has file scope rather than global scope).  No common section is generated for the static inline function.  If a static inline function cannot be inlined, the compiler generates the function definition (which is only visible within the translation unit). Whereas if the compiler cannot inline an inline function, it generates an external definition of the function and puts it into a common section, which can be used globally. The compiler generates an external definition for an extern inline function (with external linkage), no matter whether the function is inlined or not. The external definition is put in a common section, as the code below shows. Then the linker can perform a further optimization at the link time, so that only one definition in the common section is used across different translation units.

** Section #7 'i.inc' (SHT_PROGBITS) [SHF_ALLOC + SHF_EXECINSTR + SHF_GROUP]
Size : 8 bytes (alignment 4)
Address: 0x00000000

$a
i.inc
inc
0x00000000: e2800001 .... ADD r0,r0,#1
0x00000004: e12fff1e ../. BX lr

In C90/C++, the compiler treats an inline function the same as an extern inline function (different from C99), and generates an external definition for an extern inline function in a common section, when the function is not inlined (more information can be found in the Extern Inline Functions section of the ARM Compiler Reference Manual). If the function calls have been replaced by the inline function, the compiler might not keep the external definition in the common section.

Suggestions:

To use inline in C99 properly, it is recommended to specify the function with static inline, or put the definition of the function in a header file and specify it with extern inline.

References:

Article last edited on: 2012-12-11 10:47:42

Rate this article

[Bad]
|
|
[Good]
Disagree? Move your mouse over the bar and click

Did you find this article helpful? Yes No

How can we improve this article?

Link to this article
Copyright © 2011 ARM Limited. All rights reserved. External (Open), Non-Confidential