我正在使用Visual C ++ 2010和masm(“快速调用”调用约定)进行一些x64组装。

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

extern "C" void fillArray(unsigned char* byteArray, unsigned char value);


指向数组的指针将在RCX中,而char值将在DL中

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

请注意,我不是在尝试对编译器进行外编码,我只是在学习。

最佳答案

因为您将过程称为“ 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)


使用以下代码的最幼稚的变体似乎在两种情况下都表现最佳,并且代码大小也最小:

cld
mov al, 44h   ; byte value
mov edi, lpDst
mov ecx, 256000*4  ; buf size
rep stosb


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

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

Rep Stosd:

mov edi, lpDst
mov ecx, 256000
rep stosd


简单的时间:

mov edi, lpDst
mov ecx, 256000
.while ecx>0
    mov [edi],eax
    add edi,4
    dec ecx
.endw


不同的简单而:

mov edi, lpDst
xor ecx, ecx
.while ecx<256000
    mov [edi+ecx*4],eax
    inc ecx
.endw


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


SSE(NT,aligned,EDIT):

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


我将整个代码上传到了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)

08-15 23:14