NOTICE: The Processors Wiki will End-of-Life in December of 2020. It is recommended to download any files or other content you may need that are hosted on processors.wiki.ti.com. The site is now set to read only.
Automatically Remove Unused Constant Variables
This article describes a C macro which automatically removes the constant variables that are not used. If that is not interesting enough, consider that this macro makes use of several unusual techniques you might find useful elsewhere, including:
- how to stringify macro arguments
- relying on the compiler to concatenate (smoosh together) adjacent quoted strings
- multiple line macros
- multiple line asm() statements
- conditional linking
- pragmas, and how to generate them from macros
Here is a helper macro, followed by the main macro ...
- define PRAGMA(x) _Pragma(#x)
- define REMOVE_UNUSED_CONSTANT(var) \
PRAGMA(DATA_SECTION(var, ".const:" #var)) \ asm(" .sect \".const:" #var "\"\n" \ " .clink")
Here is how to use it ...
<syntaxhighlight lang="c"> const int one_const = 1; const int two_const = 2;
REMOVE_UNUSED_CONSTANT(one_const); // removed if not used REMOVE_UNUSED_CONSTANT(two_const); // removed if not used </syntaxhighlight>
Limitations and Caveats
- Does not work for C++. Such a macro can probably be written, though it would not be quite as simple.
- Invoke the macro only at the end of the source file. While it is likely to be OK when invoked elsewhere, this avoids possible problems described below in #The asm() Part.
How Does It Do That?
The pragma part of the macro places the constant variable in a section named ".const:variable name". The asm() part of the macro marks that section with the directive ".clink". That stands for conditional linking. Sections so marked are removed by the linker if it finds that the section is never used, i.e. there are no references to that variable.
Multiple Line Macros
Note how all the lines in the macro end with "\", save the last one. It is an old rule of C that #define macros must all be on one source line. The "\" syntax tells the compiler to pretend the next line was written on the current line. As you can see, you can do this repeatedly, which allows you to format a complex macro for readability.
The Pragma Part
The pragma syntax is mostly explained in Pragmas You Can Understand, particularly the VAR_IN_SECTION macro. There are two additional twists here: subsections and concatenation of string constants.
First, subsections. The variable is placed in a section all by itself. The subsection ".const:variable name" does this. By being a subsection of .const, one of the usual sections generated by the compiler, it will be combined into the rest of .const should the linker not remove it. Even so, marking this subsection with .clink means the linker can remove it without affecting any other part of .const.
Form the Section Name with Smooshing
What does the syntax ".const:" #var do? Note there is nothing but space between the " and #. Well, from Pragmas You Can Understand, you already know that #var means that macro argument will be replaced and converted to a quoted string, also known as stringifying the macro argument. So, that means the second argument to the DATA_SECTION pragma will be ".const:" "variable" . When the C compiler sees two constant strings like that, with nothing but space between them, it concatenates them. That is, it smooshes them together as if you had written ".const:variable". And, of course, that is exactly the name of the section needed.
The asm() Part
An asm() statement is a way to inject a bit of assembly code into C compiler output. Using asm() statements is largely frowned upon, and rightly so. There are many ways to get into trouble. That said, there is little to worry about here. An asm() statement, used at file scope, which only emits assembler directives, is fairly safe. One warning: To be absolutely sure no problems arise, use REMOVE_UNUSED_CONSTANT only at the end of a source file. That insures the .sect directive (which changes the current section name) will not interfere with any section name emitted by the compiler.
An asm() statements accepts one argument: a quoted string.
This particular asm() statement results in two lines of assembly:
.sect ".const:variable" .clink
The space at the beginning of each line is important. Otherwise, the assembler interprets them as labels.
Form the First asm() Line
Here is the first half of the asm part of the macro again, followed by an explanation that moves from left to right.
asm(" .sect \".const:" #var "\"\n" \
The beginning is pretty obvious. The \" means actually put a quote inside the string, and do not end the argument to asm(). The :" ends the .const part of the subsection name, followed by the variable part of the subsection name. Those are smooshed together just as in the pragma. The "\"\n" says to smoosh on two more characters: a " and a newline.
Form the Second asm() Line
Here is the second half of the asm part of the macro, followed by more explanation.
Finally, something normal! It is just another quoted string, the .clink directive explained earlier. Due to the line continuation "\" on the previous line, the compiler acts as if this line were written on the previous line, which means this string is smooshed on to the end of the previous one. Remember the previous string ends with a newline. So, in the compiler output, this forms two lines. As promised, a multiple line asm() statement! Note there is no concluding semi-colon. The user of the macro supplies that semi-colon.