18

Is there any way to have the C compiler (XC16 in my case, which is based on gcc) dump the results of compile-time expressions?

We have lots of #defines like

#define FOO 37.6
#define FOO_BASE 0.035
#define FOO_FIXEDPOINT (int16_t)(FOO/FOO_BASE)

and I would like to know the actual numbers that the compiler reduces these expressions to.

Note that this is NOT the same thing as wanting to know what the preprocessor outputs; the preprocessor does not compute math, it only substitutes in things. If I look at the preprocessor output, I get (effectively)

#define FOO_FIXEDPOINT (int16_t)(37.6/0.035)

and it's the compiler, not the preprocessor, which figures out a value.


Another important point here: I do have the freedom to create a special .c file that does things like

#include "foo.h"

const int16_t foo_fixedpoint = FOO_FIXEDPOINT;

so that I'm providing a place for the compiler to do its work and put the results as a constant.


Just as a complete self-contained example, if I put this into foo.c and run xc16-gcc.exe -E foo.c:

#include <stdint.h>

#define FOO 37.6
#define FOO_BASE 0.035
#define FOO_FIXEDPOINT (int16_t)(FOO/FOO_BASE)

int main()
{
   int x = FOO_FIXEDPOINT;
}

I get this output:

[... typedefs elided ...]
int main()
{
   int x = (int16_t)(37.6/0.035);
}
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • 7
    please don't RTFM me. In order to do that, I need to know what to search for. – Jason S Jun 30 '15 at 22:14
  • @GrzegorzSzpetkowski -- nope, no gdb (that I know of) for the dsPIC. Also there are dozens of these definitions scattered around the code so I would like to dump all of them automatically. – Jason S Jun 30 '15 at 22:16
  • 4
    @iharob: The preprocessor doesn't compute numbers, it merely performs substitution. So in this example I would get #define FOO_FIXEDPOINT (int16_t)(37.6/0.035) which doesn't help me. I want the *result* of this expression. – Jason S Jun 30 '15 at 22:16
  • 11
    NO IT IS NOT a duplicate. I don't want to dump the #defines, I want to know what the compiler reduces compile-time expressions to. – Jason S Jun 30 '15 at 22:18
  • Using what you find in those answers should do. – Iharob Al Asimi Jun 30 '15 at 22:18
  • 11
    The question is asking about whether there is a compiler switch of some sort that will dump values that reduce to compile-time constants such as `sizeof(20)` or `40/3.75`. The preprocessor does not perform this. For example, `#define FOO_FIXEDPOINT (int16_t)(FOO/FOO_BASE)` will replace all instances of `FOO_FIXEDPOINT` with the text `(int16_t)(37.6/0.035)`, which will be reduced to a compile-time constant by the compiler. –  Jun 30 '15 at 22:22
  • 3
    thanks Chrono. This is what I said 6 minutes ago, 4 minutes after I asked the question, and 2 minutes before the question was unceremoniously closed. – Jason S Jun 30 '15 at 22:25
  • 2
    @ChronoKitsune You are right, it's a different question, I just reopened. – Iharob Al Asimi Jun 30 '15 at 22:25
  • 9
    thank you. And please in the future give people a little more benefit of the doubt before closing. – Jason S Jun 30 '15 at 22:25
  • 2
    I don't think you can get these values. They get calculated in one of the compiler passes - most likely in the Constant Reduction pass. If you use something like `i = FOO_FIXEDPOINT/2;`, the 'actual' value of `FOO_FIXEDPOINT` may appear nowhere in your executable. – Jongware Jun 30 '15 at 22:35
  • Well, one thing I can do is create a fake .c file with things like `int16_t foo_fixedpoint = FOO_FIXEDPOINT;` and run the compiler on it. I know the compiler engineer for XC16 in this case so I can get him to tell me which compiler pass to dump out, if that would help... – Jason S Jun 30 '15 at 22:35
  • 2
    The best option I'm aware of is looking at the generated assembly code. – 5gon12eder Jun 30 '15 at 22:36
  • 1
    This is what a debugger is used for: the best if you don't want to modify our program and look at the assembly is to use a debugger. – ouah Jun 30 '15 at 22:49
  • 1
    I don't like to dup it as compilers always get some new useful options but the question is very similar to this: http://stackoverflow.com/questions/7574245/gcc-dump-resolved-defines – ouah Jun 30 '15 at 22:54
  • @ouah: I'm ok with marking it as a duplicate... but that question didn't get much attention, and I suspect there *is* a way (answers to question #7574245 say "you can't") if I'm allowed to write a special .c file which acts as a "compilation harness" to facilitate printing out the resolved values... even if it involves ugly parsing of assembly code or gcc internal pass dumps. – Jason S Jun 30 '15 at 22:57
  • Programming C and C++ is like hitting yourself in the face with a hammer... Why would asking a question about it be any different? 'Mglad I don't C anything except # :) –  Nov 24 '15 at 15:58
  • Since it's the compiler that determines the computed values, is there a way to get the compiler to just dump certain computed values to a text file? Then I could include it in the git commit and everything. – endolith Jun 09 '22 at 17:03
  • Duplicate of https://stackoverflow.com/q/18808108/125507 though this version has more content – endolith Jun 09 '22 at 18:28
  • I solved this by writing a separate .c file meant for the host machine, importing the same .h files, printing the values of the macros (to a .txt file) and then compiling it (using gcc for host) and running it as a post-build step. Then there's a nice summary of the important values that I can commit along with the changes, compare between projects, etc. – endolith Jun 09 '22 at 18:28

3 Answers3

17

Check out the flags -fdump-tree-* in the debugging options page, to see the output of the intermediate language (a bit low-level but quite readable).

You can use -fdump-tree-all to see the file at different stages, but probably all you need is the -fdump-tree-original. Look down bellow in the generated *.original file to look for your main function:

...
;; Function main (null)
;; enabled by -tree-original

{
  int x = 1074;

    int x = 1074;
}
  • 1
    Hmm. one more question. Why does this work this way? if it's the "original" AST, how come the constant-folding happens before this point? – Jason S Jul 01 '15 at 00:26
  • 1
    @JasonS I don't know way, but basic expressions (e.g. 3+4) seems to be evaluated much earlier before tree optimizations stage. –  Jul 01 '15 at 02:14
2

As discussed in comments, especially if the numerical macros are not mixed with macros having non-numerical types, it's simple to produce a program that prints their values.

Even though your environment is a cross compiler, this is a useful exercise because all gccs process constant expressions internally with the same code. This does math in extended precision so that compiled constants are within one ULP of the exact value.

So any gcc should give a quite accurate idea of what's going into your code.

In perl:

print "#include<stdio.h>\n";
print "#include \"$ARGV[0]\"\n";
print "#define S(X) #X\n";
print "int main(void) {\n";
open F, $ARGV[0] or die $!;
while (<F>) {
  print "  printf(\"%s=%.13g\\n\", S($1), (double)$1);\n" if /#define\s+(\w+)\s+\S/;
}
print "  return 0;\n}\n";

Now try it out by running on math.h.

perl /usr/include/math.h > math_h_syms.c

This produces:

#include<stdio.h>
#include "/usr/include/math.h"
#define S(X) #X
int main(void) {
  printf("%s=%.13g\n", S(INFINITY), (double)INFINITY);
  printf("%s=%.13g\n", S(FP_NAN), (double)FP_NAN);
  printf("%s=%.13g\n", S(FP_INFINITE), (double)FP_INFINITE);
  printf("%s=%.13g\n", S(FP_ZERO), (double)FP_ZERO);
  printf("%s=%.13g\n", S(FP_NORMAL), (double)FP_NORMAL);
  printf("%s=%.13g\n", S(FP_SUBNORMAL), (double)FP_SUBNORMAL);
  printf("%s=%.13g\n", S(FP_SUPERNORMAL), (double)FP_SUPERNORMAL);
  printf("%s=%.13g\n", S(FP_ILOGB0), (double)FP_ILOGB0);
  printf("%s=%.13g\n", S(FP_ILOGBNAN), (double)FP_ILOGBNAN);
  printf("%s=%.13g\n", S(MATH_ERRNO), (double)MATH_ERRNO);
  printf("%s=%.13g\n", S(MATH_ERREXCEPT), (double)MATH_ERREXCEPT);
  printf("%s=%.13g\n", S(math_errhandling), (double)math_errhandling);
  printf("%s=%.13g\n", S(M_E), (double)M_E);
  printf("%s=%.13g\n", S(M_LOG2E), (double)M_LOG2E);
  printf("%s=%.13g\n", S(M_LOG10E), (double)M_LOG10E);
  printf("%s=%.13g\n", S(M_LN2), (double)M_LN2);
  printf("%s=%.13g\n", S(M_LN10), (double)M_LN10);
  printf("%s=%.13g\n", S(M_PI), (double)M_PI);
  printf("%s=%.13g\n", S(M_PI_2), (double)M_PI_2);
  printf("%s=%.13g\n", S(M_PI_4), (double)M_PI_4);
  printf("%s=%.13g\n", S(M_1_PI), (double)M_1_PI);
  printf("%s=%.13g\n", S(M_2_PI), (double)M_2_PI);
  printf("%s=%.13g\n", S(M_2_SQRTPI), (double)M_2_SQRTPI);
  printf("%s=%.13g\n", S(M_SQRT2), (double)M_SQRT2);
  printf("%s=%.13g\n", S(M_SQRT1_2), (double)M_SQRT1_2);
  printf("%s=%.13g\n", S(MAXFLOAT), (double)MAXFLOAT);
  printf("%s=%.13g\n", S(FP_SNAN), (double)FP_SNAN);
  printf("%s=%.13g\n", S(FP_QNAN), (double)FP_QNAN);
  printf("%s=%.13g\n", S(HUGE), (double)HUGE);
  printf("%s=%.13g\n", S(X_TLOSS), (double)X_TLOSS);
  printf("%s=%.13g\n", S(DOMAIN), (double)DOMAIN);
  printf("%s=%.13g\n", S(SING), (double)SING);
  printf("%s=%.13g\n", S(OVERFLOW), (double)OVERFLOW);
  printf("%s=%.13g\n", S(UNDERFLOW), (double)UNDERFLOW);
  printf("%s=%.13g\n", S(TLOSS), (double)TLOSS);
  printf("%s=%.13g\n", S(PLOSS), (double)PLOSS);
  return 0;
}

Compiling and running:

INFINITY=inf
FP_NAN=1
FP_INFINITE=2
FP_ZERO=3
FP_NORMAL=4
FP_SUBNORMAL=5
FP_SUPERNORMAL=6
FP_ILOGB0=-2147483648
FP_ILOGBNAN=-2147483648
MATH_ERRNO=1
MATH_ERREXCEPT=2
math_errhandling=2
M_E=2.718281828459
M_LOG2E=1.442695040889
M_LOG10E=0.4342944819033
M_LN2=0.6931471805599
M_LN10=2.302585092994
M_PI=3.14159265359
M_PI_2=1.570796326795
M_PI_4=0.7853981633974
M_1_PI=0.3183098861838
M_2_PI=0.6366197723676
M_2_SQRTPI=1.128379167096
M_SQRT2=1.414213562373
M_SQRT1_2=0.7071067811865
MAXFLOAT=3.402823466385e+38
FP_SNAN=1
FP_QNAN=1
HUGE=3.402823466385e+38
X_TLOSS=1.414847550406e+16
DOMAIN=1
SING=2
OVERFLOW=3
UNDERFLOW=4
TLOSS=5
PLOSS=6
Gene
  • 46,253
  • 4
  • 58
  • 96
  • @JasonS: well no. `math.h` defines these as "Return values for fpclassify" (although my local mingw values are different). – Jongware Jul 01 '15 at 08:40
1

EDIT This doesn't work :-(

Even with the trick for converting integer to a string, the composed value is not evaluated by preprocessor.

#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)

#define A 1
#define B 2
#define C A+B

#pragma message("A=" STR(A))
#pragma message("B=" STR(B))
#pragma message("C=" STR(C))

compiler output (VS2008):

1>A=1
1>B=2
1>C=1+2

So preprocessor won't help here.

Original ANSWER As the last resort, if there is no way getting the values from the intermediate files, by means of gcc-options, etc.

I'd grep the source-files for #define, redirect the output in a new .c file and replace #define by #pragma message. Calling gcc on this file would list all defines. Of cause provided your compiler supported #pragma message.

Valentin H
  • 7,240
  • 12
  • 61
  • 111
  • hmm. this allows the compiler to perform math calculations and output the results as a message?! – Jason S Jun 30 '15 at 22:44
  • Unfortunately pragma message only works on strings, and the OP is asking for computed numerical values. He'd actually have to (for floating point constants) compile printf("%f\n", MACRO); – Gene Jun 30 '15 at 22:58
  • 1
    ah... yeah, and using printfs is an ugly thing because this is a cross-compiler for an embedded system. – Jason S Jun 30 '15 at 23:02
  • 1
    Oh, how embarrassing, that's right. pragma message won't help on numerical constants, only strings will be printed. Ok than what remains is to create a list of printfs. Of cause, it's not that easy, and will print something only at run-time. – Valentin H Jun 30 '15 at 23:05