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 解决方案可能是一个实时(和准确性)节省者。
我知道如何 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 解决方案可能是一个实时(和准确性)节省者。