如何将(最多)16 个单字节移动到 XMM 寄存器中?
How to move (up to) 16 single bytes into an XMM register?
我有一个置零的 128 位寄存器,我想左移并添加一个字节。我可以通过以下方式移动它:
pslldq xmm0, 1
...但现在我想将al复制到空的space中。类似于:
or xmm0, al
这当然行不通。我只想影响最低的 8 位。这将处于一个循环中,其中 al 的连续值将用于填充寄存器。所以我需要某种 mov 指令或其他替代方法。
理想的情况是一条指令左移 8 位并插入,但我认为不存在这种情况。
我花了很多时间在 x86-64 指令集数据中翻找,但找不到任何可以让我做我想做的事情。可以吗?
更新:我在尝试 pinsrb 后发现我的代码逻辑有错误。 pinsrb 会很棒,但不幸的是它只能使用立即索引,而不是寄存器。
我正在从不连续的位置获取字节,所以我想我需要一次一个字节地执行它。字节数可以是 1 到 16 之间的任何值。我抓取的第一个字节应该在 xmm0 的最低字节结束,下一个字节进入下一个最低字节等。
Intel's intrinsics guide 可用于查找向量指令。它列出了 asm 助记符和内在(您可以通过助记符而不是内在进行搜索,因为搜索匹配条目的整个文本)。
Intel的PDF参考手册也有索引。 insn set ref 手册是第 2 卷。请参阅 x86 标签 wiki 中英特尔手册的 links。
SSE4.1 PINSRB 可以完全按照您的要求进行操作,但是这会在 Haswell 及更高版本的每个时钟上出现一次洗牌瓶颈,无法实现每个时钟 2 个负载的吞吐量。 (每个 pinrsb xmm, [mem], imm8
2 微指令,其中一个用于端口 5,一个用于加载端口)。
您不需要将向量左移,因为整数 -> 带有合并指令的向量插入 (PINSR*) 为插入位置取一个索引。 (并且已经需要一个 shuffle uop,所以每次都使用相同的位置并移动向量对性能没有好处。)
对于这个问题:将16个字节单独插入到一个向量中并不是最有效的方法。在整数寄存器中将它们以 4 或 8 为一组进行组装可能是更好的方法。
;; b0 .. b15 are whatever addressing mode you want.
;; if you could get more than 1 of b0..b15 with a single vector load (i.e. there is some locality in the source bytes)
;; then DON'T DO THIS: do vector loads and shuffle + combine (pshufb if needed)
movzx eax, byte [b2] ; break the
mov ah, byte [b3]
shl eax, 16 ; partial-reg merge is pretty cheap on SnB/IvB, but very slow on Intel CPUs before Sandybridge. AMD has no penalty, just (true in this case) dependencies
mov al, byte [b0]
mov ah, byte [b1]
;; 5 uops to load + merge 4 bytes into an integer reg, plus 2x merging costs
movd xmm0, eax # cheaper than pinsrd xmm0, edx, 0. Also zeros the rest of the vector
;alternative strategy using an extra OR, probably not better anywhere: I don't think merging AL and AH is cheaper than merging just AH
;two short dep chains instead of one longer one isn't helpful when we're doing 16 bytes
movzx eax, byte [b4]
mov ah, byte [b5]
movzx edx, byte [b6]
mov dh, byte [b7]
shl edx, 16
or edx, eax
pinsrd xmm0, edx, 1
;; Then repeat for the next two dwords.
...
pinsrd xmm0, edx, 2
...
pinsrd xmm0, edx, 3
对于 movq
/ pinsrq
,您甚至可以继续使用最多 qwords 的整数 regs,但是 4 个独立的 dep 链和每个整数 reg 只有一个 shl
可能更好。
更新:AH 合并在 Haswell/Skylake 上不是免费的。合并 uop 甚至可能需要自己在一个周期内发布(即用完 4 个前端发布带宽槽)。参见
对于其他 uarches:。特别是在 AMD 和 Silvermont 上,partial-reg 写入依赖于 full reg。这正是我们在这里想要的吞吐量;没有额外的合并 uop。 (除 Intel P6 系列及其 Sandybridge 系列后代外,情况都是如此,部分寄存器重命名有时有帮助,但在这种情况下有害。)
如果您不能使用 SSE4,那么您可以使用 pinsrw (SSE2)。或者最好使用 movd
并将向量与 PUNPCKLDQ / PUNPCKLDQD 一起洗牌。 (link 是英特尔手册中的 HTML 摘录。
请参阅 Agner Fog's Optimizing Assembly guide(和说明 tables/microarch 指南)来确定什么样的指令序列实际上是好的。
我有一个置零的 128 位寄存器,我想左移并添加一个字节。我可以通过以下方式移动它:
pslldq xmm0, 1
...但现在我想将al复制到空的space中。类似于:
or xmm0, al
这当然行不通。我只想影响最低的 8 位。这将处于一个循环中,其中 al 的连续值将用于填充寄存器。所以我需要某种 mov 指令或其他替代方法。
理想的情况是一条指令左移 8 位并插入,但我认为不存在这种情况。
我花了很多时间在 x86-64 指令集数据中翻找,但找不到任何可以让我做我想做的事情。可以吗?
更新:我在尝试 pinsrb 后发现我的代码逻辑有错误。 pinsrb 会很棒,但不幸的是它只能使用立即索引,而不是寄存器。
我正在从不连续的位置获取字节,所以我想我需要一次一个字节地执行它。字节数可以是 1 到 16 之间的任何值。我抓取的第一个字节应该在 xmm0 的最低字节结束,下一个字节进入下一个最低字节等。
Intel's intrinsics guide 可用于查找向量指令。它列出了 asm 助记符和内在(您可以通过助记符而不是内在进行搜索,因为搜索匹配条目的整个文本)。
Intel的PDF参考手册也有索引。 insn set ref 手册是第 2 卷。请参阅 x86 标签 wiki 中英特尔手册的 links。
SSE4.1 PINSRB 可以完全按照您的要求进行操作,但是这会在 Haswell 及更高版本的每个时钟上出现一次洗牌瓶颈,无法实现每个时钟 2 个负载的吞吐量。 (每个 pinrsb xmm, [mem], imm8
2 微指令,其中一个用于端口 5,一个用于加载端口)。
您不需要将向量左移,因为整数 -> 带有合并指令的向量插入 (PINSR*) 为插入位置取一个索引。 (并且已经需要一个 shuffle uop,所以每次都使用相同的位置并移动向量对性能没有好处。)
对于这个问题:将16个字节单独插入到一个向量中并不是最有效的方法。在整数寄存器中将它们以 4 或 8 为一组进行组装可能是更好的方法。
;; b0 .. b15 are whatever addressing mode you want.
;; if you could get more than 1 of b0..b15 with a single vector load (i.e. there is some locality in the source bytes)
;; then DON'T DO THIS: do vector loads and shuffle + combine (pshufb if needed)
movzx eax, byte [b2] ; break the
mov ah, byte [b3]
shl eax, 16 ; partial-reg merge is pretty cheap on SnB/IvB, but very slow on Intel CPUs before Sandybridge. AMD has no penalty, just (true in this case) dependencies
mov al, byte [b0]
mov ah, byte [b1]
;; 5 uops to load + merge 4 bytes into an integer reg, plus 2x merging costs
movd xmm0, eax # cheaper than pinsrd xmm0, edx, 0. Also zeros the rest of the vector
;alternative strategy using an extra OR, probably not better anywhere: I don't think merging AL and AH is cheaper than merging just AH
;two short dep chains instead of one longer one isn't helpful when we're doing 16 bytes
movzx eax, byte [b4]
mov ah, byte [b5]
movzx edx, byte [b6]
mov dh, byte [b7]
shl edx, 16
or edx, eax
pinsrd xmm0, edx, 1
;; Then repeat for the next two dwords.
...
pinsrd xmm0, edx, 2
...
pinsrd xmm0, edx, 3
对于 movq
/ pinsrq
,您甚至可以继续使用最多 qwords 的整数 regs,但是 4 个独立的 dep 链和每个整数 reg 只有一个 shl
可能更好。
更新:AH 合并在 Haswell/Skylake 上不是免费的。合并 uop 甚至可能需要自己在一个周期内发布(即用完 4 个前端发布带宽槽)。参见
对于其他 uarches:
如果您不能使用 SSE4,那么您可以使用 pinsrw (SSE2)。或者最好使用 movd
并将向量与 PUNPCKLDQ / PUNPCKLDQD 一起洗牌。 (link 是英特尔手册中的 HTML 摘录。
请参阅 Agner Fog's Optimizing Assembly guide(和说明 tables/microarch 指南)来确定什么样的指令序列实际上是好的。