探索 Rust 编译器选项:GCC 与 LLVM

Rust 在不牺牲性能的情况下保证内存安全,因此在软件开发人员中获得了大量追随者。Rust 曾经是小众语言,现在广泛应用于 Windows 和 Android 等各个系统。

rustc 使用 LLVM 作为后端来优化并将其转换为低级机器代码。然而,最近出现了一种新的编译器,替代 GCC 前端,称为 gccrs .

在本文中,我们将探讨 Rust 编译的发展,重点关注两个原生编译器项目:LLVM 和 GCC。

什么是LLVM?

LLVM 是由可重用的编译器和工具链组件组成的编译器基础设施的集合。从技术上讲,LLVM 是 Low-Level Virtual Machine 的首字母缩写词,但随着时间的推移,首字母缩写词本身已成为该项目的品牌。LLVM 因其跨各种编程语言优化代码和生成高性能机器代码的能力而闻名。

标准的编译器基础结构可以分为前端、中端和后端。前端充当中间代码(IR)与高级编程语言的转换层,这在不同的编译器(包括 LLVM 和 GCC)中是相似的。

中端对代码应用各种优化,例如循环展开和函数内联。LLVM IR 是 LLVM 的中间表示,可以根据目标直接编译多个不同的后端。

自 Rust 编译器成立以来, rustc LLVM 一直是 Rust 编译器的默认后端;rustc 本质上是一个 LLVM 前端。Rust 和 LLVM 之间的这种合作关系已被证明是成功的,因为 LLVM 的高级优化技术增强了 Rust 程序的性能,并允许它们在多个平台上运行。

GCC?

GCC代表GNU编译器集合,是一个开源编译器集合,支持各种编程语言,如C,C++,Fortran等。它以其稳定性、可靠性和对不同架构和操作系统的广泛支持而广为人知。

出于对自由软件生态系统的需求,编程世界在很大程度上归功于 GCC 在为各种平台实现代码编译方面的贡献。除了上面列出的语言之外,GCC 已经发展到支持许多其他语言,包括 Ada、Java、Go 以及最近(仍在开发中)的 Rust。

有多个前端支持各种语言,每个前端都会将编程语言转换为抽象语法树 (AST)。AST是前端和中端之间的中介。

虽然 LLVM 有 IR 作为其中介表示,但 GCC 有 GIMPLE 和 RTL。GIMPLE 是由 GCC 中间端处理的高级中介表示。GIMPLE 提供了程序的简化表示形式,保留了高级语义并简化了优化任务。

然后,在 GIMPLE 表示之后,代码进一步转换为 RTL。这种低级表示与汇编语言指令非常相似,在用于生成机器代码之前进行了进一步的优化。

目前正在为 Rust 开发一个名为 gccrs 的 GCC 前端。该项目尚不稳定,尚未正式纳入GCC。

GCC 与 LLVM:架构差异

作为一个编译器集合,GCC 与 LLVM 的编译方法不同。GCC 采用更传统的方法,使用前端来解析源代码并生成 AST。

然后,此 AST 被转换为称为 GIMPLE 的高级中介表示,它保留了程序的高级语义。与 LLVM 不同,GCC 添加了一个中间表示 RTL:

这两种优化的目标不同。GIMPLE 侧重于高级优化,而 RTL 侧重于低级优化和转换为类似汇编的指令。

LLVM 直接从前端获取其中间表示 LLVM IR。LLVM IR 优化与语言与架构无关。这允许 LLVM 执行各种优化,这些优化可以使不同的编程语言和目标架构受益:

然而,GCC 和 LLVM 之间最惊人的区别是它们如何构建源代码。LLVM 是模块化的;从一开始,它就被构建为可扩展的,并被多种语言使用,针对各种后端机器。

另一方面,GCC 被设计为具有紧密耦合组件的单体编译器。可以为 GCC 创建扩展,但其大部分代码都是紧密结合的,需要下载整个 GCC 代码库才能进行更改或添加。

使用 GCC 和 LLVM 设置 Rust

Rust 编程语言主要使用 LLVM 作为其默认的编译器基础设施。这意味着 Rust 代码默认使用 LLVM 的优化和转换来编译以生成机器代码。

如果你想为 Rust 设置 LLVM,你可以按照 Rust 安装说明进行操作。但是,要将 GCC 用于 Rust,需要使用 gccrs . gccrs 是 Rust 编译器的前端,它使用原生 GCC 作为后端。

要安装软件包 gccrs ,请打开 gccrs 存储库并按照其中提供的安装说明进行操作。这些指令基于 Debian Linux,因此你需要根据使用的发行版调整指令:

bash 复制代码
sudo apt install build-essential libgmp3-dev libmpfr-dev libmpc-dev flex bison autogen gcc-multilib dejagnu

然后,可以克隆 gccrs 存储库:

bash 复制代码
git clone https://github.com/Rust-GCC/gccrs

最后,构建工具链以创建一个与 gccrs 存储库相邻的新目录,并运行 make 以在那里编译构建:

bash 复制代码
mkdir gccrs-build
cd gccrs-build
../gccrs/configure --- prefix=$HOME/gccrs-install --- disable-bootstrap --- enable-multilib --- enable-languages=rust
make

安装过程完成后,将准备好二进制 gccrs 文件作为使用 GCC 后端编译 Rust 代码的前端。

gccrs 目前仍处于早期开发阶段,因此它无法支持大多数 Rust 语法,尤其是与 rustc LLVM 相比。例如,在撰写本文时, gccrs 不支持 Rust 宏。因此,很难将 Rust 与 GCC 和 LLVM 进行细致比较。

要开始使用 GCC 编译 Rust 代码,可以在 gccrs 项目测试套件提供的 gccrs 存储库中找到现成的测试代码。让我们举一个例子:

type_infer1.rs:

rust 复制代码
struct Foo {
    one: i32,
// { dg-warning "field is never read" "" { target *-*-* } .-1 }
    two: i32,
// { dg-warning "field is never read" "" { target *-*-* } .-1 }
}

fn test(x: i32) -> i32 {
    return x + 1;
}

fn main() {
    let logical: bool = true;
    // { dg-warning "unused name" "" { target *-*-* } .-1 }
    let an_integer = 5;
    let mut default_integer = 7;

    default_integer = 1 + an_integer;

    let call_test = test(1);
    // { dg-warning "unused name" "" { target *-*-* } .-1 }
    let struct_test = Foo { one: 1, two: 2 };
    // { dg-warning "unused name" "" { target *-*-* } .-1 }
}

该文件包含声明结构、定义函数并演示 Rust 中变量用法的代码。这是一个简单的类型推断示例,其中包含整数的变量没有被显式类型化,因为 Rust 可以根据其用法推断类型。

要使用 LLVM 编译此 Rust 代码,您可以使用 rustc 编译器:

bash 复制代码
rustc ./gccrs/gcc/testsuite/rust/compile/torture/type_infer1.rs type_infer1.rs

使用 rustc 运行它非常简单。但是,如果你想使用 GCC 作为前端编译相同的 Rust 代码,你必须传递更多的参数:

例如,使用二进制文件通过 GCC 编译此 Rust 代码。转到在上一节中创建的 gccrs-build 文件夹,然后运行以下命令:

bash 复制代码
./gcc/crab1 ../gccrs/gcc/testsuite/rust/compile/torture/type_infer1.rs -frust-debug -Warray-bounds -dumpbase ../gccrs/gcc/testsuite/rust/compile/torture/type_infer1.rs -mtune=generic -march=x86--64 -O0 -version -fdump-tree-gimple -o test -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib64 -frust-incomplete-and-experimental-compiler-do-not-use

此命令将构建一个可用于执行 Rust 代码的二进制文件。它应该输出如下内容:

xml 复制代码
Analyzing compilation unit
Performing interprocedural optimizations
<*free_lang_data> {heap 1432k} <visibility> {heap 1432k} <build_ssa_passes> {heap 1432k} <opt_local_passes> {heap 1704k} <remove_symbols> {heap 1704k} <targetclone> {heap 1704k} <free-fnsummary> {heap 1704k}Streaming LTO
<whole-program> {heap 1704k} <fnsummary> {heap 1704k} <inline> {heap 1704k} <modref> {heap 1704k} <free-fnsummary> {heap 1704k} <single-use> {heap 1704k} <comdats> {heap 1704k}Assembling functions:
<simdclone> {heap 1704k} type_infer1::test type_infer1::main
Time variable usr sys wall GGC
phase parsing : 0.00 ( 0%) 0.00 ( 0%) 0.01 ( 50%) 82k ( 24%)
phase opt and generate : 0.00 ( 0%) 0.01 (100%) 0.01 ( 50%) 121k ( 35%)
trivially dead code : 0.00 ( 0%) 0.00 ( 0%) 0.01 ( 50%) 0 ( 0%)
parser (global) : 0.00 ( 0%) 0.00 ( 0%) 0.01 ( 50%) 82k ( 24%)
initialize rtl : 0.00 ( 0%) 0.01 (100%) 0.00 ( 0%) 12k ( 4%)
TOTAL : 0.00 0.01 0.02 343k
Extra diagnostic checks enabled; compiler may run slowly.
Configure with - enable-checking=release to disable checks.

请注意,它 gccrs 仍处于非常早期的开发阶段;你还不能导入外部包或库,它只部分支持 Rust 语言的功能。

未来展望:正在进行的项目和发展

使用 GCC 和 LLVM 编译 Rust 代码可能会在性能和优化方面产生不同的结果。这两种方法都有其独特的优势。就 GCC 而言,它可以针对多种架构,并且已经存在了更长的时间,使其在某些领域更加成熟和稳定。它有一个成熟的代码库,已经优化了几十年。

两个项目正在努力使 Rust GCC 兼容。第一个当然是gccrs ,第二个是 rustc_codegen_gcc

两者之间的区别在于, rustc_codegen_gcc 使用 rustc 做为 GCC 后端生成中间表示。它更稳定,并且可以提供更好的编译体验。

为什么 gccrs 对 Rust 社区很重要

rustc 与当前状态进行广泛比较是不公平 gccrs 的。

首先,它正处于社区主导的早期阶段,并且 rustc 背后有 Rust 基金会------ rustc 毕竟它是主要的 Rust 编译器。但是,拥有一个以社区为主导的编译器为 Rust 生态系统增加了更多的多样性,有助于使 Rust 在多个生态系统中更加通用。

GCC 是老的,相对稳定,它针对的是更多与 LLVM 不兼容的遗留系统------例如,摩托罗拉 68000 (m68k),这是 1980 年代和 1990 年代常用的传统微处理器。许多这样的应用场景都可以通过 GCC 轻松访问,而 LLVM 支持它们是不合理的,因为该技术已经过时了。

摘要、延伸阅读和资源

如果你想了解有关该主题的更多信息,请查看以下资源:

原文:blog.logrocket.com/exploring-r...

相关推荐
2401_857622661 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589361 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没2 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch3 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
杨哥带你写代码4 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
AskHarries5 小时前
读《show your work》的一点感悟
后端
A尘埃5 小时前
SpringBoot的数据访问
java·spring boot·后端
yang-23075 小时前
端口冲突的解决方案以及SpringBoot自动检测可用端口demo
java·spring boot·后端
Marst Code5 小时前
(Django)初步使用
后端·python·django
代码之光_19805 小时前
SpringBoot校园资料分享平台:设计与实现
java·spring boot·后端