Introduction
This article shows 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.
This example uses pragmas that are specific to the C6000 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
/* WRONG!!! */
#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. This can be seen with the preprocessing listing feature of the compiler.
The compiler option --gen_preprocessor_listing creates a preprocessor listing file. It has the same name as the source file, with the file extension changed to .rl.
C:\some\dir>cl6x --gen_preprocessor_listing pragma.c
Inspect the file pragma.rl. These lines appear near the end.
N LOOP_COUNT_INFO(8, 4)
X _Pragma("MUST_ITERATE(min,,multiple)")
Every line in the preprocessor listing file begins with a capital letter which identifies it. N means a normal line of source. X is that same line after preprocessing.
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 by using the preprocessor listing file like before.
N LOOP_COUNT_INFO(8, 4)
X _Pragma("MUST_ITERATE(8,,4)")
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.