通过编译器:
- GCC:
-Wa,-mbranches-within-32B-boundaries
- 叮当声(10+):
-mbranches-within-32B-boundaries
直接编译选项,而不是-Wa
.
- MSVC: /QIntel-jcc-erratum https://learn.microsoft.com/en-us/cpp/build/reference/qintel-jcc-erratum?view=msvc-170 See 英特尔 JCC 勘误表 - 用于缓解的前缀有什么影响? https://stackoverflow.com/questions/70216761/intel-jcc-erratum-what-is-the-effect-of-prefixes-used-for-mitigation
- ICC:TODO,查找文档。
GNU 工具链在汇编器中进行缓解,其中as -mbranches-within-32B-boundaries
,这使得 (GAS 手册:x86 选项 https://sourceware.org/binutils/docs/as/i386_002dOptions.html):
-
-malign-branch-boundary=32
(关心 32 字节边界)。除了手册说这个选项需要一个指数,而不是 2 的直接幂,所以可能它实际上是...boundary=5
.
-
-malign-branch=jcc+fused+jmp
(默认值是not包括任何+call+ret+indirect
)
-
-malign-branch-prefix-size=5
(每个 insn 最多 5 个段前缀)。
所以相关的 GCC 调用是 gcc -Wa,-mbranches-within-32B-boundaries
不幸的是,海湾合作委员会-mtune=skylake
不启用此功能。
GAS 的策略似乎是在最后一个对齐指令(例如.p2align
) 或在最后一个可以结束的 jcc/jmp 之后before32B 边界。我猜想最终可能会在内循环之前或之后在外循环中进行填充,也许可以帮助它们适应更少的 uop 缓存行? (Skylake 还禁用了 LSD 循环缓冲区,因此跨两个 uop 缓存行的微小循环每次迭代最多可以运行 2 个周期,而不是 1 个。)
它可能会导致相当大量的带有长宏融合跳转的填充,例如-fstack-protector-strong
最近海湾合作委员会使用sub rdx,QWORD PTR fs:0x28
/ jnz
(早期 GCC 曾使用xor
,即使在英特尔上也无法融合)。 sub + jnz 总共 11 个字节,因此在最坏的情况下可能需要 11 个字节的 CS 前缀才能将其移动到新的 32B 块的开头。显示其前面的 insn 中的 8 个 CS 前缀的示例:https://godbolt.org/z/n1dYGMdro https://godbolt.org/z/n1dYGMdro
GCC 不知道指令大小,它只打印文本。这就是为什么它需要 GAS 来支持诸如.p2align 4,,10
如果需要少于 10 个字节的填充,则按 16 对齐,以实现它想要使用的对齐试探法。 (通常后面跟着.p2align 3
无条件对齐8。)
as
还有其他默认情况下未启用的有趣选项,例如-Os
优化手写asm,例如mov $1, %rax
=> mov $1, %eax
/ xor %rax,%rax
=> %eax
/ test $1, %eax
=> al
甚至 EVEX => VEX 对于 vmovdqa64 => vmovdqa 之类的东西。
还有类似的东西-msse2avx
即使助记符不是,也始终使用 VEX 前缀v...
, and -momit-lock-prefix=yes
它可用于为单处理器系统构建 std::atomic 代码。
And -mfence-as-lock-add=yes
组装mfence
into lock addl $0x0, (%rsp)
。但疯狂的是,它也这样做了sfence
乃至lfence
,所以它在使用的代码中不可用lfence
作为执行障碍,这是主要用例lfence
。例如对于 retpolines 或计时,例如lfence;rdtsc
.
as
还具有 CPU 功能级别检查-march=znver3
例如,或者.arch
指令。和-mtune=CPU
,尽管我不知道那是做什么的。也许设定 NOP 策略?