我们的编译器很旧。 源代码中有1982年的注释,当时微软刚刚开始自己的C编译器项目。 这个人(ralphryan)的评论让我想到了他在1985年发表的一篇论文《C编程语言和C编译器》。 这是一本有趣的读物,他描述的一些内容至今仍反映在代码中。 他提到可以用两个软盘驱动器和192K的RAM(尽管他建议使用硬盘驱动器和256K的RAM)编译C程序。 能够在那种环境下运行意味着你不能一次在内存中保存很多工作。 编译器的设计目的是扫描程序,将语句和表达式尽快转换成IL(中间语言),并将它们写入磁盘,而不必一次在内存中存储整个函数。 实际上,编译器甚至在看到表达式的结尾之前就开始为表达式发出IL。 这意味着您可以在相当小的机器上编译相当大的程序。
注意:我们的编译器由两部分组成(前端和后端)。 前端读取源代码、词法分析、语法分析、语义分析并发出IL。 后端读取IL并执行代码生成和优化。 本文其余部分使用的术语“编译器”只适用于前端。
对于C代码(尤其是K&rcc),这种方法非常有效。 记住,你甚至不需要函数的原型。 微软在C中增加对C++的支持 6 7.0,于年发布 1989 1992 它与C编译器共享了许多相同的代码,这在今天仍然是正确的。 虽然编译器有两个不同的二进制文件(C1.DLL和C1XX.DLL),但是C和C++之间有很多源代码共享。
最初,编译器的旧设计对C++来说是OK的。 然而,一旦模板出现,就需要一种新的方法。 选择的实现方法是对模板进行一些最小的解析,然后将整个模板捕获为一个令牌字符串(这与编译器中处理宏的方式非常相似)。 稍后,当模板被实例化时,将通过解析器重放该令牌流,并替换模板参数。 这种方法是我们的编译器从未实现两阶段查找的根本原因。
我们的编译器的设计也使得它不适用于其他需要保留更多程序信息的用途。 当我们在编译器中添加对静态分析(/analyze)的支持时,它被添加到与实际编译器相同的代码库中,但代码位于#if块下,我们生成了单独的二进制文件(c1ast.dll和c1xxast.dll)。 随着时间的推移,这导致超过6000个if预处理器块。
静态分析工具通过在常规编译器进行解析时捕获片段来为整个函数构建AST。 然而,这种捕获的AST与实际编译器用于其数据结构的内容有根本的不同,后者常常导致不一致。 另外,随着新语言特性的增加,大多数特性必须实现两次:一次用于编译器,另一次用于静态分析。
大约三年前,我们开始了一个项目,最终对我们的编译器代码库进行了一次大修。 我们想解决我们已经有很长一段时间的问题,我们知道像constexpr这样的新特性将需要一种不同的方法。 目标是从根本上改变编译器解析和分析代码的方式。
我们很快决定了几个关键原则来指导我们的发展。 最重要的原则是,我们所做的所有复兴工作都将在与功能相同的开发分支中完成。 我们不想“变黑”并且有两个难以重新整合的不同的代码基。 我们也希望快速看到价值,事实上,我们需要快速的价值。
这项工作的第一阶段终于在VisualStudio2015中发布了。 我们已经在编译器的内部实现中改变了很多细节,尽管没有多少是直接可见的。 最明显的变化是c1ast.dll和c1xxast.dll不再存在。 我们现在使用与代码生成相同的二进制文件来处理静态分析的所有编译。 所有6000+#如果块都没有了,我们只有不到200个运行时检查进行分析。 这个巨大的变化是为什么在C++编译器的一些RC构建中禁用代码分析,因为我们撕开了“如果if块”,然后不得不在其位置构建新的基础结构。
这样做的结果是,我们现在为函数生成一个完整的树,并且可以使用相同的数据结构来生成代码或执行静态分析。 这些树也用于计算constexpr函数,这是我们刚刚发布的一个特性。 我们现在还跟踪所有构造的完整源位置信息(包括列)。 我们目前不使用列信息,但希望将来能够提供更好的诊断。
在进行这些更改时,我们将尽力提供尽可能多的向后兼容性,同时修复真正的bug并在编译器中实现新功能。 我们有一个名为Gauntlet的自动化系统,它由50多台机器组成,这些机器构建了编译器的所有版本,并在32位、64位和ARM架构(包括交叉编译器)的所有版本上运行许多测试。 所有更改必须在签入前通过挑战。 我们还定期运行一组更大的测试,并在“真实代码”上使用我们的编译器来构建visualstudio、Office、Windows、Chrome和其他应用程序。 这项工作很快就解决了额外的兼容性问题。
展望未来,我们将继续投资改进我们的编译器。 我们已经开始将模板解析到AST(抽象语法树)中,这将立即改进我们对表达式SFINAE的支持和对“限定名”的解析。 我们将继续投资改进我们的编译器,目标是使其完全符合标准。 也就是说,我们也非常有兴趣改善我们对Clang的支持。 事实上,在CppCon上有一个演示,介绍了如何在代码生成器和优化器中使用Clang前端。 以下是该会话的链接。 http://sched.co/3vc4
——吉姆·斯普林菲尔德