Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

Pragmas You Can Understand

From Texas Instruments Wiki
Jump to: navigation, search

Introduction

This article talks about how to wrap a pragma in a macro for better readability, portability, and maintenance. It is in the form of an extended example. The entire example is shown first. Then it is commented upon a few lines at a time.

Code that uses _Pragma can be built if you use, at a minimum, compilers of the following versions.

Target Version
C6000 v6.0.1
ARM v4.4.0
C5500 v3.3.0
MSP430 v1.0.0
C2000 v5.0.0

The example itself is only tested with the C6000 compiler. It uses pragmas that are only supported by that compiler. The general techniques shown can be applied to any pragma.

Full Example

/* pragma.c */
#include <assert.h>
 
#ifdef __TI_COMPILER_VERSION__
   #define PRAGMA(x) _Pragma(#x)
   #define LOOP_COUNT_INFO(min, multiple) \
      PRAGMA(MUST_ITERATE(min,,multiple))
   #define VAR_IN_SECTION(var, section) \
      PRAGMA(DATA_SECTION(var, #section))
   #define PTR_IS_ALIGNED(ptr, pow2) \
      _nassert(((unsigned int) (ptr) & (pow2)-1) == 0);
#else
   #define LOOP_COUNT_INFO(min, multiple)
   #define VAR_IN_SECTION(var, section)
   #define PTR_IS_ALIGNED(ptr, pow2)
#endif
 
VAR_IN_SECTION(array, special)
int array[100];
 
int sum(int *p, int length)
{
   int i;
   int sum = 0;
 
   PTR_IS_ALIGNED(p, 8)
   LOOP_COUNT_INFO(8, 4)
   for (i = 0; i < length; i++)
      sum += p[i];
 
   return sum;
}

Discussion

The system header file assert.h

#include <assert.h>

is included because it defines the _nassert macro referred to by PTR_IS_ALIGNED.

The test against __TI_COMPILER_VERSION__

#ifdef __TI_COMPILER_VERSION__

is how the macros are defined to proper effect when used under a TI compiler, or defined to do nothing when used under some other compiler. The identifier __TI_COMPILER_VERSION__ is predefined by TI compilers.

These lines

   #define PRAGMA(x) _Pragma(#x)
   #define LOOP_COUNT_INFO(min, multiple) \
      PRAGMA(MUST_ITERATE(min,,multiple))

define the LOOP_COUNT_INFO macro, which gives the compiler extra information about how many times a loop may iterate. It wraps usage of the MUST_ITERATE pragma.

Pragmas, as first introduced in the C language, are preprocessing directives which perform some compiler specific action. The general form of pragmas is defined by the C language

#pragma token-sequence

Any further details are left to each compiler to define. The TI compilers define several pragmas, two of which are used in the example. Consult the Compiler User’s Guide for your target for more information on what specific pragmas are available, and how they work.

One problem with supplying a pragma in the form a preprocessing directive is that it cannot be generated by use of another preprocessing macro. This deficiency gives rise to the special preprocessing operator _Pragma. The operand of _Pragma is a string literal. This

_Pragma(“sequence of tokens”)

is the same as

#pragma sequence of tokens

Preprocessing macros can generate the _Pragma operator.

A first attempt at writing a macro to generate a _Pragma might be as follows

   #define LOOP_COUNT_INFO(min, multiple) \
      _Pragma("MUST_ITERATE(min,,multiple)")

That doesn’t work. The min and multiple arguments of the LOOP_COUNT_INFO macro are not replaced inside the quotes of the _Pragma operator. You can attempt to use preprocessing to see this. The option –ppc instructs the compiler to process all the preprocessing directives (the lines that begin with #) and place the result in a file with the extension .pp.

C:\some\dir>cl6x -ppc pragma.c

C:\some\dir>more pragma.pp
…
   _nassert(((unsigned int) (p) & (8)-1) == 0);

   for (i = 0; i < length; i++)
...

The LOOP_COUNT_INFO macro is invoked on the line just before the for loop. It is preprocessed into nothing that can be seen here. Such is the case with preprocessing directives, including the _Pragma operator. You can work around this by temporarily changing _Pragma, wherever it appears, to something else like test_Pragma

   #define LOOP_COUNT_INFO(min, multiple) \
      test_Pragma("MUST_ITERATE(min,,multiple)")
…
C:\some\dir>more pragma.pp
…
   _nassert(((unsigned int) (p) & (8)-1) == 0);
   test_Pragma("MUST_ITERATE(min,,multiple)")
   for (i = 0; i < length; i++)
...

Now you can see that, even though the LOOP_COUNT_INFO macro is invoked with min=8 and multiple=4, those arguments are not replaced inside the quotes of the argument to _Pragma.

This difficulty is overcome by using a helper macro named PRAGMA:

   #define PRAGMA(x) _Pragma(#x)

This macro uses the ‘#’ operator to make a string of its argument, and applies the _Pragma operator to it. When it is invoked, its argument x is evaluated before it is changed into a string. Therefore, in this example

   #define LOOP_COUNT_INFO(min, multiple) \
      PRAGMA(MUST_ITERATE(min,,multiple))

The arguments min and multiple of the LOOP_COUNT_INFO macro are replaced prior to invocation of the PRAGMA macro. This can be verified using the preprocessing steps described earlier

   test_Pragma("MUST_ITERATE(8,,4)")

Don’t forget to change test_Pragma back to _Pragma.

The MUST_ITERATE pragma takes 3 arguments, in this order:

  • min – indicates the loop will iterate at least this many times
  • max – indicates the loop will iterate a maximum of this many times
  • multiple – indicates the loop will always iterate a multiple of this many times

Any of the arguments may be omitted. The maximum is the most often omitted argument. That is why LOOP_COUNT_INFO does not account for it, but writes the MUST_ITERATE arguments as min,,multiple.

The lines

   #define VAR_IN_SECTION(var, section) \
      PRAGMA(DATA_SECTION(var, #section))

wrap usage of the DATA_SECTION pragma. This pragma instructs the compiler to place the global data object in the given section instead of the default section .bss. This allows, among other things, more precise placement of the data when linking. Note the use of the ‘#’ operator on the section name argument. This is because the DATA_SECTION pragma itself, whether invoked via #pragma or _Pragma, requires that the section name argument be a quoted string literal.

The lines

   #define PTR_IS_ALIGNED(ptr, pow2) \
      _nassert(((unsigned int) (ptr) & (pow2)-1) == 0);

are similar to a pragma in effect, yet do not use a pragma. The intrinsic _nassert is related to the assert macro defined in assert.h. While the assert macro results in code that checks its expression at runtime and issues a message if it is false, the _nassert intrinsic results in no code. The expression to _nassert is presumed by the compiler to always be true. This extra knowledge of the source often leads to improved optimization. In this case, the PTR_IS_ALIGNED macro indicates the pointer expression is always aligned on an address at the given power of 2, which can lead to the use of more efficient data access instructions that require such alignment.

The lines

#else
   #define LOOP_COUNT_INFO(min, multiple)
   #define VAR_IN_SECTION(var, section)
   #define PTR_IS_ALIGNED(ptr, pow2)
#endif

define those macros to do nothing when a non-TI compiler is being used.

The rest of the example shows straightforward use of the macros.

Please make careful note of the use, or non-use, of semicolons throughout the example. A semicolon must not appear between a _Pragma and the statement it affects. That is why the LOOP_COUNT_INFO and VAR_IN_SECTION macros do not end in semicolons when they are defined or invoked. A semicolon must appear at the end of an _nassert statement. Thus a semicolon must be part of either the definition or the invocation of the PTR_IS_ALIGNED macro. The semicolon is written in the definition so that all the macros are consistently invoked without a semicolon.

For more details on _Pragma, perform an Internet search on the term “Pragma operator”.