【问题标题】:GCC preprocessor PLUS static analysis?GCC预处理器加上静态分析?
【发布时间】:2016-11-16 14:06:11
【问题描述】:

我知道如何run the gcc preprocessor。 Gcc 显然对代码进行了静态分析/优化,因为如果你例如添加两个常量或移位一个常量,无论您保留“a = 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; };

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

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

【问题讨论】:

  • 你确定你没有混合预处理器和编译器吗?预处理器只替换你的defines(和includes 和其他宏)它不执行任何类型的优化。那是编译器的工作。

标签: c gcc c-preprocessor static-analysis


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

    要做你想做的,你需要在展开宏的位置做代码的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 在其使用的地方是常量)。

    我所知道的唯一可以parse many C or C++ dialects 并进行宏扩展的PTS 是我们的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 前端为宏定义构建的符号表中查找标识符,并在引用 i 的位置检查 i 是否为“define i”形式的宏。通过将其编写为条件,除非宏定义值实际存在,否则不会调用 macro_definition_value 函数。原始符号表支持由 DMS 的 C 和 C++ 前端提供。

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

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

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

    【讨论】:

      猜你喜欢
      • 2021-07-30
      • 1970-01-01
      • 2013-09-04
      • 2014-09-13
      • 2011-04-24
      • 1970-01-01
      • 2021-07-20
      • 2014-06-14
      • 2021-11-27
      相关资源
      最近更新 更多