【发布时间】:2020-07-30 01:41:42
【问题描述】:
如果我有一个受Intel jcc erratum 约束的芯片,我如何在 gcc 中启用缓解(调整分支位置以避免有问题的对齐),以及哪些 gcc 版本支持它?
【问题讨论】:
标签: gcc x86 intel gnu-assembler compiler-flags
如果我有一个受Intel jcc erratum 约束的芯片,我如何在 gcc 中启用缓解(调整分支位置以避免有问题的对齐),以及哪些 gcc 版本支持它?
【问题讨论】:
标签: gcc x86 intel gnu-assembler compiler-flags
通过编译器:
-Wa,-mbranches-within-32B-boundaries
-mbranches-within-32B-boundaries 直接编译选项,而不是-Wa。/QIntel-jcc-erratum 见Intel JCC Erratum - what is the effect of prefixes used for mitigation?
GNU 工具链在汇编器中使用as -mbranches-within-32B-boundaries 进行缓解,从而启用 (GAS manual: x86 options):
-malign-branch-boundary=32(关心 32 字节边界)。除了手册说这个选项需要一个指数,而不是 2 的直接幂,所以它实际上可能是...boundary=5。-malign-branch=jcc+fused+jmp(默认不包括任何+call+ret+indirect)-malign-branch-prefix-size=5(每个 insn 最多 5 个段前缀)。所以相关的 GCC 调用是 gcc -Wa,-mbranches-within-32B-boundaries
不幸的是,GCC -mtune=skylake 没有启用此功能。
GAS 的策略似乎是在最后一个对齐指令(例如.p2align)之后或在可以结束之前 32B 边界的最后一个 jcc/jmp 之后尽早填充。我想这最终可能会在外部循环中填充,在内部循环之前或之后,也许可以帮助它们适应更少的 uop 缓存行? (Skylake 还禁用了其 LSD 循环缓冲区,因此在两个 uop 缓存行之间拆分的微小循环每次迭代最多可以运行 2 个周期,而不是 1 个。)
它会导致大量的填充和长的宏融合跳转,例如-fstack-protector-strong,在最近的 GCC 使用 sub rdx,QWORD PTR fs:0x28 / jnz(早期的 GCC 曾经使用 xor,它可以'即使在英特尔上也不会融合)。 sub + jnz 总共有 11 个字节,因此在最坏的情况下可能需要 11 个字节的 CS 前缀才能将其转移到新的 32B 块的开头。示例在它之前的 insns 中显示 8 个 CS 前缀:https://godbolt.org/z/n1dYGMdro
GCC 不知道指令大小,它只打印文本。这就是为什么它需要 GAS 支持像 .p2align 4,,10 这样的东西来对齐 16,如果这将需要少于 10 个字节的填充,以实现它想要使用的对齐启发式。 (通常后跟 .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这样的东西,即使助记符不是v...,也总是使用VEX前缀,-mfence-as-lock-add=yes将mfence组装成lock addl $0x0, (%rsp),甚至-momit-lock-prefix=yes可以用来构建std ::单处理器系统的原子代码。
as 还具有 CPU 功能级别检查,例如 -march=znver3 或 .arch 指令。还有-mtune=CPU,尽管 IDK 是做什么的。或许设置 NOP 策略?
【讨论】: