SHA256: ARM vs. RISC-V
Table of Contents
1. SHA256: ARM vs. RISC-V
1.1. stack spilling
commit e919fae01b89fa6d7fc742d140bb15dc2600becb
gcc 本意是通过 fmv 指令做 register spilling, 由于 fmv 的 cost 设置的较小, gcc 生成了大量的 register spilling, 但最终可能是因为 gcc 有什么 bug, 并没有生成 fmv 指令.
这笔提交增大了 fmv 的 cost, 间接减少了 register spilling, 指令减少了 ~1700 条 (4400 -> 2700)
1.2. arm shifting bits
https://godbolt.org/z/h6rEc179r
SHA256_STEP 函数的指令对比:
arm64 (21 insns) | riscv64 w/ b_ext (30 insns) |
---|---|
ror w1, w3, 11 | roriw a6,a3,11 |
eor w1, w1, w3, ror 6 | roriw a5,a3,6 |
xor a5,a5,a6 | |
sext.w a5,a5 | |
eor w1, w1, w3, ror 25 | li a6,7 |
rolw a6,a3,a6 | |
xor a5,a5,a6 | |
add w1, w1, w7 | addw a5,a5,a7 |
eor w6, w8, w4 | xor a6,t1,a2 |
and w6, w6, w3 | and a6,a6,a3 |
eor w6, w6, w4 | xor a6,a2,a6 |
add w1, w1, w6 | addw a5,a5,a6 |
add w0, w1, w0 | addw a0,a5,a0 |
mov w1, 12184 | li a5,1116352512 |
movk w1, 0x428a, lsl 16 | addiw a5,a5,-104 |
add w0, w0, w1 | addw a0,a0,a5 |
ror w1, w2, 13 | roriw a5,a4,2 |
eor w1, w1, w2, ror 2 | roriw a6,a4,13 |
xor a5,a5,a6 | |
sext.w a5,a5 | |
eor w1, w1, w2, ror 22 | li a6,10 |
rolw a6,a4,a6 | |
xor a5,a5,a6 | |
and w6, w2, w5 | and a6,a4,a1 |
orr w11, w2, w5 | or t5,a4,a1 |
and w11, w11, w9 | and t5,t5,t4 |
orr w6, w6, w11 | or a6,a6,t5 |
add w1, w1, w6 | addw a5,a5,a6 |
add w1, w0, w1 | addw a5,a0,a5 |
arm 与 riscv 主要的区别在于 a ^ ror(b)
这个操作:
arm 的 xor 支持 shifting bits (例如 ` eor …., ror 2
`), 可以一次完成 ror +
xor, riscv 则需要先 ror 再 xor
总的来说一条 arm64 带 shifting bits 的 eor 指令相当于 2+ 条 riscv 指令, 在完整测试中, 这类指令有 450 占总条数(2729-1917=800) 的 56%
剩下的 44% 里 arm 的 ldp/stp 还能贡献 30 条 (一条 arm ldp/stp 相当于两个 riscv ld/sd 指令)
1.3. rolw
在 https://godbolt.org/z/j3nGK8q1c 使用 `-O2` 测试完整代码时与上面的简单测试有些不同, 例如:
- `sext.w` 可以被优化掉
- rolw 使用的 `li a6` 并不会每次都生成, 而是使用某个固定的寄存器, 且只在函数开头 `li` 一次即可, 但这显然增加了寄存器分配的压力, 导致了更多的 register spilling.
commit 0ccf520d349a82dafca0deb3d307a1080e8589a
这笔提交用 roriw 代替 rolw, 降低了 RA 的压力, 最终减少了 100+ 条指令
1.4. sched-pressure
commit a047513c9222f14adc6e5a015e038b207bb9a653
这笔提交默认打开了 `-fsched-pressure`, 降低了 RA 的压力, 最张减少了 100+ 条指令.
最终指令数为 2400+ 条, 和 arm 基本相当(除了 shifting bits, ldp 等已知问题导致的 500+ 条指令差异)