如何“删除” SSE 寄存器末尾的字节?

Posted

技术标签:

【中文标题】如何“删除” SSE 寄存器末尾的字节?【英文标题】:How to "remove" bytes at the end of a SSE register? 【发布时间】:2013-03-31 21:41:10 【问题描述】:

对于 uni 赋值,我需要编写一个函数来计算汇编中字符串(由指针和索引定义)中的空格数。为此需要使用pcmpeqb(即使用SSE 寄存器),并提示使用popcntpmovmskb。我的基本方法是处理 16 字节块中的字符串,将每个块加载到 %xmm8 并将其与初始化为包含 16 个空格的 %xmm9 进行比较。但是,我需要以某种方式专门处理最后一个块。

我的第一个想法是使用旋转指令删除字符串末尾的垃圾。 (保证字符串在结束后分配了一些额外的空间以防止段错误,但那里的数据可能不应该用于比较。)我偶然发现了PSRLDQ,但它似乎不接受非立即争论。 (或者至少拒绝了我扔给它的东西。)所以我的问题是:我怎样才能删除 SSE 寄存器的最后 X 个字节,而不将它的一半归零,或者逐个字地这样做? (据我了解,大多数可用的操作都可以。)

我的代码(模样板)目前看起来像这样 - 有问题的部分接近尾声,在标签 _last: 之后:

    # === Arguments ===
    # %rdi - char *input
    # %rsi - size_t count
    # === Temporaries ===
    # %rdx - how many chars to process in final run
    # %rcx - how many characters were "read" already
    # %r8 - pop count of last iteration
    # %r9
    # %r11
    # === SSE Temporaries ===
    # %xmm8 - the chunk of the string being processed
    # %xmm9 - 16 spaces

    xor %rcx, %rcx
    xor %rax, %rax
    movdqu _spaces(%rip), %xmm9

_loop:
    # set %rdx to number of characters left to process
    mov %rsi, %rdx
    sub %rcx, %rdx

    # we've reached the end of the string
    cmp %rdx, %rsi
    jge _end

    movdqu (%rdi, %rcx), %xmm8 # load chunk of string to process
    add $16, %rcx

    # less than 16 characters to process
    cmp $16, %rdx
    jg _last

_compare: #compare %xmm8 with spaces and add count of spaces to %eax
    pcmpeqb %xmm9, %xmm8
    pmovmskb %xmm8, %r8d
    popcntl %r8d, %r8d
    add %r8d, %eax
    jmp _loop

_last: # last part of string, less than 16 chars
    sub $16, %rdx
    neg %rdx
    # I need to delete possible garbage after the last chars
    psrldq %edx, %xmm8 
    jmp _compare

_end:
    ret

(那里的控制流可能仍然存在问题,但我稍后会处理。)

【问题讨论】:

【参考方案1】:

不要费心尝试“删除” SSE 寄存器中的额外字节。相反,在您比较并执行 PMOVMSKB 之后,只需屏蔽掉生成的掩码中与额外字节相对应的位。这是矢量化中非常标准的方法;而不是为了得到你想要的数据而跳槽,处理所有事情,然后清理你以后不需要的位。

【讨论】:

【参考方案2】:

我认为最简单的解决方案是使用 SSE 寄存器中的所有 16 个字符,但屏蔽掉 pmovmskb 之后的位。请注意,像您一样使用 16 字节加载是不安全的,因为您可能会进入无法访问的页面。

【讨论】:

感谢您的警告,但分配声明我可以依赖在字符串参数中的最后一个字符之后可以访问的至少 16 个字节。睡一觉后我会试试你的建议,似乎需要重新安排一下控制流。 如何进行安全加载? 例如,对齐地址,以确保不会跨越页面边界。

以上是关于如何“删除” SSE 寄存器末尾的字节?的主要内容,如果未能解决你的问题,请参考以下文章

使用 x64 SSE / AVX 寄存器进行字符串反转

SSE XMM 点积说明

SSE/SSE2 指令的打包和解包数据?

SSE 向量重新对齐?

OpenCV 中的 Mat 矩阵和 SSE 的 16 字节对齐

在子字节上使用 SSE 进行位操作?