Возврат __m128d из процедуры MASM вызывающей стороне C

Я портирую функцию из встроенной сборки в MASM в Visual Studio 2013, и у меня возникают проблемы с получением возвращаемого значения.

Вот вызывающая сторона 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 ответ

Решение

В образовательных целях я написал версию вашей функции, которая использует встроенные функции:

#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. Я думаю, что если ваш ассемблерный код примет это соглашение о параметрах, он будет работать.

Пространство затененного стека не инициализируется фактическими значениями параметров; он предназначен для вызываемых, если им нужно место для хранения значений при использовании регистров. Вот почему вы получаете ошибку разыменования нулевого значения в вашем коде. Там несоответствие соглашения о вызовах. Отладчик знает о соглашении о вызовах, поэтому он может показать вам зарегистрированное значение параметра.

Другие вопросы по тегам