You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在GAS汇编中对128位SSE寄存器执行PSLLDQ逻辑移位?

GAS汇编中PSLLDQ寄存器操作数报错的解决方法及替代方案

错误原因

pslldq指令的x86指令集设计仅支持立即数imm8(0-15)作为移位长度操作数,不接受寄存器或内存操作数,这就是你用%xmm10作为操作数时出现operand type mismatch错误的原因。


解决方法一:跳转表处理所有可能的移位值

因为128位SSE寄存器最多左移15字节(移位16字节等同于清零),可以为每个可能的移位值(0-15)编写对应的pslldq指令,通过跳转表根据寄存器中的值跳转到对应指令执行。

示例代码:

# 假设移位长度存储在%rax中(需确保值在0-15范围内)
cmpq    $15, %rax
ja      invalid_shift         # 处理超出0-15的非法移位值

# 构建跳转表,每个条目对应一个移位值的指令地址
lea     shift_jump_table(%rip), %rcx
jmp     *(%rcx, %rax, 8)      # 根据%rax的值跳转到对应指令

# 跳转表定义
shift_jump_table:
.quad   shift_by_0
.quad   shift_by_1
.quad   shift_by_2
.quad   shift_by_3
.quad   shift_by_4
.quad   shift_by_5
.quad   shift_by_6
.quad   shift_by_7
.quad   shift_by_8
.quad   shift_by_9
.quad   shift_by_10
.quad   shift_by_11
.quad   shift_by_12
.quad   shift_by_13
.quad   shift_by_14
.quad   shift_by_15

# 各移位值对应的指令
shift_by_0:
pslldq  $0, %xmm14
jmp     shift_done
shift_by_1:
pslldq  $1, %xmm14
jmp     shift_done
shift_by_2:
pslldq  $2, %xmm14
jmp     shift_done
shift_by_3:
pslldq  $3, %xmm14
jmp     shift_done
shift_by_4:
pslldq  $4, %xmm14
jmp     shift_done
shift_by_5:
pslldq  $5, %xmm14
jmp     shift_done
shift_by_6:
pslldq  $6, %xmm14
jmp     shift_done
shift_by_7:
pslldq  $7, %xmm14
jmp     shift_done
shift_by_8:
pslldq  $8, %xmm14
jmp     shift_done
shift_by_9:
pslldq  $9, %xmm14
jmp     shift_done
shift_by_10:
pslldq  $10, %xmm14
jmp     shift_done
shift_by_11:
pslldq  $11, %xmm14
jmp     shift_done
shift_by_12:
pslldq  $12, %xmm14
jmp     shift_done
shift_by_13:
pslldq  $13, %xmm14
jmp     shift_done
shift_by_14:
pslldq  $14, %xmm14
jmp     shift_done
shift_by_15:
pslldq  $15, %xmm14

shift_done:
# 移位完成后的后续代码
invalid_shift:
# 处理非法移位值的逻辑(如报错、退出等)

解决方法二:用PSHUFB指令实现任意字节左移(更高效)

pshufb(字节乱序)指令支持用XMM寄存器作为控制掩码,可灵活实现任意字节的左移效果,无需跳转,效率更高。

掩码规则

pshufb的掩码寄存器每个字节对应目标寄存器的一个字节位置:

  • 若掩码字节最高位为1,目标位置填充0
  • 若最高位为0,目标位置取源寄存器中掩码字节低4位指定索引的字节

对于左移N字节的需求,掩码构造规则:

  • 前N个字节的掩码值为0x80(填充0)
  • 从第N个字节开始,每个位置的掩码值为当前位置索引 - N(即取源寄存器中靠前的对应字节)

示例代码(假设移位长度N在%al中,范围0-15):

# 1. 将N广播到XMM寄存器的所有字节
pxor    %xmm10, %xmm10
pinsrb  $0, %al, %xmm10       # %xmm10[0] = N,其余字节为0
pshufb  %xmm10, %xmm10        # 广播N到%xmm10的所有16个字节

# 2. 生成0-15的索引序列(每个字节对应一个位置索引)
movq    $0x0706050403020100, %rbx
movq    $0x0f0e0d0c0b0a0908, %rcx
movq    %rbx, %xmm11
movq    %rcx, %xmm12
punpcklqdq %xmm12, %xmm11     # %xmm11 = [0,1,2,...,15](每个字节为对应索引)

# 3. 计算索引与N的差值:索引 - N
movdqa  %xmm11, %xmm13        # 保存原索引用于后续比较
psubb   %xmm10, %xmm11        # %xmm11 = 索引 - N

# 4. 生成全0x80的寄存器(用于填充左移后的空位置)
movq    $0x8080808080808080, %rbx
movq    %rbx, %xmm12
movq    %rbx, %xmm14
punpcklqdq %xmm14, %xmm12     # %xmm12 = 16个0x80字节

# 5. 生成选择掩码:区分索引 >= N和索引 < N的位置
pcmpgtb %xmm10, %xmm13        # %xmm13:索引 > N → 0xff,索引 <= N → 0x00(GAS语法:src在前,dest在后)
pxor    %xmm15, %xmm15
pcmpeqb %xmm15, %xmm15        # %xmm15 = 0xff...ff
pxor    %xmm13, %xmm15        # %xmm15:索引 < N → 0xff,索引 >= N → 0x00(取反%xmm13)

# 6. 合并得到最终的PSHUFB掩码
pand    %xmm15, %xmm12        # 保留索引 < N位置的0x80,其余置0
pandn   %xmm15, %xmm11        # 保留索引 >= N位置的(索引-N),其余置0
por     %xmm12, %xmm11        # %xmm11即为最终掩码

# 7. 执行PSHUFB实现左移N字节
movdqa  %xmm14, %xmm16        # 保存原数据(避免覆盖)
pshufb  %xmm11, %xmm16        # %xmm16就是左移N字节后的结果
movdqa  %xmm16, %xmm14        # 将结果写回%xmm14

内容的提问来源于stack exchange,提问作者RajibTheKing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 13:56:05