PCMPESTRM指令在显式长度为1时失效的原因及实现排查
PCMPESTRM指令处理短字符串失效问题分析
问题描述
需求:在汇编语言中检查长度1-15的字符串,确认所有字符处于'A'-'Z'(0x41-0x5A)范围,采用PCMPESTRM指令实现,GAS代码如下:
movq $0x5A41, %r12 # A: 0x41, Z: 0x5A --> defining Range movq %r12, %xmm12 # Storing the result to SSE register movq %rdx, %rax # rdx: length of the string, storing to rax pcmpestrm $0x44, %xmm10, %xmm12 # %xmm10: actual string
现象:字符串长度2-15时误以为可通过%xmm0查看结果,但长度为1时%xmm0值无变化。
1. 长度为1时指令失效的原因
你使用的立即数0x44(二进制01000100)完全不符合RANGE_COMPARE(范围比较)的需求,具体拆解:
- 结果格式位(bits7-6):01 → 结果会存入EAX(第一个匹配元素的索引),而非XMM0。之前长度2-15时看到XMM0有变化,只是寄存器残留值的巧合,并非指令输出的有效结果。
- 比较模式位(bits5-4):00 → 实际启用的是
EQUAL_EACH模式(逐16位元素相等比较),而非你需要的RANGE_COMPARE模式(需将位5-4设为10)。 - SRC1长度位(bits3-2):01 → 指令将SRC1(%xmm10中的字符串)视为16位元素序列,而非字节序列。当字符串长度为1时,仅1个字节无法构成完整的16位元素,指令无法触发有效比较逻辑,自然不会生成结果。
长度≥2时的“正常运行”也是假象:此时指令是在比较16位元素是否等于%xmm12中的0x5A41,而非检查字节是否在A-Z范围,只是测试字符串刚好符合这个16位值才让你产生误解。
2. 实现中的核心疏漏
- 立即数配置完全错误:未正确设置PCMPESTRM的比较模式、结果格式和操作数长度。符合需求的立即数应满足:
- 结果存入XMM0:bits7-6设为
10(对应值0x80) - 启用RANGE_COMPARE模式:bits5-4设为
10(对应值0x20) - SRC1按字节处理:bits3-2设为
00(8位长度) - SRC2按字节处理:bits1-0设为
00(8位长度)
组合后正确的立即数是0xA0(二进制10100000),而非0x44。
- 结果存入XMM0:bits7-6设为
- SRC2寄存器初始化错误:RANGE_COMPARE模式下,SRC2需要为SRC1的每个字节提供一对范围值(下限、上限)。你仅加载了2字节的
0x5A41,只能处理1个字符;长度>1时,后续字符会使用%xmm12中未初始化的字节作为范围,属于未定义行为。 - 对指令逻辑理解偏差:误将EQUAL_EACH模式的结果当成RANGE_COMPARE的输出,混淆了操作数长度和比较模式的配置规则。
修复后的示例代码
# 初始化SRC2:为每个字节准备A-Z的范围对(重复0x41,0x5A填满整个XMM12) pxor %xmm12, %xmm12 # 清零寄存器 movq $0x5A415A415A415A41, %r12 # 8字节的重复范围对(0x41=下限A,0x5A=上限Z) movq %r12, %xmm12 # 加载低8字节 pshufd $0x00, %xmm12, %xmm12 # 将低8字节复制到高8字节,填满128位 movq %rdx, %rax # RAX = 字符串字节长度(1-15) # 立即数0xA0:结果存XMM0,RANGE_COMPARE模式,SRC1/SRC2均按字节处理 pcmpestrm $0xA0, %xmm10, %xmm12
修复后,%xmm0的低15位会对应每个字符的检查结果:某一位为1表示对应字符在A-Z范围内,为0则不在。
内容的提问来源于stack exchange,提问作者RajibTheKing
相关产品推荐
相关产品推荐

