- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有以下 Intel PCLMULQDQ intrinsic (无携带乘法):
__m128i a, b; // Set to some value
__m128i r = _mm_clmulepi64_si128(a, b, 0x10);
0x10
告诉我乘法是:
r = a[63:0] * b[127:64]
poly64_t a, b; // Set to some value
poly16x8_t = vmull_p64(...) or vmull_high_p64(...);
vmull_p64
适用于低 64 位,而
vmull_high_p64
对高 64 位进行操作。我想我需要将值之一移动 128 位值来模仿
_mm_clmulepi64_si128(a, b, 0x10)
.
PMULL, PMULL2 (vector) 的文档不太清楚,我不确定结果会是什么,因为我不明白 2 的排列说明符。
ARM ACLE 2.0也不是很有帮助:
poly128_t vmull_p64 (poly64_t, poly64_t);
Performs widening polynomial multiplication on double-words low part. Available on ARMv8 AArch32 and AArch64.
poly128_t vmull_high_p64 (poly64x2_t, poly64x2_t);
Performs widening polynomial multiplication on double-words high part. Available on ARMv8 AArch32 and AArch64.
_mm_clmulepi64_si128
至
vmull_{high}_p64
?
最佳答案
由于您通过评论澄清了混淆的来源:
一个完整的乘法产生的结果是输入宽度的两倍。 add 最多可以产生一个进位位,但 mul 产生整个上半部分。
乘法完全等同于移位 + 加法,这些移位使一个操作数中的位高达 2N - 1(当输入为 N 位宽时)。见 Wikipedia's example .
在正常的整数乘法中(在加法步骤中带有进位),如 x86's mul
instruction , 部分和的进位可以设置高位,因此结果正好是两倍宽。
XOR 是没有进位的加法,因此无进位乘法是相同的移位加法算法,但使用 XOR 而不是加进位。在无进位乘法中,没有进位,因此全角结果的最高位始终为零。英特尔甚至在 pclmuludq
的 x86 insn 引用手册的操作部分明确说明了这一点。 :DEST[127] ← 0;
.该部分精确地记录了产生结果的所有移位和异或。PMULL[2]
文档对我来说似乎很清楚。目的地必须是 .8H
vector (这意味着八个 16 位(半字)元素)。 PMULL
的来源必须是.8B
vector (8 个一字节元素),而 PMULL2
的来源必须是.16B
(16 个单字节元素,其中仅使用每个源的高 8 个)。
如果这是 ARM32 NEON,其中每个 16B vector 寄存器的上半部分是一个奇数的窄寄存器,PMULL2
对任何事情都没有用。
但是,没有“操作”部分来准确描述哪些位与哪些其他位相乘。幸运的是, paper linked in comments很好地总结了可用的说明 适用于 ARMv7 和 ARMv8 32 位和 64 位。 .8B/.8H 组织说明符似乎是假的,因为 PMULL
确实像 SSE 一样执行单个 64x64 -> 128 无携带 mul pclmul操作说明。 ARMv7 VMULL.P8
NEON insn 确实做了一个打包的 8x8->16,但明确表示 PMULL
(和 ARMv8 AArch32 VMULL.P8
)是不同的。
ARM 文档没有说任何这些太糟糕了;它似乎非常缺乏,尤其是。重新误导 .8B
vector 组织的东西。那篇论文展示了一个使用预期 .1q
的例子。和 .1d
(和 .2d
)组织,所以也许汇编器并不关心你认为你的数据意味着什么,只要它的大小合适。
要进行高低相乘,您需要移动其中一个。
例如,如果您需要所有四种组合 (a0*b0, a1*b0, a0*b1, a1*b1),就像你构建一个 128x128 -> 128 乘以 64x64 -> 128 乘法(使用 Karatsuba)一样,你可以这样做:
pmull a0b0.8H, a.8B, b.8B
pmull2 a1b1.8H, a.16B, b.16B
swap a's top and bottom half, which I assume can be done efficiently somehow
pmull a1b0.8H, swapped_a.8B, b.8B
pmull2 a0b1.8H, swapped_a.16B, b.16B
1: pmull a0b0.1q, a.1d, b.1d
2: pmull2 a1b1.1q, a.2d, b.2d
3: ext.16b swapped_b, b, b, #8
4: pmull a0b1.1q, a.1d, swapped_b.1d
5: pmull2 a1b0.1q, a.2d, swapped_b.2d
6: eor.16b xor_cross_muls, a0b1, a1b0
7: ext.16b cross_low, zero, xor_cross_muls, #8
8: eor.16b result_low, a0b0, cross_low
9: ext.16b cross_high, xor_cross_muls, zero, #8
10: eor.16b result_high, a1b1, cross_high
关于将 _mm_clmulepi64_si128 转换为 vmull_{high}_p64,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38553881/
我是一名优秀的程序员,十分优秀!