如何使用重复的字节值填充64位寄存器

Dzi*_*gas 5 64-bit x86 assembly masm

我正在使用Visual C++ 2010和masm('快速调用'调用约定)进行一些x64程序集.

所以我想说我在C++中有一个函数:

extern "C" void fillArray(unsigned char* byteArray, unsigned char value);
Run Code Online (Sandbox Code Playgroud)

指向数组的指针将位于RCX中,char值将位于DL中

如何使用DL为RAX填充值,这样如果我要mov qword ptr [RCX], RAX打印byteArray,所有值都将等于'char value'?

请注意,我不是要编写我的编译器代码,我只是在学习.

har*_*old 8

您可以通过0x0101010101010101乘以最低字节复制到所有其他字节(假设其余全部零开始),这是有点恼人,因为没有imul r64, r64, imm64,但你可以这样做:

mov  rax, 0x0101010101010101 
imul rax, rdx                 ; at least as fast as  mul rdx  on all CPUs
Run Code Online (Sandbox Code Playgroud)

如果rdx是不要求的形式的(换句话说,如果它具有设置一些额外的位),只需添加一个
movzx eax, dl在前面,并移动到恒定RDX或另一个寄存器中.(movzx edx,dl无法从Intel CPU上的mov-elimination中受益.)

如果您不喜欢代码大小(mov r64, imm64单独已经是10个字节),只需在数据段中保留该常量即可.

  • 对于q/a的未来读者:将一个字节广播到SSE寄存器可能是设置memset(aka fillArray)的更好选择.使用整数指令首先将它广播到32b寄存器(例如使用这个'imul`技巧),然后执行'movd`可能有意义,或者使用pshufb使用全零控制掩码(可以使用pxor有效生成)又名`_mm_setzero()`). (2认同)

zx4*_*485 5

因为你调用了你的过程'fillArray',所以我假设你想用一个字节值填充整个内存块.所以我对不同的方法进行了比较.它是32位masm代码,但结果在64位模式下应该类似.使用对齐和未对齐的缓冲区测试每种方法.结果如下:

Simple REP STOSB - aligned....: 192
Simple REP STOSB - not aligned: 192
Simple REP STOSD - aligned....: 191
Simple REP STOSD - not aligned: 222
Simple while loop - aligned....: 267
Simple while loop - not aligned: 261
Simple while loop with different addressing - aligned....: 271
Simple while loop with different addressing - not aligned: 262
Loop with 16-byte SSE write - aligned....: 192
Loop with 16-byte SSE write - not aligned: 205
Loop with 16-byte SSE write non-temporal hint - aligned....: 126 (EDIT)
Run Code Online (Sandbox Code Playgroud)

使用以下代码的最天真的变体似乎在两种情况下都表现最佳,并且具有最小的代码大小:

cld
mov al, 44h   ; byte value
mov edi, lpDst
mov ecx, 256000*4  ; buf size
rep stosb
Run Code Online (Sandbox Code Playgroud)

编辑:对齐数据并不是最快的.添加了性能最佳的MOVNTDQ版本,见下文.

为了完整起见,以下是其他例程的摘录 - 假设值在以下情况下扩展为EAX:

Rep Stosd:

mov edi, lpDst
mov ecx, 256000
rep stosd
Run Code Online (Sandbox Code Playgroud)

简单而:

mov edi, lpDst
mov ecx, 256000
.while ecx>0
    mov [edi],eax
    add edi,4
    dec ecx
.endw
Run Code Online (Sandbox Code Playgroud)

不同简单的同时:

mov edi, lpDst
xor ecx, ecx
.while ecx<256000 
    mov [edi+ecx*4],eax
    inc ecx
.endw
Run Code Online (Sandbox Code Playgroud)

SSE(两者):

movd xmm0,eax
punpckldq xmm0,xmm0    ; xxxxxxxxGGGGHHHH -> xxxxxxxxHHHHHHHH
punpcklqdq xmm0,xmm0   ; xxxxxxxxHHHHHHHH -> HHHHHHHHHHHHHHHH
mov ecx, 256000/4   ; 16 byte
mov edi, lpDst
.while ecx>0 
    movdqa xmmword ptr [edi],xmm0    ; movdqu for unaligned
    add edi,16
    dec ecx
.endw
Run Code Online (Sandbox Code Playgroud)

SSE(新台币,对齐,编辑):

movd xmm0,eax
punpckldq xmm0,xmm0    ; xxxxxxxxGGGGHHHH -> xxxxxxxxHHHHHHHH
punpcklqdq xmm0,xmm0   ; xxxxxxxxHHHHHHHH -> HHHHHHHHHHHHHHHH
mov ecx, 256000/4   ; 16 byte
mov edi, lpDst
.while ecx>0 
    movntdq xmmword ptr [edi],xmm0
    add edi,16
    dec ecx
.endw
Run Code Online (Sandbox Code Playgroud)

我在这里上传了整个代码http://pastie.org/9831404 ---来自hutch的MASM包需要组装.


如果SSSE3可用,您可以使用pshufb一个字节广播到寄存器的所有位置而不是一串punpck指令.

movd    xmm0, edx
xorps   xmm1,xmm1      ; xmm1 = 0
pshufb  xmm0, xmm1     ; xmm0 = _mm_set1_epi8(dl)
Run Code Online (Sandbox Code Playgroud)