恢复活力的微软C/C++编译器

[原文发表地址] Rejuvenating the Microsoft C/C++ Compiler

[原文发表时间] 2015/9/25 1:00AM

我们的编译器源代码处还有1982年微软最早开始编写编译器的注释代码,这些都比较陈旧。注释代码的人(Raplh Ryan) 给我了一篇他于1985年出版的名为"The C Programming Language and a C Compiler"的文章。内容非常有趣,许多文章中描述的代码至今仍然在使用。作者提到过编译C程序需要两张软盘已经192K的内存(推荐硬盘以及256K的内存)。在这种环境下就可以运行也意味着你一次不能在内存中保存太多内容。编译器设计为扫描程序转换声明与表达式为IL中间语言,并把转换后的中间语言一次写入硬盘,这个过程不需要把整个函数装载到内存。实际上,编译器还没有扫描到表达式的结尾就已经开始生成IL中间语言了。这意味着你可以在微型机上编译相对庞大的程序。

注意: 我们的编译器包含两块(编译器前端, 编译器后端)。编译器前端读取源代码,词汇,段落,进行语法分析并且生成IL。编译器后端读取IL中间语言并执行代码生成和优化。在下文中所用的”编译器“仅适用于编译器前端。

对于C代码(尤其是K&R C),这种运行机制很好。你不需要函数的原型,微软也使C 6.0 7.0可以支持C++的代码,这个功能发布于1989 1992年。 C++编译器共享了很多C编译器的代码, 现今仍然是这样,虽然编译器有不同的二进制文件 (c1.dll和c1xx.dll) ,它们分别由C和C++使用, 但是它们之间还是共享许多代码。

首先, 这种老式设计使C++编译器可以正常工作。然而,当出现模板时,就需要新的工作机制。新的机制会选择先对模板进行一些最小化解析, 然会捕捉整个模板作为一个字符串类型的语法符号(这很像编译器处理宏的方法)。然后,当模板正在实例化时,那些语法符号流会被解析器解析,模板参数会被替代。这种方法导致我们的编译器没有使用两个解析器进行查询。

但当你想保留程序的更多信息时,编译器可能不会如你所愿。当我们在编译时添加/analyze选项, 编译器也会在代码中添加相应代码,这些代码包含在#if结构中,我们将生成单独的二进制文件(c1ast。dll和c1xxast。dll)。过一段时间, 会导致生成超过6000个#if预处理区块。

静态分析工具通过捕捉编译器解析器中的解析段落来为整个函数构建一个AST。然而, 这样捕捉后的AST使用方式与与编译器对数据结构的使用有本质区别,经常会导致不一致性。当引入新的语法时,许多AST也需要实现两次, 先是编译器, 然后才是静态分析。

三年前, 我们就开始重构编译器底层代码。我们想修复一直存在的问题,就像新特性constexpr需要新的工作机制。目标是从根本上改变我们编译器解析分析代码的方式。

我们从几个关键准则来引导我们的开发过程。最重要的准则是所有的更新工作将会在同样的开发分支进行。我们不想出现有分歧的代码,而且那样会难以恢复。我们也想更快的看到实现后的价值。

第一阶段的工作最终实现在Visual Studio 2015。我们修改了很多编译器内部机制, 虽然直接可视的部分并不多。但最有价值的改变是c1ast.dll和c1xxast不再存在。对于编译过程中的静态分析和代码生成,它们使用同样的二进制文件。超过6000的#if区块不复存在,减少了200次分析过程的运行时检查。这些变动较大的改变导致RC版本的C++编译器取消的代码分析功能,因为我们将这些#if区块从源代码中分离,并在它们原来的位置构建新的架构。

现在我们可以生成一个完整的功能树,可以使用同样的数据结构去生成代码并进行静态分析。这些功能树也可以用来评估constexpr函数,这个新特性已经随新版本的发布而发布。我们也可以追踪所有构造过程的源代码信息,源代码位置。现在我们不再使用列中的信息,但是我们在未来会提供更好的诊断功能。

由于这部分的变动, 我们在修复新bugs并且实现新特性时尽量使编译器拥有较好的向后兼容性。我们的自动化系统Gaunelet由50多台机器组成, 它们同时在32位,64位 ARM交叉平台上的不同编译器上运行大量的测试用例。所有Gaunelet上的测试通过后我们才能签入新的代码。同时我们也定期地运行大量的集合测试,这些测试都是上线产品中正在使用代码, 包含visual studio, Office, Windows, Chrome以及其他应用。这些工作可以更快的解决一些代码兼容性错误。

展望未来, 我们一直在提高我们的编译器。 我们已经开始了新的工作(解析模板到AST抽象语法树中),这将会立即支持SFINAE表达式和限定的名字的析。我们也一直在努力提高编译器并使它完全符合编译标准准则。还有,我们对Clang的支持也很有兴趣。实际上,已经在CPPcon发布了使用我们开发的代码生成器,优化器的Clang前端程序。这部分请参照如下链接:https://sched.co/3vc4

--Jim Springfield