gcc对代码进行了多少次遍历?

25
  • 对于C和C++,默认情况下使用多少遍扫描?
  • 这个数字是否会根据使用的优化级别而改变?(应该会)
  • 它可以直接更改吗?

我在http://gcc.gnu.org/中搜索了这些信息,但使用site:http://gcc.gnu.org/进行谷歌搜索没有产生任何结果。

任何关于此的文档指针也将是有帮助的。


我所指的“By pass”是仅跳过源代码的原始表示,而不是Wikipedia所建议的多次跳过定义。

3
+1,虽然这让我想知道为什么需要超过两次扫描代码。第一次用于预处理器,第二次将代码加载到抽象语法树中以进行进一步分析。 - Justin Ethier
1
我同意,不应该需要超过两次传递。一次是预处理器,一次是编译器。但是我怀疑编译器可能会对语法树进行多次遍历! - Matthieu M.
@Justin Ethier,所以一旦代码达到AST或其他中间表示形式,它就不再被“跳过”了吗?(在这方面问题似乎有点不清楚) :-) - user166390
2
预处理不一定需要在单独的步骤中完成。 - James McNellis
事实上,“传递”这个概念本身就是不必要的限制。它实际上可以追溯到编译器无法适应内存的时代。现代编译器可以采用其他方式设计。 - MSalters
显示剩余2条评论
8个回答

7

编译器的传递和文件可能是你要找的最接近的东西。

列在那里的有:

  • 解析传递
  • Gimplification 传递
  • 传递管理器
  • 跨过程优化传递
  • Tree SSA 传递
  • RTL 传递

3

正如其他人在上面指出的那样,现代编译器只在解析阶段执行一次单独的通行,并在后续阶段使用内部表示执行多次通行(通常是树形或其他类似内存图的数据结构)。

具体而言,GCC采用了这种方法。请参见:https://gcc.gnu.org/onlinedocs/gccint/Parsing-pass.html#Parsing-pass


2
在gcc中,基本上有两种类型的passes: gimple和rtl。在gcc 4.6.2中,总共有207个独特的passes。是的,给定程序的总passes数量取决于优化级别。其中一些passes会被多次执行。如果有人想浏览这些passes,请查看gcc源代码中的passes.c文件。gcc 4.6.2中passes.c的路径为:gcc源文件->gcc->passes.c。
是的,您可以通过将您的passes作为动态插件添加到gcc中来更改passes的数量。

你将会因为删除太多的回答而触发自动封禁。 - user1228

1

我从未听说过编译器在文本表示上多次通过(除非您将预处理器视为一次通过)。即使编译器有多个通过文件通信的传递,这些文件也包含中间表示(序列化AST +符号表)。

另一方面,汇编程序通常会对源代码进行两次(或更多)通过。它们的预处理器通常允许在一次通过上执行某些操作,从而允许进行一些更或多或少肮脏的技巧。


0
据我在编译器设计课上听到的某人所说,gcc只进行一次遍历,而其他编译器(如Visual Studio默认使用的编译器)则进行两次遍历。这就是为什么如果您在C++中以循环方式使用类,则必须进行前向声明的原因。
Class A {
   B* b; 
}

Class B {
   A* a;
}

C#和其他语言不需要这样做,因为第一遍构建引用,第二遍编译。

但是我并不是编译器方面的专家。


11
前置声明是语言的属性,与gcc无关。这可能与K&R原始C编译器的实现有关。 - David Thornley
3
这与独立的翻译单元和链接概念更相关。可以说,这是一个单独的编译器步骤,但不是GCC步骤。 - MSalters

0

只需要一次。如果“代码”指的是程序源代码的原始文本表示,我不认为任何现代编译器有任何有意义的理由对其进行多次处理。这个单程的整个重点是将源代码转换成某种内部表示形式,该形式将用于进一步分析。该内部表示形式不再需要具有任何线性结构和/或仅限于顺序处理,这意味着“执行遍历”的概念已经不再适用。

如果您不满意这个答案,您可能应该提供更精确的解释来定义您对源代码的“遍历”。


我所说的by pass是指仅跳过“程序源代码的原始文本表示”。双重编译器可能会进行第一次扫描以查找语法错误并收集有用信息,从而使其能够更好地使用第二次编译。 - Lazer
Lazer,一个优秀的编译器在第一遍收集了许多有用的信息,以至于第二遍没有添加任何内容。毕竟,文本并没有改变,第二遍还能找到第一遍找不到的东西吗? - MSalters
@Nathan Adams: 这完全是错误的。C语言不需要原型。C++语言允许在类体内定义的内联函数的开始处使用类的完整声明。因此,“向前查看”的概念显然存在于C++中,这通常被称为“多遍编译”。声称从这个角度来看C++是“一遍”编译是明显错误的。 - AnT stands with Russia
然而,这个概念不应该被字面理解。"多遍编译"并不意味着对程序的词法表示进行字面线性扫描。而这正是问题所在。 - AnT stands with Russia
@Nathan Adams:你的codepad示例清楚地表明你误解了术语。现代C需要函数声明,而不是原型声明原型是两个完全不同的东西。除此之外,早期的C99语言甚至不需要声明。因此,在C中从来不需要原型(除了涉及可变参数函数的已知情况),而在C89/90中也不需要声明。因此,你提到C原型(或声明)的观点完全不清楚。 - AnT stands with Russia
此外,我在回答中提出的陈述非常明确。我再次引用它:“如果您所说的“代码”是程序源代码的原始文本表示形式,那么我不认为任何现代编译器有任何有意义的理由对源代码进行多次通行证。”这适用于任何编译器。如果您知道一些实际上这样做的编译器,也许您应该问问他们为什么要做这样毫无意义的事情。(至于您通过SO链接试图传达的信息...我很困惑。它在哪里提到与此主题相关的内容?) - AnT stands with Russia

0

你对多遍编译的定义似乎是旧的,源自于整个程序源代码只是不能适应可用内存的时代。这些时代已经过去了,我不知道任何一个现在使用旧定义的多遍编译器。

在德语维基百科的编译器条目中,给出了两个定义:http://de.wikipedia.org/wiki/Compiler

多遍编译器
在这种编译器类型中,源代码会经过多个步骤转换为目标代码。在编译器建立的早期,将翻译过程分成多个步骤主要是因为计算机的容量通常不足以同时保存完整的编译器和要翻译的程序。现今,多遍编译器主要用于解决前向引用(在首次使用之后声明标识符)和执行复杂的优化。

1
也许我有点偏见,但我的理解是,英文维基百科的引用通常更有用。谢谢。 - Iskander Sharipov
@IskanderSharipov 我完全同意。不幸的是,我在英文维基百科中没有找到相应的条目。被命名为“多遍编译器”的那个问题被作者驳回,因为它包括对原始源代码以外的其他表示形式的遍历。 - Peter G.

-1

你是指对源代码进行一次扫描吗?只有一次。这被称为“标记化”或“词法分析”阶段,或者更广泛地说,“解析”。

你是指编译器中的“阶段”吗?有几个。术语“pass”实际上更多地是一个旧的汇编器概念,而不是一个编译器概念,即使在那时它也只是粗略地使用。术语“pass”没有单一的定义。

编译器被分解成“阶段”。阅读任何编译器教材的介绍。它将解释这些阶段(大约有十二个逻辑阶段),GCC相当忠实地遵循了教科书模型。有些阶段通常合并成一个“pass”,而其他阶段则是单独的“passes”。

在讨论编译器方面,“pass”概念实际上并不像“phase”概念那样有用。


http://en.wikipedia.org/wiki/One-pass_compiler http://en.wikipedia.org/wiki/Multi-pass_compiler - el.pescado - нет войне

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接