0

I'm trying to find the best way to extract a list of evaluated defines from source code.
I found a few questions about how macros are evaluated at compile time, but I'm specifically looking for extracting the result for each #define to dump to a file.

Here is some sample code the question:

#define VALUE1 0x01
#define VALUE2 0x10
#define VALUE3 VALUE1 | VALUE2

int main(void) {
    printf("VALUE01: 0x%02X\n", VALUE1);
    printf("VALUE02: 0x%02X\n", VALUE2);
    printf("VALUE03: 0x%02X\n", VALUE3); //prints evaluated VALUE3
}

Technically, the sample code could achieve what I'm looking for, but is cumbersome and would require scripting to generate and maintain. Ideally, there would already be a compiler flag that I overlooked that could do this since it already has to evaluate the macros, but there wasn't one that I could find. Are there any other compiler tools that could be used to achieve this?

This answered question was the closest answer I found and provides me with the ability to dump all defines, but it dumps the un-evaluated form as seen in source code:

$ gcc -dM -E test.c | grep "VAL"
#define VALUE1 0x01
#define VALUE2 0x10
#define VALUE3 VALUE1 | VALUE2
.... (and all the other preprocessor defines)

Jens Petersen
  • 189
  • 2
  • 11
  • Just to be clear `#define VALUE3 VALUE1 | VALUE2` does not mean that `VALUE3` evaluates to `VALUE1 | VALUE2`. It means that the token `VALUE3` will be replaced by the string `VALUE1 | VALUE2` where `VALUE1` and `VALUE2` will be substituted with their respective definitions. So `VALUE3` will be `0x01 | 0x10` (and not 0x11). How it's treated in its final evaluation depends upon the context in which it is used in code. That does not include the additional complexity that rici cites in his answer. – lurker Oct 11 '21 at 15:59
  • Assuming there is no additional complexity (no runtime dependencies, redefines, etc), would there be any straightforward way of being able to evaluate to `#define VALUE3 0x11`? I ask since in my example, the compiler appears to "evaluate" the #define. I'm able to see the resulting `0x11` in the disassembly when doing an `objdump -d ` – Jens Petersen Oct 11 '21 at 18:22
  • Evaluating `VALUE3` to `0x11` would only be meaningful knowing that you limited the context of the expansion of `VALUE3` to specific cases. For example, the expression `x = VALUE3` would certainly be replaceable by `x = 0x11`. However, if you have `x = VALUE3 - 1`, this is not the same as `x = 0x10` since `+` has higher operator precedence than `|`. It's not involving any redefines or complexities. It's just context. So, it would not be very useful to assume `VALUE3` evaluates to `0x11` generally. – lurker Oct 12 '21 at 00:04
  • Generally speaking, I completely agree. But I do have a very specific use case for wanting to know exactly what `VALUE3` would evaluate to. Seems that other than maybe creating a script to extract the `#defines` and evaluate them like in the example, there wouldn't be a straightforward way of doing so. – Jens Petersen Oct 13 '21 at 15:29

1 Answers1

0

The -dM presentation is the only correct answer, since the compiler does not "evaluate the macros"; it merely replaces each macro with its replacement text, at each replacement site. So there is no definite "evaluated form" of a macro; each usage might be different.

It's important to keep in mind the fact that macro replacement doesn't evaluate anything. It really is only a text replacement. Evaluation normally happens only when the program is run, although a compiler might do the evaluation itself if the result doesn't depend on anything which might change at runtime (eg., if all the values in the expression are literal constants). In the second case, often called "constant-folding", the evaluation happens long after macro replacement is finished.

For a simple (and ugly) example, loosely based on your code:

#include <stdio.h>
#define VALUE3 VALUE1 | VALUE2
int main(void) {
  #define VALUE1 0x01
  #define VALUE2 0x10
    printf("%d, %d\n", VALUE3,
  #undef  VALUE1
  #define VALUE1 0x02
                       VALUE3);
}

will print 17, 18.

(The expansion of VALUE1 also varies during the expansion of the source code.)

rici
  • 234,347
  • 28
  • 237
  • 341
  • Let's assume the 2nd scenario where all my #defines are evaluated at compile time (no runtime dependencies, no redefines, etc). Would there be a way to utilize constant-folding to evaluate and dump the #defines to a file? – Jens Petersen Oct 11 '21 at 18:09
  • @jens: basically, no. Constant-folding happens (if it happens) well after macros have been replaced, as I said, and a replaced macro leaves no traces. So even if you could detect that the constant 6 was computed from `2*3`, there would be no way to tell that `2*3` came from a macro expansion. (Which is not a binary choice; it might be that only part of it came from a macro.) – rici Oct 11 '21 at 19:28