TMS320C28x Optimizing C/C++ Compiler v15.9.0.STS User's Guide
SPRU514 - REVISED SEPTEMBER, 2015

6 TMS320C28x C/C++ Language Implementation

The C/C++ compiler supports the C/C++ language standard that was developed by a committee of the American National Standards Institute (ANSI) and subsequently adopted by the International Standards Organization (IS0).

The C++ language supported by the C28x is defined by the ANSI/ISO/IEC 14882:2003 standard with certain exceptions.

6.1 Characteristics of TMS320C28x C

The C compiler supports the 1989 and 1999 versions of the C language:

  • C89. Compiling with the --c89 option causes the compiler to conform to the ISO/IEC 9899:1990 C standard, which was previously ratified as ANSI X3.159-1989. The names "C89" and "C90" refer to the same programming language. "C89" is used in this document.
  • C99. Compiling with the --c99 option causes the compiler to conform to the ISO/IEC 9899:1999 C standard. This standard supports several features not part of C89, such as inline functions, new data types, and one-line comments beginning with //.

The C language is also described in the second edition of Kernighan and Ritchie's The C Programming Language (K&R). The compiler can also accept many of the language extensions found in the GNU C compiler (see Section 6.14).

The compiler supports some features of C99 in the default relaxed ANSI mode with C89 support. It supports all language features of C99 in C99 mode. See Section 6.13.

The ANSI/ISO standard identifies some features of the C language that may be affected by characteristics of the target processor, run-time environment, or host environment. This set of features can differ among standard compilers.

Unsupported features of the C library are:

  • The run-time library has minimal support for wide and multibyte characters. The type wchar_t is implemented as int. The wide character set is equivalent to the set of values of type char. The library includes the header files <wchar.h> and <wctype.h>, but does not include all the functions specified in the standard.
  • The run-time library includes the header file <locale.h>, but with a minimal implementation. The only supported locale is the C locale. That is, library behavior that is specified to vary by locale is hard-coded to the behavior of the C locale, and attempting to install a different locale by way of a call to setlocale() will return NULL.
  • Some run-time functions and features in the C99 specification are not supported. See Section 6.13.

6.1.1 Implementation-Defined Behavior

The C standard requires that conforming implementations provide documentation on how the compiler handles instances of implementation-defined behavior.

The TI compiler officially supports a freestanding environment. The C standard does not require a freestanding environment to supply every C feature; in particular the library need not be complete. However, the TI compiler strives to provide most features of a hosted environment.

The section numbers in the lists that follow correspond to section numbers in Appendix J of the C99 standard. The numbers in parentheses at the end of each item are sections in the C99 standard that discuss the topic. Certain items listed in Appendix J of the C99 standard have been omitted from this list.

J.3.1 Translation

  • The compiler and related tools emit diagnostic messages with several distinct formats. Diagnostic messages are emitted to stderr; any text on stderr may be assumed to be a diagnostic. If any errors are present, the tool will exit with an exit status indicating failure (non-zero). (3.10, 5.1.1.3)
  • Nonempty sequences of white-space characters are preserved and are not replaced by a single space character in translation phase 3. (5.1.1.2)

J.3.2 Environment

  • The compiler does not in general support multibyte characters in the physical source file, so there is no mapping from multibyte characters to the source character set. However, in some locales, the --multibyte_chars compiler option makes the compiler accept multibyte characters in comments, string literals, and character constants in the physical source file. (5.1.1.2)
  • The name of the function called at program startup is "main" (5.1.2.1)
  • Program termination does not affect the environment; there is no way to return an exit code to the environment. By default, the program is known to have halted when execution reaches the special C$$EXIT label. (5.1.2.1)
  • In relaxed ANSI mode, the compiler accepts "void main(void)" and "void main(int argc, char *argv[])" as alternate definitions of main. The alternate definitions are rejected in strict ANSI mode. (5.1.2.2.1)
  • If space is provided for program arguments at link time with the --args option and the program is run under a system that can populate the .args section (such as CCS), argv[0] will contain the filename of the executable, argv[1] through argv[argc-1] will contain the command-line arguments to the program, and argv[argc] will be NULL. Otherwise, the value of argv and argc are undefined. (5.1.2.2.1)
  • Interactive devices include stdin, stdout, and stderr (when attached to a system that honors CIO requests). Interactive devices are not limited to those output locations; the program may access hardware peripherals that interact with the external state. (5.1.2.3)
  • Signals are not supported. The function signal is not supported. (7.14) (7.14.1.1)
  • The library function getenv is implemented through the CIO interface. If the program is run under a system that supports CIO, the system performs getenv calls on the host system and passes the result back to the program. Otherwise the operation of getenv is undefined. No method of changing the environment from inside the target program is provided. (7.20.4.5)
  • The system function is not supported. (7.20.4.6).

J.3.3. Identifiers

  • The compiler does not support multibyte characters in identifiers. (6.4.2)
  • The number of significant initial characters in an identifier is unlimited. (5.2.4.1, 6.4.2)

J.3.4 Characters

  • The number of bits in a byte (CHAR_BIT) is 16. See Section 6.4 for details about data types. (3.6)
  • The execution character set is the same as the basic execution character set: plain ASCII. (5.2.1)
  • The values produced for the standard alphabetic escape sequences are as follows: (5.2.2)
  • Escape Sequence ASCII Meaning Integer Value
    \a BEL (bell) 7
    \b BS (backspace) 8
    \f FF (form feed) 12
    \n LF (line feed) 10
    \r CR (carriage return) 13
    \t HT (horizontal tab) 9
    \v VT (vertical tab) 11
  • The value of a char object into which any character other than a member of the basic execution character set has been stored is the ASCII value of that character. (6.2.5)
  • Plain char is identical to signed char. (6.2.5, 6.3.1.1)
  • The source character set and execution character set are both plain ASCII, so the mapping between them is one-to-one. This behavior can be modified using the --multibyte_chars option. (6.4.4.4, 5.1.1.2)
  • The compiler currently supports only one locale, "C". (6.4.4.4).
  • The compiler currently supports only one locale, "C". (6.4.5).

J.3.5 Integers

  • No extended integer types are provided. (6.2.5)
  • Integer types are represented as two's complement, and there are no trap representations. (6.2.6.2)
  • No extended integer types are provided, so there is no change to the integer ranks. (6.3.1.1)
  • When an integer is converted to a signed integer type which cannot represent the value, the value is truncated (without raising a signal) by discarding the bits which cannot be stored in the destination type; the lowest bits are not modified. (6.3.1.3)
  • Right shift of a signed integer value performs an arithmetic (signed) shift. The bitwise operations other than right shift operate on the bits in exactly the same way as on an unsigned value. That is, after the usual arithmetic conversions, the bitwise operation is performed without regard to the format of the integer type, in particular the sign bit. (6.5)

J.3.6 Floating point

  • The accuracy of floating-point operations (+ - * /) is bit-exact. The accuracy of library functions that return floating-point results is not specified. (5.2.4.2.2)
  • The compiler does not provide non-standard values for FLT_ROUNDS (5.2.4.2.2)
  • The compiler does not provide non-standard negative values of FLT_EVAL_METHOD (5.2.4.2.2)
  • The rounding direction when an integer is converted to a floating-point number is IEEE-754 "round to even". (6.3.1.4)
  • The rounding direction when a floating-point number is converted to a narrower floating-point number is IEEE-754 "round to even". (6.3.1.5)
  • For floating-point constants that are not exactly representable, the implementation uses the nearest representable value. (6.4.4.2)
  • The compiler does not contract float expressions. (6.5)
  • The default state for the FENV_ACCESS pragma is off. (7.6.1)
  • The TI compiler does not define any additional float exceptions (7.6, 7.12)
  • The default state for the FP_CONTRACT pragma is off. (7.12.2)
  • The "inexact" floating-point exception cannot be raised if the rounded result equals the mathematical result. (F.9)
  • The "underflow" and "inexact" floating-point exceptions cannot be raised if the result is tiny but not inexact. (F.9)

J.3.7 Arrays and pointers

  • When converting a pointer to an integer or vice versa, the pointer is considered an unsigned integer of the same size, and the normal integer conversion rules apply. Some pointers are not the same size as any integral type, but the conversion proceeds as if such a type did exist, with the rules implied by normal integer conversion.
  • When converting a pointer to an integer or vice versa, if the bitwise representation of the destination can hold all of the bits in the bitwise representation of the source, the bits are copied exactly. (6.3.2.3)
  • The size of the result of subtracting two pointers to elements of the same array is the size of ptrdiff_t, which is defined in Section 6.4. (6.5.6)

J.3.8 Hints

  • When the optimizer is used, the register storage-class specifier is ignored. When the optimizer is not used, the compiler will preferentially place register storage class objects into registers to the extent possible. The compiler reserves the right to place any register storage class object somewhere other than a register. (6.7.1)
  • The inline function specifier is ignored unless the optimizer is used. For other restrictions on inlining, see Section 2.11.4. (6.7.4)

J.3.9 Structures, unions, enumerations, and bit-fields

  • A "plain" int bit-field is treated as a signed int bit-field. (6.7.2, 6.7.2.1)
  • In addition to _Bool, signed int, and unsigned int, the compiler allows char, signed char, unsigned char, signed short, unsigned shot, signed long, unsigned long, signed long long, unsigned long long, and enum types as bit-field types. (6.7.2.1)
  • Bit-fields may not straddle a storage-unit boundary.(6.7.2.1)
  • Bit-fields are allocated in endianness order within a unit. (6.7.2.1)
  • Non-bit-field members of structures are aligned as specified in See . (6.7.2.1)
  • The integer type underlying each enumerated type is described in Section 6.4.1. (6.7.2.2)

J.3.10 Qualifiers

  • The TI compiler does not shrink or grow volatile accesses. It is the user's responsibility to make sure the access size is appropriate for devices that only tolerate accesses of certain widths. The TI compiler does not change the number of accesses to a volatile variable unless absolutely necessary. This is significant for read-modify-write expressions such as += ; for an architecture which does not have a corresponding read-modify-write instruction, the compiler will be forced to use two accesses, one for the read and one for the write. Even for architectures with such instructions, it is not guaranteed that the compiler will be able to map such expressions to an instruction with a single memory operand. It is not guaranteed that the memory system will lock that memory location for the duration of the instruction. In a multi-core system, some other core may write the location after a RMW instruction reads it, but before it writes the result. The TI compiler will not reorder two volatile accesses, but it may reorder a volatile and a non-volatile access, so volatile cannot be used to create a critical section. Use some sort of lock if you need to create a critical section. (6.7.3)

J.3.11 Preprocessing directives

  • Include directives may have one of two forms, " " or < >. For both forms, the compiler will look for a real file on-disk by that name using the include file search path. See Section 2.5.2. (6.4.7).
  • The value of a character constant in a constant expression that controls conditional inclusion matches the value of the same character constant in the execution character set (both are ASCII). (6.10.1).
  • The compiler uses the file search path to search for an included < > delimited header file. See Section 2.5.2. (6.10.2).
  • he compiler uses the file search path to search for an included " " delimited header file. See Section 2.5.2. (6.10.2). (6.10.2).
  • There is no arbitrary nesting limit for #include processing. (6.10.2).
  • See Section 6.9 for a description of the recognized non-standard pragmas. (6.10.6).
  • The date and time of translation are always available from the host. (6.10.8).

J.3.12 Library functions

  • Almost all of the library functions required for a hosted implementation are provided by the TI library, with exceptions noted in Section 6.13.1. (5.1.2.1).
  • The format of the diagnostic printed by the assert macro is "Assertion failed, (assertion macro argument), file file, line line". (7.2.1.1).
  • No strings other than "C" and "" may be passed as the second argument to the setlocale function (7.11.1.1).
  • No signal handling is supported. (7.14.1.1).
  • The +INF, -INF, +inf, -inf, NAN, and nan styles can be used to print an infinity or NaN. (7.19.6.1, 7.24.2.1).
  • The output for %p conversion in the fprintf or fwprintf function is the same as %x of the appropriate size. (7.19.6.1, 7.24.2.1).
  • The termination status returned to the host environment by the abort, exit, or _Exit function is not returned to the host environment. (7.20.4.1, 7.20.4.3, 7.20.4.4).
  • The system function is not supported. (7.20.4.6).

J.3.13 Architecture

  • The values or expressions assigned to the macros specified in the headers float.h, limits.h, and stdint.h are described along with the sizes and format of integer types are described in Section 6.4. (5.2.4.2, 7.18.2, 7.18.3)
  • The number, order, and encoding of bytes in any object are described in . (6.2.6.1)
  • The value of the result of the sizeof operator is the storage size for each type, in terms of bytes. See . (6.5.3.4)

6.2 Characteristics of TMS320C28x C++

The C28x compiler supports C++ as defined in the ANSI/ISO/IEC 14882:2003 standard, including these features:

  • Complete C++ standard library support, with exceptions noted below.
  • Templates
  • Exceptions, which are enabled with the --exceptions option; see Section 6.6.
  • Run-time type information (RTTI), which can be enabled with the --rtti compiler option.

The exceptions to the standard are as follows:

  • The compiler does not support embedded C++ run-time-support libraries.
  • The <complex> header and its functions are not included in the library.
  • The library supports wide chars (wchar_t), in that template functions and classes that are defined for char are also available for wchar_t. For example, wide char stream classes wios, wiostream, wstreambuf and so on (corresponding to char classes ios, iostream, streambuf) are implemented. However, there is no low-level file I/O for wide chars. Also, the C library interface to wide char support (through the C++ headers <cwchar> and <cwctype>) is limited as described above in the C library.
  • If the definition of an inline function contains a static variable, and it appears in multiple compilation units (usually because it’s a member function of a class defined in a header file), the compiler generates multiple copies of the static variable rather than resolving them to a single definition. The compiler emits a warning (#1369) in such cases.
  • Two-phase name binding in templates, as described in [tesp.res] and [temp.dep] of the standard, is not implemented.
  • The export keyword for templates is not implemented.
  • A typedef of a function type cannot include member function cv-qualifiers.
  • A partial specialization of a class member template cannot be added outside of the class definition.

6.3 Using MISRA C:2004

MISRA C is a set of software development guidelines for the C programming language. It promotes best practices in developing safety-related electronic systems in road vehicles and other embedded systems. MISRA C was originally launched in 1998 by the Motor Industry Software Reliability Association, and has since been adopted across a wide variety of industries. A subsequent update to the guidelines was publishes as MISRA C:2004

You can alter your code to work with the MISRA C:2004 rules. The following options and pragmas enable/disable the rules:

  • The --check_misra option enables checking of the specified MISRA C:2004 rules.
  • The CHECK_MISRA pragma enables/disables MISRA C:2004 rules at the source level. This pragma is equivalent to using the --check_misra option. See Section 6.9.1.
  • RESET_MISRA pragma resets the specified MISRA C:2004 rules to their state before any CHECK_MISRA pragmas were processed. See Section 6.9.16.

The syntax of the option and pragmas is:

--check_misra={all|required|advisory|none|rulespec}
#pragma CHECK_MISRA ("{all|required|advisory|none|rulespec}")
#pragma RESET_MISRA ("{all|required|advisory|rulespec}")

The rulespec parameter is a comma-separated list of rule numbers to enable.

Example: --check_misra=1.1,1.4,1.5,2.1,2.7,7.1,7.2,8.4

  • Enables rules 1.1, 1.4, 1.5, 2.1, 2.7, 7.1, 7.2, and 8.4.

Two options control the severity of certain MISRA C:2004 rules:

  • The --misra_required option sets the diagnostic severity for required MISRA C:2004 rules.
  • The --misra_advisory option sets the diagnostic severity for advisory MISRA C:2004 rules.

The syntax for these options is:

--misra_advisory={error|warning|remark|suppress}
--misra_required={error|warning|remark|suppress}

6.4 Data Types

Table 6-1 lists the size, representation, and range of each scalar data type for the C28x compiler. Many of the range values are available as standard macros in the header file limits.h.

Table 6-1 TMS320C28x C/C++ Data Types

Range
Type Size Representation Minimum Maximum
char, signed char 16 bits ASCII -32 768 32 767
unsigned char, _Bool 16 bits ASCII 0 65 535
short 16 bits 2s complement -32 768 32 767
unsigned short 16 bits Binary 0 65 535
int, signed int 16 bits 2s complement -32 768 32 767
unsigned int 16 bits Binary 0 65 535
long, signed long 32 bits 2s complement -2 147 483 648 2 147 483 647
unsigned long 32 bits Binary 0 4 294 967 295
long long, signed long long 64 bits 2s complement -9 223 372 036 854 775 808 9 223 372 036 854 775 807
unsigned long long 64 bits Binary 0 18 446 744 073 709 551 615
enum (1) 16 bits 2s complement -32 768 32 767
float 32 bits IEEE 32-bit 1.19 209 290e-38(2) 3.40 282 35e+38
double 32 bits IEEE 32-bit 1.19 209 290e-38(2) 3.40 282 35e+38
long double 64 bits IEEE 64-bit 2.22 507 385e-308(2) 1.79 769 313e+308
pointers (3) 32 bits Binary 0 0xFFFFFFFF
(1) For details about the size of an enum type, see Section 6.4.1.
(2) Figures are minimum precision.
(3) Even though pointers are 32-bits, the compiler assumes that the addresses of global variables and functions are within 22-bits.

NOTE

TMS320C28x Byte is 16 Bits

By ANSI/ISO C definition, the sizeof operator yields the number of bytes required to store an object. ANSI/ISO further stipulates that when sizeof is applied to char, the result is 1. Since the TMS320C28x char is 16 bits (to make it separately addressable), a byte is also 16 bits. This yields results you may not expect; for example, size of (int) = = 1 (not 2). TMS320C28x bytes and words are equivalent (16 bits). To access data in increments of 8 bits, use the __byte() and __mov_byte() intrinsics described in Section 7.5.6.

6.4.1 Size of Enum Types

An enum type is represented by an underlying integer type. The size of the integer type and whether it is signed is based on the range of values of the enumerated constants.

In strict C89 or C99 mode, the compiler allows only enumeration constants with values that will fit in "int" or "unsigned int".

For C++ and relaxed C89/C99, the compiler allows enumeration constants up to the largest integral type (64 bits). The default, which is recommended, is for the underlying type to be the first type in the following list in which all the enumerated constant values can be represented: int, unsigned int, long, unsigned long long long, unsigned long long.

6.4.2 Support for 64-Bit Integers

The TMS320C28x compiler supports the long long and unsigned long long data types. The range values are available as standard macros in the header file limits.h.

The long long data types are stored in register pairs. In memory they are stored as 64-bit objects at word (32-bit) aligned addresses.

A long long integer constant can have an ll or LL suffix. Without the suffix the value of the constant determines the type of the constant.

The formatting rules for long long in C I/O require ll in the format string. For example:

printf("%lld", 0x0011223344556677); printf("%llx", 0x0011223344556677);

The run-time-support library functions, llabs(), strtoll() and strtoull(), are added.

6.4.3 C28x long double Floating-Point Type Change

When compiling C/C++ code for the TMS320C28x only, the long double floating point type is now IEEE 64-bit double precision. No other floating-point types have changed formats. C28x floating point types are:

Type Format
float IEEE 32-bit single precision
double IEEE 32-bit single precision
long double IEEE 64-bit double precision

When you initialize a long double to a constant, you must use the l or L suffix. The constant is treated as a double type without the suffix and the run-time support double-to-long conversion routine is called for the initialization. This could result in the loss of precision. For example:

long double a = 12.34L; /* correctly initializes to double precision */ long double b = 56.78; /* converts single precision value to double precision */

The formatting rules for long doubles in C I/O require a capital ’L’ in the format string. For example:

printf("%Lg", 1.23L); printf("%Le", 3.45L);

In response to the change in the long double type to 64-bit IEEE double-precision format, the C28x calling conventions have changed.

All long double arguments are passed by reference. A long double return value is returned by reference. The first two long double arguments will pass their addresses in XAR4 and XAR5. All other long double arguments will have their addresses passed on the stack.

If a function returns a long double, the function making that call will place the return address in XAR6. For example:

long double foo(long double a, long double b, long double c) { long double d = a + b + c; return d; } long double a = 1.2L; long double b = 2.2L; long double c = 3.2L; long double d; void bar() { d = foo(a, b, c); }

In function bar(), at the call to foo(), the register values are:

Register Equals
XAR4 The address of a
XAR5 The address of b
*−SP[2] The address of c
XAR6 The address of d

The run-time-support library has been updated to include the necessary long double arithmetic operations and conversion functions. All C28x floating-point run-time-support routines have had their names updated. For example, a previous call to the floating point add routine was:

LCR F$$ADD

This has been updated to:

LCR FS$$ADD ; single-precision add LCR FD$$ADD ; double-precision add

Any C28x routine that calls floating-point arithmetic or conversion routines will need to be recompiled.

6.5 Keywords

The C28x C/C++ compiler supports all of the standard C89 keywords, including const, volatile, and register. It also supports all of the standard C99 keywords, including inline and restrict. It also supports TI extension keywords __interrupt, __cregister, and __asm. The compiler supports the restrict keyword for FPU targets; for other targets restrict is ignored. Some keywords are not available in strict ANSI mode.

The following keywords may appear in other target documentation and require the same treatment as the interrupt and restrict keywords:

  • trap
  • reentrant
  • cregister

6.5.1 The const Keyword

The C/C++ compiler supports the ANSI/ISO standard keyword const in all modes except K&R compatibility mode (--kr_compatible).

This keyword gives you greater optimization and control over allocation of storage for certain data objects. You can apply the const qualifier to the definition of any variable or array to ensure that its value is not altered.

Global objects qualified as const are placed in the .econst section. The linker allocates the .econst section from ROM or FLASH, which are typically more plentiful than RAM. The const data storage allocation rule has two exceptions:

  • If the keyword volatile is also specified in the definition of an object (for example, volatile const int x). Volatile keywords are assumed to be allocated to RAM. (The program is not allowed to modify a const volatile object, but something external to the program might.)
  • If the object has automatic storage (function scope).

In both cases, the storage for the object is the same as if the const keyword were not used.

The placement of the const keyword within a definition is important. For example, the first statement below defines a constant pointer p to a modifiable int. The second statement defines a modifiable pointer q to a constant int:

int * const p = &x; const int * q = &x;

Using the const keyword, you can define large constant tables and allocate them into system ROM. For example, to allocate a ROM table, you could use the following definition:

const int digits[] = {0,1,2,3,4,5,6,7,8,9};

6.5.2 The __cregister Keyword

The compiler extends the C/C++ language by adding the cregister keyword to allow high level language access to control registers. This keyword is available in normal mode, but not in strict ANSI/ISO mode (using the --strict_ansi compiler option). The alternate keyword, __cregister, provides the same functionality but is available in either strict ANSI/ISO mode or normal mode.

When you use the cregister keyword on an object, the compiler compares the name of the object to a list of standard control registers for the C28x (see Table 6-2). If the name matches, the compiler generates the code to reference the control register. If the name does not match, the compiler issues an error.

Table 6-2 Valid Control Registers

Register Description
IER Interrupt enable register
IFR Interrupt flag register

The cregister keyword can be used only in file scope. The cregister keyword is not allowed on any declaration within the boundaries of a function. It can only be used on objects of type integer or pointer. The cregister keyword is not allowed on objects of any floating-point type or on any structure or union objects.

The cregister keyword does not imply that the object is volatile. If the control register being referenced is volatile (that is, can be modified by some external control), then the object must be declared with the volatile keyword also.

To use the control registers in Table 6-2, you must declare each register as follows. The c28x.h include file defines all the control registers through this syntax:

extern cregister volatile unsigned intregister;

Once you have declared the register, you can use the register name directly, though in a limited manner. IFR is read only and can be set only by using the | (OR) operation with an immediate. IFR can be cleared only by using the & (AND) operation with an immediate. For example:

IFR |= 0x4; IFR &= 0x0800

The IER register also can be used in an assignment other than OR and AND. Since the C28x architecture has limited instructions to manipulate these registers, the compiler terminates with the following message if illegal use of these registers is found:

>>> Illegal use of control register

See Example 6-1 for an example that declares and uses control registers.

Example 6-1 Define and Use Control Registers

extern cregister volatile unsigned int IFR; extern cregister volatile unsigned int IER; extern int x; main() { IER = x; IER |= 0x100; printf("IER = %x\n", IER); IFR &= 0x100; IFR |= 0x100;

6.5.3 The __interrupt Keyword

The compiler extends the C/C++ language by adding the __interrupt keyword, which specifies that a function is treated as an interrupt function. This keyword is an IRQ interrupt. The alternate keyword, "interrupt", may also be used except in strict ANSI C or C++ modes.

Note that the interrupt function attribute described in Section 6.9.13 is the recommended syntax for declaring interrupt functions.

Functions that handle interrupts follow special register-saving rules and a special return sequence. The implementation stresses safety. The interrupt routine does not assume that the C run-time conventions for the various CPU register and status bits are in effect; instead, it re-establishes any values assumed by the run-time environment. When C/C++ code is interrupted, the interrupt routine must preserve the contents of all machine registers that are used by the routine or by any function called by the routine. When you use the __interrupt keyword with the definition of the function, the compiler generates register saves based on the rules for interrupt functions and the special return sequence for interrupts.

You can only use the __interrupt keyword with a function that is defined to return void and that has no parameters. The body of the interrupt function can have local variables and is free to use the stack or global variables. For example:

__interrupt void int_handler() { unsigned int flags; ... }

The name c_int00 is the C/C++ entry point. This name is reserved for the system reset interrupt. This special interrupt routine initializes the system and calls the main() function. Because it has no caller, c_int00 does not save any registers.

NOTE

Hwi Objects and the __interrupt Keyword

The __interrupt keyword must not be used when SYS/BIOS Hwi objects are used in conjunction with C functions. The Hwi_enter/Hwi_exit macros and the Hwi dispatcher already contain this functionality, and the use of the C modifier can cause unwanted conflicts.

6.5.4 The restrict Keyword

To help the compiler determine memory dependencies, you can qualify a pointer, reference, or array with the restrict keyword. The restrict keyword is a type qualifier that can be applied to pointers, references, and arrays. Its use represents a guarantee by you, the programmer, that within the scope of the pointer declaration the object pointed to can be accessed only by that pointer. Any violation of this guarantee renders the program undefined. This practice helps the compiler optimize certain sections of code because aliasing information can be more easily determined.

In Example 6-2, the restrict keyword is used to tell the compiler that the function func1 is never called with the pointers a and b pointing to objects that overlap in memory. You are promising that accesses through a and b will never conflict; therefore, a write through one pointer cannot affect a read from any other pointers. The precise semantics of the restrict keyword are described in the 1999 version of the ANSI/ISO C Standard.

The "restrict" keyword is a C99 keyword, and cannot be accepted in strict ANSI C89 mode. Use the "__restrict" keyword if the strict ANSI C89 mode must be used. See Section 6.13.

Example 6-2 Use of the restrict Type Qualifier With Pointers

void func1(int * restrict a, int * restrict b) { /* func1's code here */ }

Example 6-3 illustrates using the restrict keyword when passing arrays to a function. Here, the arrays c and d must not overlap, nor may c and d point to the same array.

Example 6-3 Use of the restrict Type Qualifier With Arrays

void func2(int c[restrict], int d[restrict]) { int i; for(i = 0; i < 64; i++) { c[i] += d[i]; d[i] += 1; } }

At this time the restrict keyword is useful only for FPU targets. For non-FPU targets restrict is ignored.

6.5.5 The volatile Keyword

The C/C++ compiler supports the volatile keyword in all modes except K&R compatibility mode (--kr_compatible). In addition, the __volatile keyword is supported in relaxed ANSI mode for C89, C99, and C++.

The volatile keyword indicates to the compiler that there is something about how the variable is accessed that requires that the compiler not use overly-clever optimization on expressions involving that variable. For example, the variable may also be accessed by an external program, an interrupt, another thread, or a peripheral device.

The compiler eliminates redundant memory accesses whenever possible, using data flow analysis to figure out when it is legal. However, some memory accesses may be special in some way that the compiler cannot see, and in such cases you should use the volatile keyword to prevent the compiler from optimizing away something important. The compiler does not optimize out any accesses to variables declared volatile. The number of volatile reads and writes will be exactly as they appear in the C/C++ code, no more and no less and in the same order.

Any variable which might be modified by something external to the obvious control flow of the program (such as an interrupt service routine) must be declared volatile. This tells the compiler that an interrupt function might modify the value at any time, so the compiler should not perform optimizations which will change the number or order of accesses of that variable. This is the primary purpose of the volatile keyword. In the following example, the loop intends to wait for a location to be read as 0xFF:

unsigned int *ctrl; while (*ctrl !=0xFF);

However, in this example, *ctrl is a loop-invariant expression, so the loop is optimized down to a single-memory read. To get the desired result, define ctrl as:

volatile unsigned int *ctrl;

Here the *ctrl pointer is intended to reference a hardware location, such as an interrupt flag.

The volatile keyword must also be used when accessing memory locations that represent memory-mapped peripheral devices. Such memory locations might change value in ways that the compiler cannot predict. These locations might change if accessed, or when some other memory location is accessed, or when some signal occurs.

Volatile must also be used for local variables in a function which calls setjmp, if the value of the local variables needs to remain valid if a longjmp occurs.

Example 6-4 Volatile for Local Variables With setjmp

#include <stdlib.h>jmp_buf context; void function() { volatile int x = 3; switch(setjmp(context)) { case 0: setup(); break; default: { printf("x == %d\n", x); /* We can only reach here if longjmp has occurred; because x's lifetime begins before the setjmp and lasts through the longjmp, the C standard requires x be declared "volatile" */ break; } } }

The --unified_memory option can be used if your memory map is configured as a single unified space; this option allows the compiler to generate more efficient instructions for most memcpy calls and structure assignments. Even under unified memory, memory for some peripherals and some RAM associated with those peripherals is allocated only in data memory. If –unified_memory is enabled, you can prevent program memory address access to specific symbols by declaring those symbols as volatile.

6.6 C++ Exception Handling

The compiler supports all the C++ exception handling features as defined by the ANSI/ISO 14882 C++ Standard. More details are discussed in The C++ Programming Language, Third Edition by Bjarne Stroustrup.

The compiler --exceptions option enables exception handling. The compiler’s default is no exception handling support.

For exceptions to work correctly, all C++ files in the application must be compiled with the --exceptions option, regardless of whether exceptions occur in a particular file. Mixing exception-enabled object files and libraries with object files and libraries that do not have exceptions enabled can lead to undefined behavior.

Exception handling requires support in the run-time-support library, which come in exception-enabled and exception-disabled forms; you must link with the correct form. When using automatic library selection (the default), the linker automatically selects the correct library Section 4.3.1.1. If you select the library manually, you must use run-time-support libraries whose name contains _eh if you enable exceptions.

Using the --exceptions option causes the compiler to insert exception handling code. This code will increase the size of the programand execution time, even if no exceptions are thrown.

See Section 8.1 for details on the run-time libraries.

6.7 Register Variables and Parameters

The C/C++ compiler treats register variables (variables defined with the register keyword) differently, depending on whether you use the --opt_level (-O) option.

  • Compiling with optimization
  • The compiler ignores any register definitions and allocates registers to variables and temporary values by using an algorithm that makes the most efficient use of registers.

  • Compiling without optimization
  • If you use the register keyword, you can suggest variables as candidates for allocation into registers. The compiler uses the same set of registers for allocating temporary expression results as it uses for allocating register variables.

The compiler attempts to honor all register definitions. If the compiler runs out of appropriate registers, it frees a register by moving its contents to memory. If you define too many objects as register variables, you limit the number of registers the compiler has for temporary expression results. This limit causes excessive movement of register contents to memory.

Any object with a scalar type (integral, floating point, or pointer) can be defined as a register variable. The register designator is ignored for objects of other types, such as arrays.

The register storage class is meaningful for parameters as well as local variables. Normally, in a function, some of the parameters are copied to a location on the stack where they are referenced during the function body. The compiler copies a register parameter to a register instead of the stack, which speeds access to the parameter within the function.

For more information about register conventions, see Section 7.2.

6.8 The __asm Statement

The C/C++ compiler can embed assembly language instructions or directives directly into the assembly language output of the compiler. This capability is an extension to the C/C++ language implemented through the __asm keyword. The __asm keyword provides access to hardware features that C/C++ cannot provide.

The alternate keyword, "asm", may also be used except in strict ANSI C mode. It is available in relaxed C and C++ modes.

Using __asm is syntactically performed as a call to a function named __asm, with one string constant argument:

__asm("assembler text");

The compiler copies the argument string directly into your output file. The assembler text must be enclosed in double quotes. All the usual character string escape codes retain their definitions. For example, you can insert a .byte directive that contains quotes as follows:

__asm("STR: .byte \"abc\"");

The inserted code must be a legal assembly language statement. Like all assembly language statements, the line of code inside the quotes must begin with a label, a blank, a tab, or a comment (asterisk or semicolon). The compiler performs no checking on the string; if there is an error, the assembler detects it. For more information about the assembly language statements, see the TMS320C28x Assembly Language Tools User's Guide.

The __asm statements do not follow the syntactic restrictions of normal C/C++ statements. Each can appear as a statement or a declaration, even outside of blocks. This is useful for inserting directives at the very beginning of a compiled module.

The __asm statement does not provide any way to refer to local variables. If your assembly code needs to refer to local variables, you will need to write the entire function in assembly code.

For more information, refer to Section 7.5.5.

NOTE

Avoid Disrupting the C/C++ Environment With asm Statements

Be careful not to disrupt the C/C++ environment with __asm statements. The compiler does not check the inserted instructions. Inserting jumps and labels into C/C++ code can cause unpredictable results in variables manipulated in or around the inserted code. Directives that change sections or otherwise affect the assembly environment can also be troublesome.

Be especially careful when you use optimization with __asm statements. Although the compiler cannot remove __asm statements, it can significantly rearrange the code order near them and cause undesired results.

NOTE

Use Single asm Statement for the RPT Instruction

When adding a C28x RPT instruction, do not use a separate asm statement for RPT and the repeated instruction. The compiler could insert debug directives between asm directives and the assembler does not allow any directives between the RPT and the repeated instruction. For example, to insert a RPT MAC instruction, use the following:

asm("\tRPT #10\n\t||MAC P, *XAR4++, *XAR7++");

6.9 Pragma Directives

Pragma directives tell the compiler how to treat a certain function, object, or section of code. The C28x C/C++ compiler supports the following pragmas:

The arguments func and symbol cannot be defined or declared inside the body of a function. You must specify the pragma outside the body of a function; and the pragma specification must occur before any declaration, definition, or reference to the func or symbol argument. If you do not follow these rules, the compiler issues a warning and may ignore the pragma.

For pragmas that apply to functions or symbols, the syntax differs between C and C++.

  • In C, you must supply the name of the object or function to which you are applying the pragma as the first argument. Because the entity operated on is specified, a pragma in C can appear some distance way from the definition of that entity.
  • In C++, pragmas are positional. They do not name the entity on which they operate as an argument. Instead, they always operate on the next entity defined after the pragma.

6.9.1 The CHECK_MISRA Pragma

The CHECK_MISRA pragma enables/disables MISRA C:2004 rules at the source level. This pragma is equivalent to using the --check_misra option.

The syntax of the pragma in C is:

#pragma CHECK_MISRA (" {all|required|advisory|none|rulespec} ")

The rulespec parameter is a comma-separated list of rule numbers. See Section 6.3 for details.

The RESET_MISRA pragma can be used to reset any CHECK_MISRA pragmas; see Section 6.9.16.

6.9.2 The CLINK Pragma

The CLINK pragma can be applied to a code or data symbol. It causes a .clink directive to be generated into the section that contains the definition of the symbol. The .clink directive tells the linker that a section is eligible for removal during conditional linking. Thus, if the section is not referenced by any other section in the application being compiled and linked, it will not be included in the resulting output file.

The syntax of the pragma in C is:

#pragma CLINK (symbol )

The syntax of the pragma in C++ is:

#pragma CLINK

The RETAIN pragma has the opposite effect of the CLINK pragma. See Section 6.9.17 for more details.

6.9.3 The CODE_ALIGN Pragma

The CODE_ALIGN pragma aligns func along the specified alignment. The alignment constant must be a power of 2. The CODE_ALIGN pragma is useful if you have functions that you want to start at a certain boundary.

The syntax of the pragma in C is:

#pragma CODE_ALIGN ( func, constant )

The syntax of the pragma in C++ is:

#pragma CODE_ALIGN ( constant )

6.9.4 The CODE_SECTION Pragma

The CODE_SECTION pragma allocates space for the symbol in C, or the next symbol declared in C++, in a section named section name.

The syntax of the pragma in C is:

#pragma CODE_SECTION (symbol, "section name")

The syntax of the pragma in C++ is:

#pragma CODE_SECTION ("section name")

The CODE_SECTION pragma is useful if you have code objects that you want to link into an area separate from the .text section.

The following example demonstrates the use of the CODE_SECTION pragma.

Example 6-5 Using the CODE_SECTION Pragma C Source File

char bufferA[80]; char bufferB[80]; #pragma CODE_SECTION(funcA, "codeA") char funcA(int i); char funcB(int i); void main() { char c; c = funcA(1); c = funcB(2); } char funcA (int i) { return bufferA[i]; } char funcB (int j) { return bufferB[j]; }

Example 6-6 Generated Assembly Code From Example 6-5

.sect ".text" .global _main; **************************************************************** ;* FNAME: _main FR SIZE: 2 * ;* * ;* FUNCTION ENVIRONMENT * ;* * ;* FUNCTION PROPERTIES * ;* 0 Parameter, 1 Auto, 0 SOE * ;*************************************************************** :_main: ADDB SP,#2 MOVB AL,#1 ; |12| LCR #_funcA ; |12| ; call occurs [#_funcA] ; |12| MOV *-SP[1],AL ; |12| MOVB AL,#1 ; |13| LCR #_funcB ; |13| ; call occurs [#_funcB] ; |13| MOV *-SP[1],AL ; |13| SUBB SP,#2 LRETR ; return occurs .sect "codeA" .global _funcA ;*************************************************************** ;* FNAME: _funcA FR SIZE: 1 * ;* * ;* FUNCTION ENVIRONMENT * ;* * ;* FUNCTION PROPERTIES * ;* 0 Parameter, 1 Auto, 0 SOE * ;*************************************************************** _funcA: ADDB SP,#1 MOV *-SP[1],AL ; |17| MOVZ AR6,*-SP[1] ; |18| ADD AR6,#_bufferA ; |18| SUBB SP,#1 ; |18| MOV AL,*+XAR6[0] ; |18| LRETR ;return occurs .sect ".text" .global _funcB; **************************************************************** ;* FNAME: _funcB FR SIZE: 1 * ;* * ;* FUNCTION ENVIRONMENT * ;* * ;* FUNCTION PROPERTIES * ;* 0 Parameter, 1 Auto, 0 SOE * ;*************************************************************** _funcB: ADDB SP,#1 MOV *-SP[1],AL ; |22| MOVZ AR6,*-SP[1] ; |23| ADD AR6,#_bufferB ; |23| SUBB SP,#1 ; |23| MOV AL,*+XAR6[0] ; |23| LRETR ;return occurs

6.9.5 The DATA_ALIGN Pragma

The DATA_ALIGN pragma aligns the symbol in C, or the next symbol declared in C++, to an alignment boundary. The alignment boundary is the maximum of the symbol's default alignment value or the value of the constant in bytes. The constant must be a power of 2. The maximum alignment is 32768.

The DATA_ALIGN pragma cannot be used to reduce an object's natural alignment.

The syntax of the pragma in C is:

#pragma DATA_ALIGN (symbol, constant)

The syntax of the pragma in C++ is:

#pragma DATA_ALIGN (constant)

6.9.6 The DATA_SECTION Pragma

The DATA_SECTION pragma allocates space for the symbol in C, or the next symbol declared in C++, in a section named section name.

The syntax of the pragma in C is:

#pragma DATA_SECTION (symbol, "section name")

The syntax of the pragma in C++ is:

#pragma DATA_SECTION ("section name")

The DATA_SECTION pragma is useful if you have data objects that you want to link into an area separate from the .ebss section.

Example 6-7 through Example 6-9 demonstrate the use of the DATA_SECTION pragma.

Example 6-7 Using the DATA_SECTION Pragma C Source File

#pragma DATA_SECTION(bufferB, "my_sect") char bufferA[512]; char bufferB[512];

Example 6-8 Using the DATA_SECTION Pragma C++ Source File

char bufferA[512]; #pragma DATA_SECTION("my_sect") char bufferB[512];

Example 6-9 Using the DATA_SECTION Pragma Assembly Source File

.global _bufferA .ebss _bufferA,512,4 .global _bufferB _bufferB: .usect "my_sect",512,4

6.9.7 The Diagnostic Message Pragmas

The following pragmas can be used to control diagnostic messages in the same ways as the corresponding command line options:

Pragma Option Description
diag_suppress num -pds=num[, num2, num3...] Suppress diagnostic num
diag_remark num -pdsr=num[, num2, num3...] Treat diagnostic num as a remark
diag_warning num -pdsw=num[, num2, num3...] Treat diagnostic num as a warning
diag_error num -pdse=num[, num2, num3...] Treat diagnostic num as an error
diag_default num n/a Use default severity of the diagnostic
diag_push n/a Push the current diagnostics severity state to store it for later use.
diag_pop n/a Pop the most recent diagnostic severity state stored with #pragma diag_push to be the current setting.

The syntax of the diag_suppress, diag_remark, diag_warning, and diag_error pragmas in C is:

#pragma diag_xxx [=]num[, num2, num3...]

Notice that the names of these pragmas are in lowercase.

The diagnostic affected (num) is specified using either an error number or an error tag name. The equal sign (=) is optional. Any diagnostic can be overridden to be an error, but only diagnostic messages with a severity of discretionary error or below can have their severity reduced to a warning or below, or be suppressed. The diag_default pragma is used to return the severity of a diagnostic to the one that was in effect before any pragmas were issued (i.e., the normal severity of the message as modified by any command-line options).

The diagnostic identifier number is output with the message when you use the -pden command line option. The following example suppresses a diagnostic message and then restores the previous diagnostics severity state:

#pragma diag_push #pragma diag_suppress 551 #pragma CHECK_MISRA("-9.1") #pragma diag_pop

6.9.8 The FAST_FUNC_CALL Pragma

The FAST_FUNC_CALL pragma, when applied to a function, generates a TMS320C28x FFC instruction to call the function instead of the CALL instruction. Refer to the TMS320C28x DSP CPU and Instruction Set User’s Guide for more details on the FFC instruction.

The syntax of the pragma in C is:

#pragma FAST_FUNC_CALL (func)

The syntax of the pragma in C++ is:

#pragma FAST_FUNC_CALL (func)

The FAST_FUNC_CALL pragma should be applied only to a call to an assembly function that returns with the LB *XAR7 instruction. See Section 7.5.1 for information on combining C/C++ and assembly code.

Since this pragma should be applied only to assembly functions, if the compiler finds a definition for func in the file scope, it issues a warning and ignores the pragma.

The following example demonstrates the use of the FAST_FUNC_CALL pragma.

Example 6-10 Using the FAST_FUNC_CALL Pragma Assembly Function

_add_long: ADD ACC, *-SP[2] LB *XAR7

Example 6-11 Using the FAST_FUNC_CALL Pragma C Source File

#pragma FAST_FUNC_CALL (add_long) long add_long(long, long); void foo() { long x, y; x = 0xffff; y = 0xff; y = add_long(x, y); }

Example 6-12 Generated Assembly File

;*************************************************************** ;* FNAME: _foo FR SIZE: 6 * ;* * ;* FUNCTION ENVIRONMENT * ;* * ;* FUNCTION PROPERTIES * ;* 2 Parameter, 4 Auto, 0 SOE * ;*************************************************************** _foo: ADDB SP,#6 MOVB ACC,#255 MOVL XAR6,#65535 ; |8| MOVL *-SP[6],ACC MOVL *-SP[2],ACC ; |10| MOVL *-SP[4],XAR6 ; |8| MOVL ACC,*-SP[4] ; |10| FFC XAR7,#_add_long ; |10| ; call occurs [#_add_long] ; |10| MOVL *-SP[6],ACC ; |10| SUBB SP,#6 LRETR ; return occurs

6.9.9 The FUNC_ALWAYS_INLINE Pragma

The FUNC_ALWAYS_INLINE pragma instructs the compiler to always inline the named function. The compiler only inlines the function if it is legal to inline the function and the compiler is invoked with any level of optimization (--opt_level=0). See Section 2.11 for details about interaction between various types of inlining.

This pragma must appear before any declaration or reference to the function that you want to inline. In C, the argument func is the name of the function that will be inlined. In C++, the pragma applies to the next function declared.

The syntax of the pragma in C is:

#pragma FUNC_ALWAYS_INLINE (func)

The syntax of the pragma in C++ is:

#pragma FUNC_ALWAYS_INLINE

The following example uses this pragma:

#pragma FUNC_ALWAYS_INLINE(functionThatMustGetInlined) static inline void functionThatMustGetInlined(void) { P1OUT |= 0x01; P1OUT &= ~0x01; }

NOTE

Use Caution with the FUNC_ALWAYS_INLINE Pragma

The FUNC_ALWAYS_INLINE pragma overrides the compiler's inlining decisions. Overuse of this pragma could result in increased compilation times or memory usage, potentially enough to consume all available memory and result in compilation tool failures.

6.9.10 The FUNC_CANNOT_INLINE Pragma

The FUNC_CANNOT_INLINE pragma instructs the compiler that the named function cannot be expanded inline. Any function named with this pragma overrides any inlining you designate in any other way, such as using the inline keyword. Automatic inlining is also overridden with this pragma; see Section 2.11.

The pragma must appear before any declaration or reference to the function that you want to keep. In C, the argument func is the name of the function that cannot be inlined. In C++, the pragma applies to the next function declared.

The syntax of the pragma in C is:

#pragma FUNC_CANNOT_INLINE (func)

The syntax of the pragma in C++ is:

#pragma FUNC_CANNOT_INLINE

6.9.11 The FUNC_EXT_CALLED Pragma

When you use the --program_level_compile option, the compiler uses program-level optimization. When you use this type of optimization, the compiler removes any function that is not called, directly or indirectly, by main(). You might have C/C++ functions that are called by hand-coded assembly instead of main().

The FUNC_EXT_CALLED pragma specifies that the optimizer should keep these C functions or any functions these C/C++ functions call. These functions act as entry points into C/C++. The pragma must appear before any declaration or reference to the function to keep. In C, the argument func is the name of the function to keep. In C++, the pragma applies to the next function declared.

The syntax of the pragma in C is:

#pragma FUNC_EXT_CALLED (func)

The syntax of the pragma in C++ is:

#pragma FUNC_EXT_CALLED

Except for _c_int00, which is the name reserved for the system reset interrupt for C/C++programs, the name of the interrupt (the func argument) does not need to conform to a naming convention.

When you use program-level optimization, you may need to use the FUNC_EXT_CALLED pragma with certain options. See Section 3.3.2.

6.9.12 The FUNCTION_OPTIONS Pragma

The FUNCTION_OPTIONS pragma allows you to compile a specific function in a C or C++ file with additional command-line compiler options. The affected function will be compiled as if the specified list of options appeared on the command line after all other compiler options. In C, the pragma is applied to the function specified. In C++, the pragma is applied to the next function.

The syntax of the pragma in C is:

#pragma FUNCTION_OPTIONS ( func, "additional options" )

The syntax of the pragma in C++ is:

#pragma FUNCTION_OPTIONS( "additional options" )

6.9.13 The INTERRUPT Pragma

The INTERRUPT pragma enables you to handle interrupts directly with C code. In C, the argument func is the name of a function. In C++, the pragma applies to the next function declared.

The syntax of the pragma in C is:

#pragma INTERRUPT (func)

The syntax of the pragma in C++ is:

#pragma INTERRUPT
void func( void )

The GCC interrupt attribute syntax, which has the same effects as the INTERRUPT pragma, is as follows. Note that the interrupt attribute can precede either the function's definition or its declaration.

__attribute__((interrupt)) void func( void )

Except for _c_int00, which is the name reserved for the system reset interrupt for C programs, the name of the interrupt (the func argument) does not need to conform to a naming convention.

On the FPU, there are two kinds of interrupts - High Priority Interrupt (HPI) and Low Priority Interrupt (LPI). High priority interrupts use a fast context save and cannot be nested. Low priority interrupts behave like normal C28x interrupts and can be nested.

The kind of interrupt can be specified by way of the interrupt pragma using an optional second argument. The C syntax of the pragma is:

#pragma INTERRUPT (func, {HPI|LPI} )

The syntax of the pragma in C++ is:

#pragma INTERRUPT ( {HPI|LPI} )

The syntax of the GCC attribute, which has the same effects as the INTERRUPT pragma, is:

__attribute__((interrupt("HPI"|"LPI" ))) void func( void )
{ ... }

On FPU, if no interrupt priority is specified LPI is assumed. Interrupts specified with the interrupt keyword also default to LPI.

NOTE

Hwi Objects and the INTERRUPT Pragma

The INTERRUPT pragma must not be used when SYS/BIOS Hwi objects are used in conjunction with C functions. The Hwi_enter/Hwi_exit macros and the Hwi dispatcher contain this functionality, and the use of the C modifier can cause negative results.

6.9.14 The MUST_ITERATE Pragma

The MUST_ITERATE pragma specifies to the compiler certain properties of a loop. When you use this pragma, you are guaranteeing to the compiler that a loop executes a specific number of times or a number of times within a specified range.

Any time the UNROLL pragma is applied to a loop, MUST_ITERATE should be applied to the same loop. For loops the MUST_ITERATE pragma's third argument, multiple, is the most important and should always be specified.

Furthermore, the MUST_ITERATE pragma should be applied to any other loops as often as possible. This is because the information provided via the pragma (especially the minimum number of iterations) aids the compiler in choosing the best loops and loop transformations (that is, nested loop transformations). It also helps the compiler reduce code size.

No statements are allowed between the MUST_ITERATE pragma and the for, while, or do-while loop to which it applies. However, other pragmas, such as UNROLL, can appear between the MUST_ITERATE pragma and the loop.

6.9.14.1 The MUST_ITERATE Pragma Syntax

The syntax of the pragma for C and C++ is:

#pragma MUST_ITERATE (min, max, multiple)

The arguments min and max are programmer-guaranteed minimum and maximum trip counts. The trip count is the number of times a loop iterates. The trip count of the loop must be evenly divisible by multiple. All arguments are optional. For example, if the trip count could be 5 or greater, you can specify the argument list as follows:

#pragma MUST_ITERATE(5)

However, if the trip count could be any nonzero multiple of 5, the pragma would look like this:

#pragma MUST_ITERATE(5, , 5) /* Note the blank field for max */

It is sometimes necessary for you to provide min and multiple in order for the compiler to perform unrolling. This is especially the case when the compiler cannot easily determine how many iterations the loop will perform (that is, the loop has a complex exit condition).

When specifying a multiple via the MUST_ITERATE pragma, results of the program are undefined if the trip count is not evenly divisible by multiple. Also, results of the program are undefined if the trip count is less than the minimum or greater than the maximum specified.

If no min is specified, zero is used. If no max is specified, the largest possible number is used. If multiple MUST_ITERATE pragmas are specified for the same loop, the smallest max and largest min are used.

6.9.14.2 Using MUST_ITERATE to Expand Compiler Knowledge of Loops

Through the use of the MUST_ITERATE pragma, you can guarantee that a loop executes a certain number of times. The example below tells the compiler that the loop is guaranteed to run exactly 10 times:

#pragma MUST_ITERATE(10,10) for(i = 0; i < trip_count; i++) { ...

In this example, the compiler attempts to generate a loop even without the pragma. However, if MUST_ITERATE is not specified for a loop such as this, the compiler generates code to bypass the loop, to account for the possibility of 0 iterations. With the pragma specification, the compiler knows that the loop iterates at least once and can eliminate the loop-bypassing code.

MUST_ITERATE can specify a range for the trip count as well as a factor of the trip count. For example:

#pragma MUST_ITERATE(8, 48, 8) for(i = 0; i < trip_count; i++) { ...

This example tells the compiler that the loop executes between 8 and 48 times and that the trip_count variable is a multiple of 8 (8, 16, 24, 32, 40, 48). The multiple argument allows the compiler to unroll the loop.

You should also consider using MUST_ITERATE for loops with complicated bounds. In the following example:

for(i2 = ipos[2]; i2 < 40; i2 += 5) { ...

The compiler would have to generate a divide function call to determine, at run time, the exact number of iterations performed. The compiler will not do this. In this case, using MUST_ITERATE to specify that the loop always executes eight times allows the compiler to attempt to generate a loop:

#pragma MUST_ITERATE(8, 8) for(i2 = ipos[2]; i2 < 40; i2 += 5) { ...

6.9.15 The NO_HOOKS Pragma

The NO_HOOKS pragma prevents entry and exit hook calls from being generated for a function.

The syntax of the pragma in C is:

#pragma NO_HOOKS (func)

The syntax of the pragma in C++ is:

#pragma NO_HOOKS

See Section 2.13 for details on entry and exit hooks.

6.9.16 The RESET_MISRA Pragma

The RESET_MISRA pragma resets the specified MISRA C:2004 rules to the state they were before any CHECK_MISRA pragmas (see Section 6.9.1) were processed. For instance, if a rule was enabled on the command line but disabled in the source, the RESET_MISRA pragma resets it to enabled. This pragma accepts the same format as the --check_misra option, except for the "none" keyword.

The syntax of the pragma in C is:

#pragma RESET_MISRA (" {all|required|advisory|rulespec} ")

The rulespec parameter is a comma-separated list of rule numbers. See Section 6.3 for details.

6.9.17 The RETAIN Pragma

The RETAIN pragma can be applied to a code or data symbol. It causes a .retain directive to be generated into the section that contains the definition of the symbol. The .retain directive indicates to the linker that the section is ineligible for removal during conditional linking. Therefore, regardless whether or not the section is referenced by another section in the application that is being compiled and linked, it will be included in the output file result of the link.

The syntax of the pragma in C is:

#pragma RETAIN ( symbol )

The syntax of the pragma in C++ is:

#pragma RETAIN

The CLINK pragma has the opposite effect of the RETAIN pragma. See Section 6.9.2 for more details.

6.9.18 The SET_CODE_SECTION and SET_DATA_SECTION Pragmas

These pragmas can be used to set the section for all declarations below the pragma.

The syntax of the pragmas in C/C++ is:

#pragma SET_CODE_SECTION ("section name")
#pragma SET_DATA_SECTION ("section name")

In Example 6-13 x and y are put in the section mydata. To reset the current section to the default used by the compiler, a blank parameter should be passed to the pragma. An easy way to think of the pragma is that it is like applying the CODE_SECTION or DATA_SECTION pragma to all symbols below it.

Example 6-13 Setting Section With SET_DATA_SECTION Pragma

#pragma SET_DATA_SECTION("mydata") int x; int y; #pragma SET_DATA_SECTION()

The pragmas apply to both declarations and definitions. If applied to a declaration and not the definition, the pragma that is active at the declaration is used to set the section for that symbol. Here is an example:

Example 6-14 Setting a Section With SET_CODE_SECTION Pragma

#pragma SET_CODE_SECTION("func1") extern void func1(); #pragma SET_CODE_SECTION() ... void func1() { ... }

In Example 6-14 func1 is placed in section func1. If conflicting sections are specified at the declaration and definition, a diagnostic is issued.

The current CODE_SECTION and DATA_SECTION pragmas and GCC attributes can be used to override the SET_CODE_SECTION and SET_DATA_SECTION pragmas. For example:

Example 6-15 Overriding SET_DATA_SECTION Setting

#pragma DATA_SECTION(x, "x_data") #pragma SET_DATA_SECTION("mydata") int x; int y; #pragma SET_DATA_SECTION()

In Example 6-15 x is placed in x_data and y is placed in mydata. No diagnostic is issued for this case.

The pragmas work for both C and C++. In C++, the pragmas are ignored for templates and for implicitly created objects, such as implicit constructors and virtual function tables.

6.9.19 The UNROLL Pragma

No statements are allowed between the UNROLL pragma and the for, while, or do-while loop to which it applies. However, other pragmas, such as MUST_ITERATE, can appear between the UNROLL pragma and the loop.

The syntax of the pragma for C and C++ is:

#pragma UNROLL(n)

If possible, the compiler unrolls the loop so there are n copies of the original loop. The compiler only unrolls if it can determine that unrolling by a factor of n is safe. In order to increase the chances the loop is unrolled, the compiler needs to know certain properties:

  • The loop iterates a multiple of n times. This information can be specified to the compiler via the multiple argument in the MUST_ITERATE pragma.
  • The smallest possible number of iterations of the loop
  • The largest possible number of iterations of the loop

The compiler can sometimes obtain this information itself by analyzing the code. However, sometimes the compiler can be overly conservative in its assumptions and therefore generates more code than is necessary when unrolling. This can also lead to not unrolling at all. Furthermore, if the mechanism that determines when the loop should exit is complex, the compiler may not be able to determine these properties of the loop. In these cases, you must tell the compiler the properties of the loop by using the MUST_ITERATE pragma.

Specifying #pragma UNROLL(1) asks that the loop not be unrolled. Automatic loop unrolling also is not performed in this case.

If multiple UNROLL pragmas are specified for the same loop, it is undefined which pragma is used, if any.

6.10 The _Pragma Operator

The C28x C/C++ compiler supports the C99 preprocessor _Pragma() operator. This preprocessor operator is similar to #pragma directives. However, _Pragma can be used in preprocessing macros (#defines).

The syntax of the operator is:

_Pragma ("string_literal");

The argument string_literal is interpreted in the same way the tokens following a #pragma directive are processed. The string_literal must be enclosed in quotes. A quotation mark that is part of the string_literal must be preceded by a backward slash.

You can use the _Pragma operator to express #pragma directives in macros. For example, the DATA_SECTION syntax:

#pragma DATA_SECTION( func ," section ")

Is represented by the _Pragma() operator syntax:

_Pragma ("DATA_SECTION( func ,\" section \")")

The following code illustrates using _Pragma to specify the DATA_SECTION pragma in a macro:

... #define EMIT_PRAGMA(x) _Pragma(#x) #define COLLECT_DATA(var) EMIT_PRAGMA(DATA_SECTION(var,"mysection")) COLLECT_DATA(x) int x; ...

The EMIT_PRAGMA macro is needed to properly expand the quotes that are required to surround the section argument to the DATA_SECTION pragma.

6.11 Object File Symbol Naming Conventions (Linknames)

Each externally visible identifier is assigned a unique symbol name to be used in the object file, a so-called linkname. This name is assigned by the compiler according to an algorithm which depends on the name, type, and source language of the symbol. This algorithm may add a prefix to the identifier (typically an underscore), and it may mangle the name.

For COFF, the compiler places an underscore at the beginning of the linknames of C identifiers, so you can safely use identifiers that do not begin with an underscore in your assembly code.

Name mangling encodes the types of the parameters of a function in the linkname for a function. Name mangling only occurs for C++ functions which are not declared 'extern "C"'. Mangling allows function overloading, operator overloading, and type-safe linking. Be aware that the return value of the function is not encoded in the mangled name, as C++ functions cannot be overloaded based on the return value.

The mangling algorithm used closely follows that described in The Annotated Reference Manual (ARM).

For example, the general form of a C++ linkname for a function named func is:

_func__F parmcodes

Where parmcodes is a sequence of letters that encodes the parameter types of func.

For this simple C++ source file:

int foo(int i){ } //global C++ function

This is the resulting assembly code:

_foo__Fi

The linkname of foo is _foo__Fi, indicating that foo is a function that takes a single argument of type int. To aid inspection and debugging, a name demangling utility is provided that demangles names into those found in the original C++ source. See Section 9 for more information.

6.12 Initializing Static and Global Variables

The ANSI/ISO C standard specifies that global (extern) and static variables without explicit initializations must be initialized to 0 before the program begins running. This task is typically done when the program is loaded. Because the loading process is heavily dependent on the specific environment of the target application system, the compiler itself makes no provision for initializing to 0 otherwise uninitialized static storage class variables at run time. It is up to your application to fulfill this requirement.

NOTE

Initialize Global Objects

You should explicitly initialize all global objects which you expected the compiler would set to zero by default.

6.12.1 Initializing Static and Global Variables With the Linker

If your loader does not preinitialize variables, you can use the linker to preinitialize the variables to 0 in the object file. For example, in the linker command file, use a fill value of 0 in the .ebss section:

SECTIONS { ... .ebss: {} = 0x00; ... }

Because the linker writes a complete load image of the zeroed .ebss section into the output COFF file, this method can have the unwanted effect of significantly increasing the size of the output file (but not the program).

If you burn your application into ROM, you should explicitly initialize variables that require initialization. The preceding method initializes .ebss to 0 only at load time, not at system reset or power up. To make these variables 0 at run time, explicitly define them in your code.

For more information about linker command files and the SECTIONS directive, see the linker description information in the TMS320C28x Assembly Language Tools User's Guide.

6.12.2 Initializing Static and Global Variables With the const Type Qualifier

Static and global variables of type const without explicit initializations are similar to other static and global variables because they might not be preinitialized to 0 (for the same reasons discussed in Section 6.12). For example:

const int zero; /* might not be initialized to 0 */

However, the initialization of const global and static variables is different because these variables are declared and initialized in a section called .econst. For example:

const int zero = 0 /* guaranteed to be 0 */

This corresponds to an entry in the .econst section:

.sect .econst _zero .word 0

This feature is particularly useful for declaring a large table of constants, because neither time nor space is wasted at system startup to initialize the table. Additionally, the linker can be used to place the .econst section in ROM.

You can use the DATA_SECTION pragma to put the variable in a section other than .econst. For example, the following C code:

#pragma DATA_SECTION (var, ".mysect") const int zero=0;

is compiled into this assembly code:

.sect .mysect _zero .word 0

6.13 Changing the ANSI/ISO C/C++ Language Mode

The language mode command-line options determine how the compiler interprets your source code. You specify one option to identify which language standard your code follows. You can also specify a separate option to specify how strictly the compiler should expect your code to conform to the standard.

Specify one of the following language options to control the language standard that the compiler expects the source to follow. The options are:

  • ANSI/ISO C89 (--c89, default for C files)
  • ANSI/ISO C99 (--c99, see Section 6.13.1.)
  • Kernighan and Ritchie (K&R) C (--kr_compatible or -pk, see Section 6.13.2.) Does not apply to C++ code.
  • ISO C++03 (--c++03, default for C++ files)
  • Embedded C++ (--embedded_cpp or -pe, see Section 6.13.4.)

Use one of the following options to specify how strictly the code conforms to the standard:

  • Relaxed ANSI/ISO (--relaxed_ansi or -pr) This is the default.
  • Strict ANSI/ISO (--strict_ansi or -ps)

The default is relaxed ANSI/ISO mode. Under relaxed ANSI/ISO mode, the compiler accepts language extensions that could potentially conflict with ANSI/ISO C/C++. Under strict ANSI mode, these language extensions are suppressed so that the compiler will accept all strictly conforming programs. (See Section 6.13.3.)

6.13.1 Enabling C99 Mode (--c99)

The compiler supports the 1999 standard of C as standardized by the ISO. However, the following list of run-time functions and features are not implemented or fully supported:

  • complex.h
  • ctype.h
    • isblank()
  • fenv.h
  • float.h
    • DECIMAL_DIG
    • FLT_EVAL_METHOD
  • inttypes.h
    • wcstoimax() / wcstoumax()
  • math.h: The math library used by the compiler in C99 mode has been changed. Full C99 math support is now available, including long double (64-bit) and float versions of floating point math routines. See the list of standard math.h C99 routines.
  • stdarg.h
    • va_copy macro
  • stdio.h
    • %a and %A format specifiers for hexadecimal float
    • The %e specifier may produce "-0" when "0" is expected by the standard
    • snprintf() does not properly pad with spaces when writing to a wide character array
  • stdlib.h
    • strtof() atof() / strtod() / strtold() do not support hexadecimal float strings
    • vfscanf() / vscanf() / vsscanf() return value on floating point matching failure is incorrect
  • tgmath.h
  • time.h
    • strftime()
  • wchar.h
    • getws() / fputws()
    • mbrlen()
    • mbsrtowcs()
    • wcscat()
    • wcschr()
    • wcscmp() / wcsncmp()
    • wcscpy() / wcsncpy()
    • wcsftime()
    • wcsrtombs()
    • wcsstr()
    • wcstok()
    • wcsxfrm()
    • Wide character print / scan functions
    • Wide character conversion functions

6.13.2 Compatibility With K&R C (--kr_compatible Option)

ANSI/ISO C is a superset of the de facto C standard defined in Kernighan and Ritchie's The C Programming Language. K&R C mode does not apply to C++ code, nor does it accept the strict interpretation option. Most programs written for other non-ANSI/ISO compilers correctly compile and run without modification. However, subtle changes in the language can affect existing code. Appendix C in The C Programming Language (second edition, referred to in this manual as K&R) summarizes differences between ANSI/ISO C and the first edition's C standard (the first edition is referred to in this manual as K&R C).

To simplify the process of compiling existing C programs with the ANSI/ISO C/C++ compiler, the compiler has a K&R option (--kr_compatible) that modifies some semantic rules of the language for compatibility with older code. In general, the --kr_compatible option relaxes requirements that are stricter for ANSI/ISO C than for K&R C. The --kr_compatible option does not disable any new features of the language such as function prototypes, enumerations, initializations, or preprocessor constructs. Instead, --kr_compatible simply liberalizes the ANSI/ISO rules without revoking any of the features.

The specific differences between the ANSI/ISO version of C and the K&R version of C are as follows:

  • The integral promotion rules have changed regarding promoting an unsigned type to a wider signed type. Under K&R C, the result type was an unsigned version of the wider type; under ANSI/ISO, the result type is a signed version of the wider type. This affects operations that perform differently when applied to signed or unsigned operands; namely, comparisons, division (and mod), and right shift:
  • unsigned short u; int i; if (u < i) /* SIGNED comparison, unless --kr_compatible used */
  • ANSI/ISO prohibits combining two pointers to different types in an operation. In most K&R compilers, this situation produces only a warning. Such cases are still diagnosed when --kr_compatible is used, but with less severity:
  • int *p; char *q = p; /* error without --kr_compatible, warning with --kr_compatible */
  • External declarations with no type or storage class (only an identifier) are illegal in ANSI/ISO but legal in K&R:
  • a; /* illegal unless --kr_compatible used */
  • ANSI/ISO interprets file scope definitions that have no initializers as tentative definitions. In a single module, multiple definitions of this form are fused together into a single definition. Under K&R, each definition is treated as a separate definition, resulting in multiple definitions of the same object and usually an error. For example:
  • int a; int a; /* illegal if --kr_compatible used, OK if not */

    Under ANSI/ISO, the result of these two definitions is a single definition for the object a. For most K&R compilers, this sequence is illegal, because int a is defined twice.

  • ANSI/ISO prohibits, but K&R allows objects with external linkage to be redeclared as static:
  • extern int a; static int a; /* illegal unless --kr_compatible used */
  • Unrecognized escape sequences in string and character constants are explicitly illegal under ANSI/ISO but ignored under K&R:
  • char c = '\q'; /* same as 'q' if --kr_compatible used, error if not */
  • ANSI/ISO specifies that bit fields must be of type int or unsigned. With --kr_compatible, bit fields can be legally defined with any integral type. For example:
  • struct s { short f : 2; /* illegal unless --kr_compatible used */ };
  • K&R syntax allows a trailing comma in enumerator lists:
  • enum { a, b, c, }; /* illegal unless --kr_compatible used */
  • K&R syntax allows trailing tokens on preprocessor directives:
  • #endif NAME /* illegal unless --kr_compatible used */

6.13.3 Enabling Strict ANSI/ISO Mode and Relaxed ANSI/ISO Mode (--strict_ansi and --relaxed_ansi Options)

Under relaxed ANSI/ISO mode (the default), the compiler accepts language extensions that could potentially conflict with a strictly conforming ANSI/ISO C/C++ program. Under strict ANSI mode, these language extensions are suppressed so that the compiler will accept all strictly conforming programs.

Use the --strict_ansi option when you know your program is a conforming program and it will not compile in relaxed mode. In this mode, language extensions that conflict with ANSI/ISO C/C++ are disabled and the compiler will emit error messages where the standard requires it to do so. Violations that are considered discretionary by the standard may be emitted as warnings instead.

Examples:

The following is strictly conforming C code, but will not be accepted by the compiler in the default relaxed mode. To get the compiler to accept this code, use strict ANSI mode. The compiler will suppress the interrupt keyword language exception, and interrupt may then be used as an identifier in the code.

int main() { int interrupt = 0; return 0; }

The following is not strictly conforming code. The compiler will not accept this code in strict ANSI mode. To get the compiler to accept it, use relaxed ANSI mode. The compiler will provide the interrupt keyword extension and will accept the code

interrupt void isr(void); int main() { return 0; }

The following code is accepted in all modes. The __interrupt keyword does not conflict with the ANSI/ISO C standard, so it is always available as a language extension.

__interrupt void isr(void); int main() { return 0; }

The default mode is relaxed ANSI. This mode can be selected with the --relaxed_ansi (or -pr) option. Relaxed ANSI mode accepts the broadest range of programs. It accepts all TI language extensions, even those which conflict with ANSI/ISO, and ignores some ANSI/ISO violations for which the compiler can do something reasonable. The GCC language extensions described in Section 6.14 are available in relaxed ANSI/ISO mode.

6.13.4 Enabling Embedded C++ Mode (--embedded_cpp Option)

The compiler supports the compilation of embedded C++. In this mode, some features of C++ are removed that are of less value or too expensive to support in an embedded system. When compiling for embedded C++, the compiler generates diagnostic messages for the use of omitted features.

Embedded C++ is enabled by compiling with the --embedded_cpp option.

Embedded C++ omits these C++ features:

  • Templates
  • Exception handling
  • Run-time type information
  • The new cast syntax
  • The keyword mutable
  • Multiple inheritance
  • Virtual inheritance
  • Iostream (see below)

Under the standard definition of embedded C++, namespaces and using-declarations are not supported. The C28x compiler nevertheless allows these features under embedded C++ because the C++ run-time-support library makes use of them. Furthermore, these features impose no run-time penalty.

The TMS320C28x compiler defines the _embedded_cplusplus macro for embedded C++ compilation.

The run-time-support libraries supplied with the compiler can be used to link with a module compiled for embedded C++.

The default run-time support library does not support using iostream under embedded C++. Embedded C++ equivalents of some header files, such as iostream, are not included in the RTS source code distributed with the compiler. These features cannot be used in Embedded C++ without new header files and an appropriately-configured library

6.14 GNU Language Extensions

The GNU compiler collection (GCC) defines a number of language features not found in the ANSI/ISO C and C++ standards. The definition and examples of these extensions (for GCC version 4.7) can be found at the GNU web site, https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/C-Extensions.html#C-Extensions.

Most of these extensions are also available for C++ source code.

6.14.1 Extensions

Most of the GCC language extensions are available in the TI compiler when compiling in relaxed ANSI mode (--relaxed_ansi). Note that the --gcc option is deprecated.

The extensions that the TI compiler supports are listed in Table 6-3, which is based on the list of extensions found at the GNU web site. The shaded rows describe extensions that are not supported.

Table 6-3 GCC Language Extensions

Extensions Descriptions
Statement expressions Putting statements and declarations inside expressions (useful for creating smart 'safe' macros)
Local labels Labels local to a statement expression
Labels as values Pointers to labels and computed gotos
Nested functions As in Algol and Pascal, lexical scoping of functions
Constructing calls Dispatching a call to another function
Naming types(1) Giving a name to the type of an expression
typeof operator typeof referring to the type of an expression
Generalized lvalues Using question mark (?) and comma (,) and casts in lvalues
Conditionals Omitting the middle operand of a ?: expression
long long Double long word integers and long long int type
Hex floats Hexadecimal floating-point constants
Complex Data types for complex numbers
Zero length Zero-length arrays
Variadic macros Macros with a variable number of arguments
Variable length Arrays whose length is computed at run time
Empty structures Structures with no members
Subscripting Any array can be subscripted, even if it is not an lvalue.
Escaped newlines Slightly looser rules for escaped newlines
Multi-line strings(1) String literals with embedded newlines
Pointer arithmetic Arithmetic on void pointers and function pointers
Initializers Non-constant initializers
Compound literals Compound literals give structures, unions, or arrays as values
Designated initializers Labeling elements of initializers
Cast to union Casting to union type from any member of the union
Case ranges 'Case 1 ... 9' and such
Mixed declarations Mixing declarations and code
Function attributes Declaring that functions have no side effects, or that they can never return
Attribute syntax Formal syntax for attributes
Function prototypes Prototype declarations and old-style definitions
C++ comments C++ comments are recognized.
Dollar signs A dollar sign is allowed in identifiers.
Character escapes The character ESC is represented as \e
Variable attributes Specifying the attributes of variables
Type attributes Specifying the attributes of types
Alignment Inquiring about the alignment of a type or variable
Inline Defining inline functions (as fast as macros)
Assembly labels Specifying the assembler name to use for a C symbol
Extended asm Assembler instructions with C operands
Constraints Constraints for asm operands
Alternate keywords Header files can use __const__, __asm__, etc
Explicit reg vars Defining variables residing in specified registers
Incomplete enum types Define an enum tag without specifying its possible values
Function names Printable strings which are the name of the current function
Return address Getting the return or frame address of a function (limited support)
Other built-ins Other built-in functions (see Section 6.14.5)
Vector extensions Using vector instructions through built-in functions
Target built-ins Built-in functions specific to particular targets
Pragmas Pragmas accepted by GCC
Unnamed fields Unnamed struct/union fields within structs/unions
Thread-local Per-thread variables
Binary constants Binary constants using the '0b' prefix.
(1) Feature defined for GCC 3.0; definition and examples at https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/C-Extensions.html#C-Extensions

6.14.2 Function Attributes

The following GCC function attributes are supported: alias, always_inline, const, constructor, deprecated, format, format_arg, interrupt, malloc, noinline, noreturn, pure, ramfunc, section, unused, used, and warn_unused_result.

For example, this function declaration uses the alias attribute to make "my_alias" a function alias for the "myFunc" function:

void my_alias() __attribute__((alias("myFunc")));

See Section 6.9.13 for more about the interrupt function attribute.

The format attribute is applied to the declarations of printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf, scanf, fscanf, vfscanf, vscanf, vsscanf, and sscanf in stdio.h. Thus when GCC extensions are enabled, the data arguments of these functions are type checked against the format specifiers in the format string argument and warnings are issued when there is a mismatch. These warnings can be suppressed in the usual ways if they are not desired.

The malloc attribute is applied to the declarations of malloc, calloc, realloc and memalign in stdlib.h.

The ramfunc attribute specifies that a function will be placed in and executed from RAM. The ramfunc attribute allows the compiler to optimize functions for RAM execution, as well as to automatically copy functions to RAM on flash-based devices. For example:

__attribute__((ramfunc)) void f(void) { ... }

The --ramfunc=on option specifies that all functions compiled with this option are placed in and executed from RAM, even if this function attribute is not used.

Newer TI linker command files support the ramfunc attribute automatically by placing functions with this attribute in the .TI.ramfunc section. If you have a linker command file that does not include a section specification for the .TI.ramfunc section, you can modify the linker command file to place this section in RAM. See the Placing functions in RAM wiki page for more about the ramfunc attribute and option. See the TMS320C28x Assembly Language Tools User's Guide for details on section placement.

Fast branch instructions are generated for ramfunc functions. Regular branch instructions are generated for all other functions.

The ramfunc attribute is ignored by the CLA compiler.

6.14.3 Variable Attributes

The following variable attributes are supported: aligned, deprecated, mode, section, transparent_union, unused, and used.

The used attribute is defined in GCC 4.2 (see https://gcc.gnu.org/onlinedocs/gcc-4.2.4/gcc/Variable-Attributes.html#Variable-Attributes).

6.14.4 Type Attributes

The following type attributes are supported: aligned, deprecated, transparent_union, and unused.

6.14.5 Built-In Functions

The following built-in functions are supported: __builtin_abs, __builtin_classify_type, __builtin_constant_p, __builtin_expect, __builtin_fabs, __builtin_fabsf, __builtin_frame_address, __builtin_labs, __builtin_memcpy, and __builtin_return_address.

The __builtin_frame_address function always returns zero.

The __builtin_return_address function always returns zero.

6.15 Compiler Limits

Due to the variety of host systems supported by the C/C++ compiler and the limitations of some of these systems, the compiler may not be able to successfully compile source files that are excessively large or complex. In general, exceeding such a system limit prevents continued compilation, so the compiler aborts immediately after printing the error message. Simplify the program to avoid exceeding a system limit.

Some systems do not allow filenames longer than 500 characters. Make sure your filenames are shorter than 500.

The compiler has no arbitrary limits but is limited by the amount of memory available on the host system. On smaller host systems such as PCs, the optimizer may run out of memory. If this occurs, the optimizer terminates and the shell continues compiling the file with the code generator. This results in a file compiled with no optimization. The optimizer compiles one function at a time, so the most likely cause of this is a large or extremely complex function in your source module. To correct the problem, your options are:

  • Don't optimize the module in question.
  • Identify the function that caused the problem and break it down into smaller functions.
  • Extract the function from the module and place it in a separate module that can be compiled without optimization so that the remaining functions can be optimized.
Submit Documentation Feedback

Copyright© 2015, Texas Instruments Incorporated. An IMPORTANT NOTICE for this document addresses availability, warranty, changes, use in safety-critical applications, intellectual property matters and other important disclaimers.