C++ Support in TI Compilers

From Texas Instruments Wiki
Jump to: navigation, search

Status as of March 2014

The most recent release of the TI compiler supports C++98 and C++03. We do not support C++11 or C++ TR1.

Status as of July 2008

This document describes the state of the C++ support in TI compiler tools as of these compiler versions:

Target Version
C6000 v6.1.0
ARM v4.5.0
C5500 v4.2.0
MSP430 v3.0.0
C2000 v5.1.0

Support for the Language

Support for the language is excellent. There are no major gaps. Multiple forms of C++ are supported, including Embedded C++ described below.

Embedded C++

Embedded C++ is a subset of the C++ language aimed at embedded systems. It got its start from a group of semiconductor companies in Japan in the mid-90s. It is a pure subset of C++, with a few very minor differences in the library. The general idea of Embedded C++ is to keep most of the features of C++ which support object oriented programming, but leave out or make optional those features with undue performance impact. For more information visit the Embedded C++ home page.

Support for Embedded C++ is in the form of the compiler option -pe. This option makes it illegal to use any C++ feature that is not in Embedded C++. Embedded C++ omits these C++ features:

  • Templates
  • Exceptions
  • Run-time type information (RTTI)
  • The new cast syntax
  • The keyword mutable
  • Multiple inheritance
  • Virtual inheritance

Note Embedded C++ disallows templates. This means you can use very few of the features in the C++ portion of the RTS library.

Some Comments on Efficiency

This is a rather broad topic that likely merits a separate document. This section covers a few highlights.

No aspect of C++ inefficiency is unique to TI compilers. These aspects are common to all C++ compilers. This means you can consult any number of existing books, training courses, web pages, etc. on this topic to learn more about it.

C++ Language Feature EC++ Subset Performance Penalty
Classes Yes No
Overloading Yes No
Operator Overloading Yes No
Inheritance (derived classes) Yes No
Polymorphism (virtual functions) Yes Only when used
Wide Character Support Yes No
Multiple Inheritance No No
Virtual Base Classes No Only when used
Templates No No
Exceptions No Severe, even if not used (1)
Namespaces No No
Run-time Type Identification (RTTI) No Yes, even if not used
Dynamic Casts No Only when used

Note (1): More detail in the next 2 sections

Be careful, though. Sometimes even those no performance penalty features can end up costing you. These books have some great examples:

  • Effective C++ , Scott Meyers
  • More Effective C++ , Scott Meyers

C++ is a powerful tool. It can be powerfully applied for both good and bad. A chainsaw is a decent analogy. Used well, you get the job done quickly and efficiently. Used poorly, the results can be tragic.

Do not think that Embedded C++ code is always more efficient. Even the C++ features allowed by Embedded C++ can be used poorly, with ill effect. The bad news is you have to understand the efficiency aspects of the C++ code you write. The good news is you can learn about those efficiency aspects from any number of sources that are available to you right now.

Exceptions and RTTI

Support for exceptions and run-time type identification (RTTI) is disabled by default. Use command line options to enable them.

  • For exceptions: --exceptions
  • For RTTI: --rtti

If any C++ code is built with one of those options, then all of it must be built that way, including libraries. Using exceptions also requires you to link with a special version of the compiler runtime support library. For example, when building for a C6400 device, use rts6400_eh.lib instead of rts6400.lib. Note eh stands for exception handling.

Support for debugging exceptions is lacking. If an exception is thrown while you are debugging, you won't get lost with respect to where you are in the code. But you will not be able to determine, via debugger queries of one sort or another, the type or the value of the exception itself.

Exceptions are costly in both cycles and code size, regardless of whether any exceptions are ever thrown. However, things are a bit different for the ARM compiler.

Exceptions on ARM

When building with the older TIABI and default TI_ARM9_ABI, the ARM compiler exceptions perform as described in the previous section. When building with the new EABI, however, exceptions are implemented with a different method that imposes no cycle count penalty, though it does impose a memory size penalty.

EABI stands for Embedded Application Binary Interface. A web search on "EABI" yields several useful links that describe ABIs generally, and the EABI in particular. You can also consult the document "TMS470 EABI Migration" supplied in the \docs directory of the ARM compiler release.

Code built with --abi=eabi --exceptions uses a method of exception handling that imposes no cycle count overhead, provided no exceptions are thrown. When an exception is thrown, exception handling tables are indexed with the program counter to handle the exception.

These tables can require lots of memory. Thus, exceptions are not enabled by default. Since these tables are not referenced during ordinary system execution, it makes sense to allocate them to cheaper memory. Exactly how the tables are built and used are beyond the scope of this document. But, at the end of the final link, they are contained in two separate sections: ARM.extab and ARM.exidx. Lines similar to these in your link command file place these sections in cheaper memory.

.ARM.extab > CHEAP_MEMORY
.ARM.exidx > CHEAP_MEMORY

This form of exception handling also requires that all of the code, including libraries, be built with exceptions enabled. The RTS library you link with must have _eh in the name. Note if you rely on the new linker ability to choose the RTS library for you, then this detail is taken care of automatically.

EABI support is being added to other TI compilers. These compilers, too, will support table driven exception handling when building under EABI.

Support for the Library

C++ library support, as of the above releases, is excellent. TI compilers supply a full implementation of the C++ library, including the STL. The C++ additions to the library are obtained from Dinkumware, a world class supplier of C++ libraries.

There are some minor limitations regarding wide char support. Refer to the readme files and compiler documentation for more details.

ARM v4.4.X C++ Library Problem

The C++ RTS libraries supplied with the ARM compiler v4.4.X have a problem that is corrected in v4.5.0. If the final link uses the Dinkumware library (named lib_something.a), then all of the C and C++ code linked into the system must be built against the Dinkumware header files that only come with a v4.4.X release. In particular, this means any outside vendor libraries must be rebuilt.

This section refers to two different RTS library implementations: the TI implementation and the Dinkumware implementation. The TI library is the same one that always been released with the compiler. The Dinkumware (aka Dinkum) library is new. It contains all the new C++ library functionality.

The header files you build with and RTS the library you link with must match. Consider, for example, the isspace function from the header file <ctype.h>. That function takes a single character as input and returns a boolean value which indicates whether that character is whitespace or not. The isspace function (like all the other character test functions) is implemented with a table lookup. Something like this:

return char_props_table[char_under_test] & SPACE_FLAG;

Details such as the exact name of the table used in the lookup are in the header file <ctype.h>. Those details are different between the TI and Dinkum libraries. The table itself is declared in object code that comes from the library. Thus, usage of the TI library header file <ctype.h> means you must link with a TI RTS library. The same is true of the Dinkum library.

Observant readers may note that you aren't necessarily in trouble yet. The code that executes that return statement comes from the same library that defines the table. True enough. However, if you build with optimization (-o2 or higher) then the isspace function is inlined, and then a direct reference to the character properties table does get compiled into user code. And thus the name of the table in user code and the name of the table that comes from the RTS object library must match.

This problem only exists in the ARM compiler v4.4.X releases. None of the other TI compilers have this problem.

Name Mangling

Name mangling is a detail of how C++ code is built. It is a detail which must be understood to be able to combine C++ code with other code. Examples of other code include C, assembly, XDAIS algorithms (which can only be in C), BIOS, etc. This section explains why name mangling is necessary and how to deal with it.

One feature of C++ is called function overloading. This feature allows you to provide different implementations of the same function that can operate on different arguments.

typedef class complex {} cx;
 
cx cx_add(cx op1, cx op2);
cx cx_add(int op1, cx op2);
cx cx_add(cx op1, int op2);

Support for this feature is implemented by what is called name mangling. The names of the functions are mangled to guarantee uniqueness. The mangling takes into account the function name, type and number of arguments, as well as the return type. For example, the mangled name of the first function above is _cx_add__F7complexT1.

The algorithm for name mangling is quite complicated, and generally impossible to duplicate manually. Rather than attempt that, use the C linkage convention to defeat the name mangling. The syntax can be applied to a single function declaration:

extern "C" void func(int arg);

Or to a group of function declarations:

extern "C" {                   // linkage block
void func1(int arg);
void func2(int arg);
...
}

Functions declared extern "C" are not subject to name mangling. They are converted to assembly names by using the C compiler convention of prepending an underscore. For example, func1 becomes _func1. Note, however, even the C compiler convention for how C variable and function names are converted to assembly names is different under EABI. Under EABI, the name is func1 in both C and assembly code.

Note other code can be in the linkage block, but only the function declarations are affected. Such declarations are normally placed in a header file that is then included any place those functions are defined or called. For a header file that can be shared between C++ and C code, that typically looks like:

#ifdef __cplusplus
extern "C" {
#endif
 
// Standard "C" code here
 
#ifdef __cplusplus
}
#endif

Note the special name __cplusplus is automatically defined when building with any C++ compiler.

On a related note: If you are looking at mangled names in assembly, map files, etc. that came from C++ code, you can use the C++ Demangler tool to convert the mangled names into how they look in C++. The C6000 tool, for instance, is named dem6x. Consult your compiler User's Guide for more detail.

Implementing XDAIS Algorithms in C++

The XDAIS standard was developed to be object-based (usage of handles to create instances of algorithms) but not object oriented. For example IALG, used for memory requests abstraction, is a C interface.

At present, XDAIS algorithms can be invoked from a C++ application, but cannot be developed and supplied via C++.

BIOS and C++

BIOS is implemented as a combination of C and assembly code. Any C++ function called from BIOS code has to be declared with the extern "C" linkage convention. This includes functions referred to in gconf or tconf BIOS configurations. For more detail, see the Name Mangling section above.

BIOS header files can be used in C++ code because they use the


# ifdef __cplusplus technique described in the Name Mangling section.

Additional details on using BIOS with C++ can be found in section 2.8 of the BIOS User's Guide. For instance, the new and delete operators are implemented with MEM_alloc and MEM_free in order to support reentrancy.

A BIOS C++ example using classes and member functions is available. See the bigtime example supplied with Code Composer Studio. It can be found in the directory: CCS_install_root\bios_version\packages\ti\bios\examples\basic\bigtime.

Other issues

C++ Template Instantiation Issues

C++ Inlining Issues