代码生成属性
以下 属性 用于控制代码生成。
优化提示
cold 和 inline 属性 提供生成代码的建议,可能比没有这些提示时生成得更快。这些属性仅为提示,可能会被忽略。
这两个属性都可以用于 函数。当应用于 trait 中的函数时,它们仅在该函数用作 trait 实现的默认函数时生效,而不是应用于所有 trait 实现。这些属性对没有函数体的 trait 函数没有影响。
inline 属性
inline 属性 建议将带有该属性的函数副本放置在调用者处,而不是生成代码去调用函数定义位置的代码。
注意
rustc编译器会根据内部启发式算法自动内联函数。不正确地内联函数可能会使程序变慢,因此应谨慎使用此属性。
使用 inline 属性有三种方式
#[inline]建议 执行内联扩展。#[inline(always)]建议 始终执行内联扩展。#[inline(never)]建议 永远不要执行内联扩展。
注意
任何形式的
#[inline]都只是一个提示,语言对将带属性的函数副本放置在调用者处没有 强制要求。
cold 属性
cold 属性 建议带属性的函数不太可能被调用。
no_builtins 属性
no_builtins 属性 可以应用于 crate 级别,以禁用将某些代码模式优化为调用假定存在的库函数。
target_feature 属性
target_feature 属性 可应用于函数,以针对特定的平台架构特性启用该函数的代码生成。它使用 MetaListNameValueStr 语法,其中只有一个键 enable,其值是逗号分隔的要启用的特性名称字符串。
#![allow(unused)] fn main() { #[cfg(target_feature = "avx2")] #[target_feature(enable = "avx2")] fn foo_avx2() {} }
每个 目标架构 都有一组可以启用的特性。为 crate 未编译的目标架构指定特性是错误的。
在带有 target_feature 注解的函数中定义的闭包会继承包含函数的属性。
调用一个使用当前代码运行平台不支持的特性编译的函数是 未定义行为,除非 平台明确说明这是安全的。
除非下述平台规则另有规定,否则以下限制适用
- 安全的
#[target_feature]函数(以及继承该属性的闭包)只能在启用了被调用方所有target_feature特性的调用方中安全地调用。此限制不适用于unsafe上下文。 - 安全的
#[target_feature]函数(以及继承该属性的闭包)只能在启用了被强制转换方所有target_feature特性的上下文中被强制转换为 安全的 函数指针。此限制不适用于unsafe函数指针。
隐式启用的特性也包含在此规则中。例如,一个 sse2 函数可以调用标记有 sse 的函数。
#![allow(unused)] fn main() { #[cfg(target_feature = "sse2")] { #[target_feature(enable = "sse")] fn foo_sse() {} fn bar() { // Calling `foo_sse` here is unsafe, as we must ensure that SSE is // available first, even if `sse` is enabled by default on the target // platform or manually enabled as compiler flags. unsafe { foo_sse(); } } #[target_feature(enable = "sse")] fn bar_sse() { // Calling `foo_sse` here is safe. foo_sse(); || foo_sse(); } #[target_feature(enable = "sse2")] fn bar_sse2() { // Calling `foo_sse` here is safe because `sse2` implies `sse`. foo_sse(); } } }
带有 #[target_feature] 属性的函数 永远不会 实现 Fn 系列 trait,尽管从包含函数继承特性的闭包会实现。
#[target_feature] 属性不允许用于以下位置
main函数panic_handler函数- 安全的 trait 方法
- trait 中安全的默认函数
标记有 target_feature 的函数不会被内联到不支持指定特性的上下文中。#[inline(always)] 属性不能与 target_feature 属性一起使用。
可用特性
以下是可用特性名称的列表。
x86 或 x86_64
在此平台上执行使用不支持特性编译的代码是未定义行为。因此,在此平台上使用 #[target_feature] 函数需遵循上述限制。
| 特性 | 隐式启用 | 描述 |
|---|---|---|
adx | ADX — 多精度带进位加法指令扩展 | |
aes | sse2 | AES — 高级加密标准指令集 |
avx | sse4.2 | AVX — 高级向量扩展 |
avx2 | avx | AVX2 — 高级向量扩展 2 |
bmi1 | BMI1 — 位操作指令集 | |
bmi2 | BMI2 — 位操作指令集 2 | |
cmpxchg16b | cmpxchg16b — 原子比较并交换 16 字节(128 位)数据 | |
f16c | avx | F16C — 16 位浮点转换指令 |
fma | avx | FMA3 — 三操作数融合乘加 |
fxsr | fxsave 和 fxrstor — 保存和恢复 x87 FPU、MMX 技术和 SSE 状态 | |
lzcnt | lzcnt — 前导零计数 | |
movbe | movbe — 交换字节后移动数据 | |
pclmulqdq | sse2 | pclmulqdq — 打包的无进位乘法四字 |
popcnt | popcnt — 置为 1 的位数计数 | |
rdrand | rdrand — 读取随机数 | |
rdseed | rdseed — 读取随机种子 | |
sha | sse2 | SHA — 安全哈希算法扩展 |
sse | SSE — 流式 SIMD 扩展 | |
sse2 | sse | SSE2 — 流式 SIMD 扩展 2 |
sse3 | sse2 | SSE3 — 流式 SIMD 扩展 3 |
sse4.1 | ssse3 | SSE4.1 — 流式 SIMD 扩展 4.1 |
sse4.2 | sse4.1 | SSE4.2 — 流式 SIMD 扩展 4.2 |
ssse3 | sse3 | SSSE3 — 补充流式 SIMD 扩展 3 |
xsave | xsave — 保存处理器扩展状态 | |
xsavec | xsavec — 保存处理器扩展状态(带压缩) | |
xsaveopt | xsaveopt — 保存处理器优化扩展状态 | |
xsaves | xsaves — 保存处理器扩展状态(supervisor 模式) |
aarch64
在此平台上,#[target_feature] 函数的使用遵循上述限制。
有关这些特性的更多文档可在 ARM 架构参考手册 或 developer.arm.com 的其他位置找到。
注意
如果使用以下成对特性,它们应同时被标记为启用或禁用
paca和pacg,LLVM 目前将其实现为一个特性。
| 特性 | 隐式启用 | 特性名称 |
|---|---|---|
aes | neon | FEAT_AES & FEAT_PMULL — 高级 SIMD AES & PMULL 指令 |
bf16 | FEAT_BF16 — BFloat16 指令 | |
bti | FEAT_BTI — 分支目标识别 | |
crc | FEAT_CRC — CRC32 校验和指令 | |
dit | FEAT_DIT — 数据独立时序指令 | |
dotprod | FEAT_DotProd — 高级 SIMD Int8 点积指令 | |
dpb | FEAT_DPB — 将数据缓存清除至持久点 | |
dpb2 | FEAT_DPB2 — 将数据缓存清除至深度持久点 | |
f32mm | sve | FEAT_F32MM — SVE 单精度 FP 矩阵乘法指令 |
f64mm | sve | FEAT_F64MM — SVE 双精度 FP 矩阵乘法指令 |
fcma | neon | FEAT_FCMA — 浮点复数支持 |
fhm | fp16 | FEAT_FHM — 半精度 FP FMLAL 指令 |
flagm | FEAT_FlagM — 条件标志操作 | |
fp16 | neon | FEAT_FP16 — 半精度 FP 数据处理 |
frintts | FEAT_FRINTTS — 浮点转整数辅助指令 | |
i8mm | FEAT_I8MM — Int8 矩阵乘法 | |
jsconv | neon | FEAT_JSCVT — JavaScript 转换指令 |
lse | FEAT_LSE — 大型系统扩展 | |
lor | FEAT_LOR — 有限排序区域扩展 | |
mte | FEAT_MTE & FEAT_MTE2 — 内存标记扩展 | |
neon | FEAT_FP & FEAT_AdvSIMD — 浮点和高级 SIMD 扩展 | |
pan | FEAT_PAN — 特权访问禁止扩展 | |
paca | FEAT_PAuth — 指针认证(地址认证) | |
pacg | FEAT_PAuth — 指针认证(通用认证) | |
pmuv3 | FEAT_PMUv3 — 性能监视器扩展 (v3) | |
rand | FEAT_RNG — 随机数生成器 | |
ras | FEAT_RAS & FEAT_RASv1p1 — 可靠性、可用性和可维护性扩展 | |
rcpc | FEAT_LRCPC — Release consistent Processor Consistent | |
rcpc2 | rcpc | FEAT_LRCPC2 — 带立即偏移的 RcPc |
rdm | FEAT_RDM — 舍入双精度乘加 | |
sb | FEAT_SB — 推测屏障 | |
sha2 | neon | FEAT_SHA1 & FEAT_SHA256 — 高级 SIMD SHA 指令 |
sha3 | sha2 | FEAT_SHA512 & FEAT_SHA3 — 高级 SIMD SHA 指令 |
sm4 | neon | FEAT_SM3 & FEAT_SM4 — 高级 SIMD SM3/4 指令 |
spe | FEAT_SPE — 统计分析扩展 | |
ssbs | FEAT_SSBS & FEAT_SSBS2 — 推测存储旁路安全 | |
sve | fp16 | FEAT_SVE — 可伸缩向量扩展 |
sve2 | sve | FEAT_SVE2 — 可伸缩向量扩展 2 |
sve2-aes | sve2, aes | FEAT_SVE_AES — SVE AES 指令 |
sve2-sm4 | sve2, sm4 | FEAT_SVE_SM4 — SVE SM4 指令 |
sve2-sha3 | sve2, sha3 | FEAT_SVE_SHA3 — SVE SHA3 指令 |
sve2-bitperm | sve2 | FEAT_SVE_BitPerm — SVE 位排列 |
tme | FEAT_TME — 事务内存扩展 | |
vh | FEAT_VHE — 虚拟化宿主扩展 |
riscv32 或 riscv64
在此平台上,#[target_feature] 函数的使用遵循上述限制。
这些特性的更多文档可在其各自的规范中找到。许多规范在 RISC-V ISA 手册 或托管在 RISC-V GitHub 账户 上的其他手册中描述。
| 特性 | 隐式启用 | 描述 |
|---|---|---|
a | A — 原子指令 | |
c | C — 压缩指令 | |
m | M — 整数乘法和除法指令 | |
zb | zba, zbc, zbs | Zb — 位操作指令 |
zba | Zba — 地址生成指令 | |
zbb | Zbb — 基本位操作 | |
zbc | Zbc — 无进位乘法 | |
zbkb | Zbkb — 密码学位操作指令 | |
zbkc | Zbkc — 密码学无进位乘法 | |
zbkx | Zbkx — 交叉开关排列 | |
zbs | Zbs — 单一位指令 | |
zk | zkn, zkr, zks, zkt, zbkb, zbkc, zkbx | Zk — 标量密码学 |
zkn | zknd, zkne, zknh, zbkb, zbkc, zkbx | Zkn — NIST 算法套件扩展 |
zknd | Zknd — NIST 套件:AES 解密 | |
zkne | Zkne — NIST 套件:AES 加密 | |
zknh | Zknh — NIST 套件:哈希函数指令 | |
zkr | Zkr — 熵源扩展 | |
zks | zksed, zksh, zbkb, zbkc, zkbx | Zks — 商密算法套件 |
zksed | Zksed — 商密套件:SM4 分组密码指令 | |
zksh | Zksh — 商密套件:SM3 哈希函数指令 | |
zkt | Zkt — 数据独立执行延迟子集 |
wasm32 或 wasm64
在 Wasm 平台上,安全的 #[target_feature] 函数始终可以在安全上下文中使用。通过 #[target_feature] 属性导致未定义行为是不可能的,因为尝试使用 Wasm 引擎不支持的指令会在加载时失败,不会出现与编译器预期不同的解释风险。
| 特性 | 隐式启用 | 描述 |
|---|---|---|
bulk-memory | WebAssembly 批量内存操作提案 | |
extended-const | WebAssembly 扩展常量表达式提案 | |
mutable-globals | WebAssembly 可变全局变量提案 | |
nontrapping-fptoint | WebAssembly 非陷阱浮点转整数转换提案 | |
relaxed-simd | simd128 | WebAssembly 松弛 SIMD 提案 |
sign-ext | WebAssembly 符号扩展运算符提案 | |
simd128 | WebAssembly SIMD 提案 | |
multivalue | WebAssembly 多值提案 | |
reference-types | WebAssembly 引用类型提案 | |
tail-call | WebAssembly 尾调用提案 |
附加信息
关于根据编译时设置选择性地启用或禁用代码编译,请参阅 target_feature 条件编译选项。请注意,此选项不受 target_feature 属性的影响,仅由整个 crate 启用的特性驱动。
对于这些平台上的运行时特性检测,请参阅标准库中的 is_x86_feature_detected 或 is_aarch64_feature_detected 宏。
注意
rustc为每个目标和 CPU 启用了一组默认特性。可以使用-C target-cpu标志选择 CPU。可以使用-C target-feature标志为整个 crate 启用或禁用单个特性。
track_caller 属性
除了入口点 fn main 外,track_caller 属性可以应用于任何具有 "Rust" ABI 的函数。
当应用于 trait 声明中的函数和方法时,该属性适用于所有实现。如果 trait 提供了带有该属性的默认实现,则该属性也适用于覆盖实现。
当应用于 extern 块中的函数时,该属性也必须应用于任何链接的实现,否则会导致未定义行为。当应用于可供 extern 块使用的函数时,extern 块中的声明也必须具有该属性,否则会导致未定义行为。
行为
将此属性应用于函数 f 允许 f 中的代码获取导致 f 被调用的“最顶层”被追踪调用的 Location 的提示。在观察点,实现的行为就像它从 f 的栈帧向上追溯,以找到最近的 未带属性 函数 outer 的栈帧,并返回 outer 中被追踪调用的 Location。
#![allow(unused)] fn main() { #[track_caller] fn f() { println!("{}", std::panic::Location::caller()); } }
注意
core提供了core::panic::Location::caller用于观察调用方位置。它封装了rustc实现的core::intrinsics::caller_location内建函数。
注意
因为结果
Location是一个提示,实现可能会提前停止向上追溯栈。有关重要注意事项,请参阅限制。
示例
当 f 直接由 calls_f 调用时,f 中的代码观察到其在 calls_f 中的调用点。
#![allow(unused)] fn main() { #[track_caller] fn f() { println!("{}", std::panic::Location::caller()); } fn calls_f() { f(); // <-- f() prints this location } }
当 f 由另一个带有属性的函数 g 调用,而 g 又由 calls_g 调用时,f 和 g 中的代码都观察到 g 在 calls_g 中的调用点。
#![allow(unused)] fn main() { #[track_caller] fn f() { println!("{}", std::panic::Location::caller()); } #[track_caller] fn g() { println!("{}", std::panic::Location::caller()); f(); } fn calls_g() { g(); // <-- g() prints this location twice, once itself and once from f() } }
当 g 由另一个带有属性的函数 h 调用,而 h 又由 calls_h 调用时,f、g 和 h 中的所有代码都观察到 h 在 calls_h 中的调用点。
#![allow(unused)] fn main() { #[track_caller] fn f() { println!("{}", std::panic::Location::caller()); } #[track_caller] fn g() { println!("{}", std::panic::Location::caller()); f(); } #[track_caller] fn h() { println!("{}", std::panic::Location::caller()); g(); } fn calls_h() { h(); // <-- prints this location three times, once itself, once from g(), once from f() } }
依此类推。
限制
此信息仅为提示,不要求实现保留它。
特别是,将带有 #[track_caller] 的函数强制转换为函数指针会创建一个垫片(shim),该垫片对于观察者来说看起来像是在带属性函数的定义处被调用,从而在虚拟调用中丢失了实际的调用方信息。这种强制转换的一个常见例子是创建其方法带有此属性的 trait 对象。
注意
上述针对函数指针的垫片是必需的,因为
rustc在代码生成上下文中通过向函数 ABI 追加一个隐式参数来实现track_caller,但这对于间接调用来说是不健全的,因为该参数不是函数类型的一部分,并且给定的函数指针类型可能引用带有或不带有该属性的函数。创建垫片将隐式参数对函数指针的调用者隐藏起来,从而保证了健全性。
instruction_set 属性
instruction_set 属性 可以应用于函数,以控制该函数将为其生成哪个指令集。
这允许在支持它的 CPU 架构上,在单个程序中混合使用多种指令集。
它使用 MetaListPath 语法,以及由架构家族名称和指令集名称组成的路径。
在不支持 instruction_set 属性的目标上使用它是编译错误。
在 ARM 平台
对于 ARMv4T 和 ARMv5te 架构,支持以下内容
arm::a32— 将函数生成为 A32 “ARM” 代码。arm::t32— 将函数生成为 T32 “Thumb” 代码。
#[instruction_set(arm::a32)]
fn foo_arm_code() {}
#[instruction_set(arm::t32)]
fn bar_thumb_code() {}
使用 instruction_set 属性具有以下效果
- 如果函数的地址被当作函数指针,地址的最低位将根据指令集设置为 0 (arm) 或 1 (thumb)。
- 函数中的任何内联汇编必须使用指定的指令集,而不是目标的默认指令集。