将_M12过程中的__m128d返回给C调用者

时间:2014-12-09 19:18:57

标签: masm sse visual-c++-2013

我正在将一个函数从内联汇编移植到Visual Studio 2013中的MASM,并且无法从中获取返回值。

这是C调用者和汇编函数原型:

extern "C" void AbsMax(__m128d* samples, int len, __m128d* pResult);

__m128d AbsMax(__m128d* samples, int len)
{
    __m128d absMax = { 0, 0 };
    AbsMax(samples, len, &absMax);
    return absMax;
}

和汇编功能:

.686              ;Target processor.  Use instructions for Pentium class machines
.xmm

.model flat, c    ;Use the flat memory model. Use C calling conventions
.code             ;Indicates the start of a code segment.

AbsMax proc samples:PTR DWORD, len:DWORD, result:PTR XMMWORD
    ;; Load up registers. xmm0 is min, xmm1 is max. L is Ch0, H is Ch1.
    mov     ecx,  [len]
    shl     ecx,  4
    mov     esi,  [samples]
    lea     esi,  [esi+ecx]
    neg     ecx
    pxor    xmm0, xmm0
    pxor    xmm1, xmm1

ALIGN 16
_loop:
    movaps  xmm2, [esi+ecx]
    add     ecx,  16
    minpd   xmm0, xmm2
    maxpd   xmm1, xmm2
    jne     _loop

    ;; Store larger of -min and max for each channel. xmm2 is -min.
    pxor    xmm2, xmm2
    subpd   xmm2, xmm0
    maxpd   xmm1, xmm2
    movaps  [result], xmm1  ; <=== access violation here

    xor eax, eax
    xor ebx, ebx
    ret
AbsMax ENDP 
END 

据我了解MASM的约定,返回值通常通过EAX寄存器返回。但是,由于我试图返回一个128位的值,我假设输出参数是要走的路。正如您在汇编列表中所看到的,分配out参数(movaps [result])会导致访问冲突(访问冲突读取位置0x00000000)。我已经在调试器中验证了结果的地址,看起来很好。

我做错了什么?

1 个答案:

答案 0 :(得分:3)

出于教育目的,我编写了一个使用内在函数的函数版本:

#include <immintrin.h>

extern "C" void AbsMax(__m128d* samples, int len, __m128d* pResult)
{
    __m128d min = _mm_setzero_pd();
    __m128d max = _mm_setzero_pd();
    while (len--)
    {
        min = _mm_min_pd(min, *samples);
        max = _mm_max_pd(max, *samples);
        ++samples;
    }
    *pResult = _mm_max_pd(max, _mm_sub_pd(_mm_setzero_pd(), min));
}

然后我使用VC ++ x64编译器使用cl /c /O2 /FA absmax.cpp编译生成汇编列表(编辑以删除行注释):

; Listing generated by Microsoft (R) Optimizing Compiler Version 18.00.31101.0 
include listing.inc

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

PUBLIC  AbsMax
_TEXT   SEGMENT
samples$ = 8
len$ = 16
pResult$ = 24
AbsMax PROC                     ; COMDAT
    xorps   xmm3, xmm3
    movaps  xmm2, xmm3
    movaps  xmm1, xmm3
    test    edx, edx
    je  SHORT $LN6@AbsMax
    npad   3
$LL2@AbsMax:
    minpd   xmm2, XMMWORD PTR [rcx]
    maxpd   xmm1, XMMWORD PTR [rcx]
    lea rcx, QWORD PTR [rcx+16]
    dec edx
    jne SHORT $LL2@AbsMax
$LN6@AbsMax:
    subpd   xmm3, xmm2
    maxpd   xmm1, xmm3
    movaps  XMMWORD PTR [r8], xmm1
    ret 0
AbsMax  ENDP
_TEXT   ENDS
END

注意到默认情况下x64使用__fastcall约定,并隐藏堆栈上的参数,我看到out参数实际上是通过r8间接写入的,这是第三个整数参数对于x64代码,每MSDN。我想如果您的汇编代码采用这个参数约定,它将起作用。

阴影堆栈空间未使用实际参数值初始化;如果他们在使用寄存器时需要一个存放值的地方,它就是为callees设计的。这就是您在代码中获得零值解除引用错误的原因。调用约定不匹配。调试器知道调用约定,因此它可以显示参数的注册值。