Target Triple 到底是什么?
Target Triple 到底是什么?
Cross-compiling (交叉编译)是指编译一个计算机程序,使其在与编译主机不同的机器上运行。 尽管历史上编译器只为宿主机编译,但这被认为是一种过时做法:现在所有重要的原生编译器都是交叉编译器。
毕竟,你不会想在真正的 iPhone 硬件上构建你的 iPhone 应用程序。
许多不同的编译器有不同的机制来分类和识别 targets (目标)。 target 是编译器可以为其生成可执行代码的平台。 然而,由于 LLVM 的迅猛普及,实际上所有编译器现在都使用 target triples (目标三元组)。 你可能已经遇到过,比如久负盛名的 x86_64-unknown-linux
,或者邪恶的 x86_64-pc-windows
。 这个系统错综复杂,并且 几乎 自洽。
但是,target triple 到底是什么,它们又是从哪里来的?
愚蠢的 GCC 约定
所以,如果你去浏览 OSDev 上的 Target Triplet 页面,你会学到关于 target triples 的真假信息,因为这个页面是关于 GCC 的,而不是关于一般的原生编译器的。
一般来说,target triple 是什么并没有“绝对真理”。 没有哪个标准机构会分配这些名称。 但正如我们将看到的,LLVM 是潮流引领者。
如果你运行以下命令,你可以了解你机器的 target triple:
$gcc -dumpmachine
x86_64-linux-gnu
Console
现在,如果你对任何广泛使用 target triples 的系统稍有了解,你就会知道这不是 一个 target triple,因为这个 target 的名称是 x86_64-unknown-linux-gnu
,这是 clang
和 rustc
都称呼它的方式——
$clang -dumpmachine
x86_64-pc-linux-gnu
$rustc -vV | grep host
host: x86_64-unknown-linux-gnu
Console
哦,不。
好吧,GCC 缺少 pc
或 unknown
组件,这专门是 GCC 的事情;它允许以明确的方式省略 triple 的某些部分。 而且它们是 GCC 的发明,所以最好从评估 GCC 的观点开始。
根据 GCC 的说法,target triple 是 <machine>-<vendor>-<os>
形式的字符串。“machine”部分明确地标识了系统的架构。 实际上,这是编译器最终输出的汇编语言。“vendor”部分本质上是不相关的,主要用于将相关的操作系统排序在一起。 最后,“os”部分标识了为此代码编译的操作系统。 对于编译器来说,这主要确定了可执行文件格式:Windows 的 COFF/PE,Apple 操作系统的 Mach-O,Linux 及其朋友的 ELF,等等(但这是一种过度简化)。
但你可能会注意到 x86_64-unknown-linux-gnu
有一个额外的第四个条目1,它扮演着许多角色,但最常被称为 target 的“ABI”。 对于 linux
,它标识了 target 的 libc,这对某些语言特性(例如线程局部变量和解旋)的代码生成有影响。 它是可选的,因为许多 target 只有一个 ABI。
使用 GCC 进行交叉编译
这里的关键历史是理解 GCC 进行交叉编译的非常愚蠢的方式。 传统上,每个 GCC 二进制文件都是为 一个 target triple 构建的。 GCC 二进制文件的完整名称将包含该 triple,因此在交叉编译时,你将使用 x86_64-unknown-linux-gcc
进行编译,使用 x86_64-unknown-linux-ld
进行链接,等等(在这里,gcc
不是 triple 的第四个 ABI 组件;它只是 x86_64-unknown-linux
工具链中的一个工具)。
没有脑子的人会这样做2。 LLVM 和所有遵循它的交叉编译器都将所有的后端放在一个二进制文件中,并使用像 --target
这样的编译器标志来选择后端。
但无论如何,这就是 target triples 的由来,以及它们看起来像现在这样的原因:它们最初是 autoconf
脚本中二进制文件名称的前缀。
但 GCC 是一种古老的技术。 在 21 世纪,LLVM 统治着所有的原生编译器。
古老语言中的名称
LLVM 的 target triple 列表应该被认为是“最官方的”,原因如下:
- 惯性。 每个人及其母亲都使用 LLVM 作为中间端和后端,因此它的命名约定会渗透到诸如
clang
、rustc
、swiftc
、icc
和nvcc
之类的语言前端中。 - 芯片和操作系统供应商的上游工作。 LLVM 是人们主要受雇从事的工作,而不是 GCC,因此它的平台特定约定通常反映了供应商的偏好。
这些在很大程度上是因为 Apple、Google 和 Nvidia 拥有大量的编译器工程师为 LLVM 做出贡献。
“官方” target triples 的来源有很多。 通常,我会将 target triple 描述为“官方”当:
- 主要的编译器(比如
clang
或rustc
)使用它。 在记录它们的 target 方面,Rust 比 LLVM 做得更好,所以我更喜欢尊重它。 你可以在这里找到 Rust 的官方 triples。 - 平台开发者(例如,硬件制造商、操作系统供应商)分发一个工具链,该工具链的 target triple 格式为
arch-vendor-os
。
那么,(1) 类中的名称是什么? LLVM 并没有真正尽力提供这样的列表。 但我们必须从某个地方开始,所以让我们深入研究源代码。
我们可以深入研究 LLVM 的 target triple 解析器中的 Triple.cpp
。 它列出了 LLVM 识别的 triple 的每个部分的所有名称。 查看 Triple::parseArch()
,我们有以下名称,包括许多别名。 右列的第一个项目是 LLVM 的架构首选名称,由 Triple::getArchTypeName()
指示。
架构 | 可能的名称
---|---
Intel x86 (32-bit) | i386
, i486
, i586
, i686
, i786
, i886
, i986
Intel x86 (64-bit) | x86_64
, amd64
, x86_86h
3
ARM (32-bit) | arm
, xscale
, …
ARM (32-bit, big-endian) | armeb
, xscaleeb
, …
ARM (64-bit) | aarch64
, aarch64e
, aarch64ec
, arm64
, …
ARM (64-bit, big-endian) | aarch64_be
, …
ARM (64-bit, ILP324) | aarch64_32
, arm64_32
, …
ARM Thumb | thumb
, …
ARM Thumb (big-endian) | thumbeb
, …
IBM PowerPC5 (32-bit) | powerpc
, powerpcspe
, ppc
, ppc32
IBM PowerPC (little-endian) | powerpcle
, ppcle
, ppc32le
IBM PowerPC (64-bit) | powerpc64
, ppu
, ppc64
IBM PowerPC (64-bit, little-endian) | powerpc64le
, ppc64le
MIPS (32-bit) | mips
, mipseb
, mipsallegrex
, mipsisa32r6
, mipsr6
MIPS (32-bit, little-endian) | mipsel
, mipsallegrexel
, mipsisa32r6el
, mipsr6el
MIPS (64-bit) | mips64
, mips64eb
, mipsn32
, mipsisa64r6
, mips64r6
, mipsn32r6
MIPS (64-bit, little-endian) | mips64el
, mipsn32el
, mipsisa64r6el
, mips64r6el
, mipsn32r6el
RISC-V (32-bit) | riscv32
RISC-V (64-bit) | riscv64
IBM z/Architecture | s390x
6, systemz
SPARC | sparc
SPARC (little-endian) | sparcel
SPARC (64-bit) | sparcv6
, sparc64
WebAssembly (32-bit) | wasm32
WebAssembly (64-bit) | wasm64
Loongson (32-bit) | loongarch32
Loongson (64-bit) | loongarch64
Radeon R600 | r600
AMD GCN | amdgcn
Qualcomm Hexagon | hexagon
Nvidia PTX7 (32-bit) | nvptx
Nvidia PTX (64-bit) | nvptx64
AMD IL8 (32-bit) | amdil
AMD IL (64-bit) | amdil64
Direct-X IL | dxil
, …
HSAIL (32-bit) | hsail
HSAIL (64-bit) | hsail64
Khronos SPIR (32-bit) | spir
Khronos SPIR (64-bit) | spir64
Khronos SPIR-V | spirv
, …
Khronos SPIR-V (32-bit) | spirv32
, …
Khronos SPIR-V (64-bit) | spirv64
, …
Android RenderScript (32-bit) | renderscript32
Android RenderScript (64-bit) | renderscript64
Movidius SHAVE | shave
Atmel AVR | avr
Motorola 68k | m68k
Argonaut ARC | arc
Texas Instruments MSP430 | msp430
Tensilica Xtensa | xtensa
C-SKY | csky
OpenASIP | tce
OpenASIP (little-endian) | tcele
Myracom Lanai | lanai
XMOS xCore | xcore
Kalimba9 | kalimba
VE9 | ve
在这里,我们开始看到 target triples 不是一个整洁的系统。 它们是 地狱。 如果架构名称列表中包含“…”,则表示 LLVM 接受更多名称。
问题在于架构通常具有 版本 和 特性,这些版本和特性会微妙地改变编译器生成代码的方式。 例如,在为 x86_64
编译时,我们可能需要指定我们想要使用 AVX512 指令。 在 LLVM 上,你可以使用 -mattr=+avx512
来做到这一点。 每个架构都有微妙不同的方式来做到这一点,因为每个架构都有 不同的 GCC! 每个 GCC 变体都会将不同的东西放在 -mXXX
标志(-m
代表 “machine”)后面,这意味着该接口实际上并不统一。 因此,-march
、-mcpu
、-mtune
和 -mattr
的含义差异很大。
因为 LLVM 应该(在很大程度上)取代 GCC,所以它复制了很多这种古怪的行为。
所以,我们需要谈谈 32 位 ARM 架构名称。
ARMTargetParser.cpp
LLVM 中有一个专门用于解析 ARM 架构名称的地狱般的文件。 尽管 ARM 系列的成员具有许多可配置的特性(你可以使用 llc -march aarch64 -mattr help
10发现这些特性),但架构的名称在某种程度上是有意义的,并且可以有许多选项,主要与存在的许多 ARM 版本有关。
它有多糟糕? 好吧,我们可以查看 rustc
使用 rustc --print target-list
支持的各种 ARM target:
$rustc --print target-list | grep -P 'arm|aarch|thumb' \
| cut -d- -f1 | sort | uniq
aarch64
aarch64_be
arm
arm64_32
arm64e
arm64ec
armeb
armebv7r
armv4t
armv5te
armv6
armv6k
armv7
armv7a
armv7k
armv7r
armv7s
armv8r
thumbv4t
thumbv5te
thumbv6m
thumbv7a
thumbv7em
thumbv7m
thumbv7neon
thumbv8m.base
thumbv8m.main
Console
这些大多是 32 位 ARM 版本,附带配置文件信息。 这些对应于此处给出的名称。 为什么 ARM 将版本号放入架构名称中,而不是像你在 x86 上那样使用 -mcpu
(例如 -mcpu alderlake
)? 我不知道,因为 ARM 不是我的强项。 这可能是因为 ARM 支持很早就被添加到 GCC 中。
在内部,LLVM 将这些称为“subarchitectures (子架构)”,尽管 ARM 获得了特殊处理,因为存在如此多的变体。 SPIR-V、Direct X 和 MIPS 都有子架构,因此如果你倒霉,可能会看到类似 dxilv1.7
的东西。
当然,LLVM 的 ARM 支持也支持一些不属于该系统的顽皮的子架构,并带有顽皮的虚构名称。
arm64e
是 Apple 的东西,它是某些 Apple 硬件上存在的aarch64
的增强版本,它添加了他们自己风格的指针身份验证和其他一些特性。arm64ec
是 Microsoft 的一个完全不相关的发明,本质上是“aarch64
但具有x86_64
式 ABI”,以使在原本是aarch64-pc-windows-msvc
target 的情况下x86_64
模拟更方便。
为什么 Windows 人员发明了另一个 ABI,而不是像 Apple 使用 ARM MacBooks 上的 Rosetta 那样使事情变得干净和简单? 我不知道,但 http://www.emulators.com/docs/abc_arm64ec_explained.htm 包含各种借口,但我对其中任何一个都没有留下深刻的印象。 我的理解是,他们的编译器组织只是在生活中比 Apple 的差,这并不奇怪,因为 Apple 在编译器方面的表现优于该行业中的任何其他人。
实际上,既然我们正在讨论架构的名称,我有一些事情需要澄清。
虚构的架构名称
x86 和 ARM 似乎都吸引了很多人为它们编造昵称,这导致了以下方面的很多困惑:
- 什么是“真正的”名字。
- 特定工具链想要什么名字。
- 你应该在你自己的世界性工具中使用什么名称。
让我们谈谈人们喜欢为它们编造的不正确的名称。 请根据我自己在许多工具中的经验,将以下内容视为关于人们如何称呼这些架构的相对规范的参考。
当我们说“x86”不加限定词时,在 2025 年,我们几乎总是指 x86_64
,因为 32 位 x86 已经死了。 如果你需要谈论 32 位 x86,你应该说“32 位 x86”、“protected mode”11 或 “i386”(第一个实现 protected mode 的 Intel 微架构)12。 你不应该称其为 x86_32
或仅仅 x86
。
你也可以将其称为 IA-32(Intel Architecture 32,或 ia32
),但没有人会这样称呼它,并且你可能会将人们与 ia64
(或 IA-64)混淆,这是 Intel 失败的通用 VLIW 架构 Itanium 的官方名称,它与 x86 完全不兼容。 ia64
是 GCC 和 LLVM 用于命名 Itanium triples 的名称。 Itanium 支持在奥巴马政府期间被淹没在浴缸中,所以它不再重要了。 Rust 从未有过官方的 Itanium 支持。
32 位 x86 绝对不能 称为 “x32”; 这是 Linux 在删除之前用来称呼其 x86 ILP324 变体的名称(按照 ARM 名称,它本应称为 x86_6432
)。
还有许多虚构的 64 位 x86 名称,除非你想让年轻一代嘲笑你,否则你应该避免使用它们。 amd64
指的是 AMD 在他们的 K8 微架构中对 long mode 的原始实现,该实现首先在他们的 Athlon 64 产品中发布。 当然,AMD 仍然制造最好的 x86 芯片(我正在一台装有 Zen2 Threadripper 的机器上编写这篇文章),但是称其为 amd64
是很愚蠢的,而且看起来很像 arm64
,而且我真的很讨厌我看到这么多 Go 代码的文件名为 fast_arm64.s
和 fast_amd64.s
。 Debian 也使用 amd64
/arm64
,这使得浏览软件包有点烦人。
关于这个话题,你_绝对不应该_ 在 AMD K8 之后将 64 位模式称为 k8
。 除了像我这样奇怪的计算机分类学家之外,没有人知道那是什么。 但是 Bazel 就是这么称呼它的,这真的很令人恼火13。
你也不应该称其为 x64
。 尽管 LLVM 出于历史原因确实接受 amd64
,但除了 Microsoft 之外,没有人称其为 x64
。 即使它在 Windows 上相当普遍,当我游戏开发的朋友写 x64
时,我绝对会让他们难堪。
在 ARM 方面,好吧。 Arm14 有一个坏习惯,就是对 64 位 ARM 使用不一致的命名,因为他们同时使用了 AArch64 和 ARM64。 但是,在编译器领域,aarch64
似乎更受欢迎。
你也应该坚持使用各种架构的 LLVM 名称,而不是选择你最喜欢的 Arm Cortex 名称(如 cortex_m0
)。
供应商和操作系统
最糟糕的已经过去。 现在让我们继续检查 triple 的其余部分:平台供应商和操作系统。
供应商旨在识别谁负责该 target 的 ABI 定义。 尽管对编译器本身几乎没有价值,但它确实有助于将相关的 target 排序在一起。 有点。
回到 llvm::Triple
,我们可以检查 Triple::VendorType
。 供应商几乎总是对应于开发操作系统或其他代码运行平台的公司,但也有一些例外。
我们还可以使用一个方便的命令来获取 rustc
知道的供应商:
rustc --print target-list | grep -P '\w+-\w+-' | cut -d- -f2 | sort | uniq
Console
结果是这样的。 这只是一个具有代表性的列表;我省略了一些不太容易识别的列表。
供应商 | 名称 | 示例 Triple
---|---|---
Vendor Unknown15 | unknown
| x86_64-unknown-linux
“PC” | pc
| x86_64-pc-windows-msvc
Advanced Micro Devices Inc. | amd
| amdgcn-amd-gfx906
Apple Inc. | apple
| aarch64-apple-ios-sim
Intel Corporation | intel
| i386-intel-elfiamcu
IBM Corporation | ibm
| powerpc64-ibm-aix
Mesa3D Project | mesa
| amdgcn-mesa-mesa3d
MIPS Technologies LLC | mti
| mips-mti-none-elf
Nintendo | nintendo
| armv6k-nintendo-3ds
Nvidia Corporation | nvidia
| nvptx64-nvidia-cuda
Sony Interactive Entertainment | scei
, sie
,