target triple 的三段式结构必须严格为 arch-vendor-os,缺一不可且顺序不可颠倒,它决定后端选择、abi、调用约定及内置函数;错误会导致目标不可创建或链接失败,裸机需用 unknown/none,验证须用 llc/clang 命令行实测。

Target Triple 的三段式结构必须严格匹配目标平台
Target Triple 不是随便拼出来的字符串,它由 arch-vendor-os 三部分组成,缺一不可,顺序也不能颠倒。LLVM 和 Clang 会用它精确查找对应后端、ABI 规则、默认调用约定和内置函数(__builtin_*)。填错最直接的表现是:error: unable to create target: 'xxx': No available targets are compatible with triple "xxx"。
常见错误包括:
- 把
aarch64写成arm64(Clang 接受后者作为别名,但某些 LLVM API 如TargetRegistry::lookupTarget只认标准形式) - 漏掉 vendor,比如写
aarch64-linux而不是aarch64-unknown-linux-gnu——unknown是合法 vendor 值,不能省 - OS 层写
android却没加androideabi或linux-android后缀,导致 libc++ 链接失败
裸机 MCU 场景下 vendor 和 os 必须用 unknown / none
对 Cortex-M、RISC-V 裸机程序,没有操作系统、没有标准 C 库,Triple 中的 vendor 和 os 必须明确设为 unknown 或 none,否则 Clang 会默认启用 glibc 相关 ABI 和符号,链接时找不到 __libc_start_main 等。
正确写法示例:
-
thumbv7m-unknown-elf(Cortex-M3/M4,软浮点,ARM Thumb 指令集) -
armv7em-unknown-eabi(Cortex-M4F,硬浮点,需配合-mfpu=vfp4 -mfloat-abi=hard) -
riscv32-unknown-elf(RV32IMAC,无 OS) -
spirv64-unknown-unknown(SPIR-V 64 位,vendor/os 均为 unknown)
注意:elf 和 eabi 不可混用。eabi 表示 Embedded ABI(含 AAPCS 规范),elf 仅表示输出格式,实际仍走 EABI;裸机项目推荐统一用 eabi。
如何验证你写的 Triple 是否被当前 LLVM 支持
不要靠猜或文档抄写,直接查本地安装的 LLVM 支持哪些 Triple:
- 运行
llc --version,末尾会列出所有已注册的 Target(如AArch64 ARM BPF MSP430 ... RISCV WebAssembly) - 用
clang --target=xxx --print-supported-cpus测试 Triple 解析是否成功(例如clang --target=thumbv8m.main-unknown-eabi --print-supported-cpus) - 在代码里调用
TargetRegistry::lookupTarget前,先打印sys::getDefaultTargetTriple()对照——它返回的是 host Triple,可作格式参考
如果 --print-supported-cpus 报错 unknown target,说明该 Triple 未被启用,可能原因:LLVM 编译时未开启对应 Target(如没加 -DLLVM_TARGETS_TO_BUILD="ARM;RISCV"),或拼写大小写错误(ARM ≠ arm)。
Clang 命令行中 Triple 的两种等效写法
Clang 允许通过两种方式指定 Triple,行为略有差异:
-
clang --target=aarch64-unknown-linux-gnu:完整指定,覆盖所有后端行为(ABI、默认 FPU、内建函数) -
clang -march=armv8-a -mcpu=cortex-a72 -target linux-gnu:拆开写,-target只管 vendor/os,-march/-mcpu控制指令集和微架构;但此时-target若不带 arch,Clang 会尝试从-march推断,容易出错
强烈建议始终使用第一种——显式、可控、无歧义。尤其在 CI 或跨团队协作中,--target= 是唯一能保证行为一致的方式。
真正容易被忽略的点是:Triple 不仅决定生成什么指令,还隐式控制 sizeof(void*)、__SIZEOF_POINTER__、__ARM_ARCH_8A 这类宏定义,以及 stdint.h 中类型宽度。一个字母写错,可能导致裸机启动代码里指针截断、栈帧错位,问题极难定位。











