Получение значения выражений времени компиляции в C

Есть ли способ, чтобы компилятор C (XC16 в моем случае, который основан на gcc) дампировал результаты выражения времени компиляции?

У нас есть много #define, таких как

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

и я хотел бы знать фактические числа, которые компилятор уменьшает эти выражения до.

Обратите внимание, что это НЕ то же самое, что вы хотите знать, что выводит препроцессор; препроцессор не вычисляет математику, она только заменяет вещи. Если я посмотрю на вывод препроцессора, я получаю (эффективно)

#define FOO_FIXEDPOINT (int16_t)(37.6/0.035)

и это компилятор, а не препроцессор, который вычисляет значение.


Еще один важный момент: у меня есть свобода создавать специальный файл .c, который делает такие вещи, как

#include "foo.h"

const int16_t foo_fixedpoint = FOO_FIXEDPOINT;

чтобы я предоставлял место компилятору для выполнения его работы и ставил результаты как константу.


Как полный самодостаточный пример, если я поместил его в foo.c и запустил 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;
}

Я получаю этот вывод:

[... typedefs elided ...]
int main()
{
   int x = (int16_t)(37.6/0.035);
}

Ответы

Ответ 1

Посмотрите флаги -fdump-tree-* на странице параметры отладки, чтобы увидеть выход промежуточного языка (немного низкоуровневый, но вполне читаемый).

Вы можете использовать -fdump-tree-all для просмотра файла на разных этапах, но, вероятно, все, что вам нужно, это -fdump-tree-original. Посмотрите вниз в сгенерированном файле *.original, чтобы найти свою основную функцию:

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

{
  int x = 1074;

    int x = 1074;
}

Ответ 2

Как обсуждалось в комментариях, особенно если числовые макросы не смешиваются с макросами, имеющими нецифровые типы, просто создать программу, которая печатает свои значения.

Несмотря на то, что ваша среда является кросс-компилятором, это полезное упражнение, потому что все gccs обрабатывают константные выражения внутри одного и того же кода. Это делает математику с расширенной точностью, так что скомпилированные константы находятся в пределах одного ULP точного значения.

Итак, любой gcc должен дать довольно точное представление о том, что происходит в вашем коде.

В 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";

Теперь попробуйте, запустив math.h.

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

Это дает:

#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;
}

Компиляция и запуск:

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

Ответ 3

EDIT Это не работает: - (

Даже с трюком для преобразования целого в строку, скомпилированное значение не оценивается препроцессором.

#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))

вывод компилятора (VS2008):

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

Поэтому препроцессор здесь не поможет.

Оригинальный ОТВЕТ В крайнем случае, если невозможно получить значения из промежуточных файлов, с помощью gcc-опций и т.д.

Я бы grep исходные файлы для #define, перенаправить вывод в новый .c файл и заменить #define на #pragma message. Вызов gcc в этом файле будет содержать список всех определений. При условии, что ваш компилятор поддерживает #pragma message.