Возврат __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. Я думаю, что если ваш ассемблерный код примет это соглашение о параметрах, он будет работать.
Пространство затененного стека не инициализируется фактическими значениями параметров; он предназначен для вызываемых, если им нужно место для хранения значений при использовании регистров. Вот почему вы получаете ошибку разыменования нулевого значения в вашем коде. Там несоответствие соглашения о вызовах. Отладчик знает о соглашении о вызовах, поэтому он может показать вам зарегистрированное значение параметра.