GCC 预处理器 PLUS 静态分析?

GCC preprocessor PLUS static analysis?

我知道如何 run the gcc preprocessor。 Gcc 显然执行代码的静态 analysis/optimization,因为如果你例如添加两个常量,或移动一个常量,无论您保留 "a = constant << 3" 还是手动执行该操作并在代码中包含 "a = shifted_constant",生成的汇编代码都是相同的。

给定来自 SHA256 代码的切片,

  A=0x6a09e667;
  B=0xbb67ae85;
  C=0x3c6ef372;
  D=0xa54ff53a;
  E=0x510e527f;
  F=0x9b05688c;
  G=0x1f83d9ab;
  H=0x5be0cd19;

  P(A, B, C, D, E, F, G, H, W[ 0], 0x428a2f98);

在 运行 gcc -E 我得到

 { tmp = ((((E) >> (6)) | ((E) << (32-(6)))) ^ (((E) >> (11))
       | ((E) << (32-(11)))) ^ (((E) >> (25)) | ((E) << (32-(25)))))
       + (G ^ (E & (F ^ G))) + 0x428a2f98 + W[ 0]; D += (tmp + H);
   H  += tmp + ((((A) >> (2)) | ((A) << (32-(2)))) ^ (((A) >> (13))
       | ((A) << (32-(13)))) ^ (((A) >> (22)) | ((A) << (32-(22)))))
       + ((A & B) | (C & (A | B))); };

对于P线,是宏的嵌套宏

很好,但我想要一个更简洁的结果,其中常量已被替换,常量算术已被执行等。 所以如果我得到的不是上面的这样的东西:

{ tmp = hex_constant1 + W[0]; D += (tmp + 0x5be0cd19); H += tmp + hex_constant2; };

我会更喜欢它。我没有耐心手动计算表达式,但基本上它们应该折叠成两个十六进制常量。

是否有任何工具/命令行选项可以执行此操作?

GCC 不优化预处理的 C 代码。预处理后,此代码转换为应用了大多数优化的高级 IR (GIMPLE),然后再降低为 RTL 等低级 IR。

您想要的是优化 GIMPLE 转储以将这些查找为常量。您应该像这样使用优化的树转储:

gcc -O2 -fdump-tree-optimized test.c -S

然后查找类似 test.c.211t.optimized 的文件(编号 211 可能有所不同,具体取决于 gcc 版本)。 Gimple 代码与原始 C 非常相似,因此您可以毫无问题地从中读取常量。

大多数编译器 运行 预处理器获取程序源的文本字符串,然后将其解析为编译器内部数据结构。一旦程序被解析,你就看不到编译器对代码做了什么(嗯,也许编译器有某种调试转储,但你不能指望它)。

要执行您想要的操作,您需要执行 partial evaluation 展开宏所在位置的代码。没有标准的编译器可以帮助您做到这一点。

您需要一个program transformation system (PTS),这是一个解析源代码、应用代码转换然后重新生成修改后的源文本的工具。一个好的PTS会让你编写一套源到源的转换规则来应用,每条规则的形式是:

 when you see *this*, then replace it by *that* if *condition* is true

要进行简单的常量折叠,您需要如下规则:

  rule  simplify_times_zero(e: power): product -> product
       " \e * 0 " ->  " \e ";

它处理将某个值乘以零的特殊(代数)情况。 您显然需要一堆这些规则,更多内容如下。

您的 PTS 还必须能够读取您正在使用的 C 或 C++ 的特定方言,并且能够将任何标识符解析为它的定义(否则无法知道 E 在它使用的地方是常量)。

我知道的唯一 PTS 可以 parse many C or C++ dialects, and carry out macro expansions, is our DMS Software Reengineering Toolkit。它接受使用上述示例语法的规则。

您需要的附加规则是实际进行算术运算的规则:

rule fold_addition(c1: INT32CONSTANT, c2: INT32CONSTANT): product -> product =
     " \c1 + \c2 " ->  int32_multiply(c1,c2);

其中 int32_multiply 是一个进行数学运算的函数。每个运算符和操作数类型都需要其中之一,以遵守 C 和 C++ 语言的规则。

您还需要替换已知值的规则:

rule substitute_defined_constant(i: IDENTIFIER): primitive -> primitive
  " \i " ->  macro_definition_value(i) if  is_defined_constant(i);

其中is_defined_constant在DMS前端为宏定义建立的符号table中查找标识符,检查i是否为"define i "形式的宏我在哪里被引用。通过将其写为条件,除非宏定义值实际存在,否则不会调用 macro_definition_value 函数。原始符号 table 支持由 DMS 的 C 和 C++ 前端提供。

有了这组规则,以及将这些规则应用到感兴趣的宏扩展点的规则策略,DMS 应该能够"fold" 将方程式转化为您表达的形式。现在,所有转换都是在 DMS 的程序内部表示上完成的,但 DMS 可以漂亮地打印结果,因此您可以实际看到它。

作为开发中的一般功能 UI,这可能非常有用。实际设置所有这些并使其在实践中发挥作用可能需要几天时间; DMS 是一个复杂的系统,C 和 C++ 无济于事。

如果您只打算执行此操作一次或两次,那么您最好还是硬着头皮手动执行(或者如另一个答案所建议的那样,使用调试来检查编译器的输出,如果工作)。如果它是一项日常任务,PTS 解决方案可能是一个实时(和准确性)节省者。