2014-12-09 10 views
9

Przesyłam funkcję z wbudowanego zestawu do MASM w programie Visual Studio 2013 i mam problem z uzyskaniem z niej wartości zwracanej.Zwracanie procedury __m128d z MASM do wywołującego C

Oto rozmówcy C i prototyp funkcji montaż:

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; 
} 

a funkcja montaż:

.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 

Jak rozumiem Konwencji o MASM, zwracane wartości są zwykle zwrócone na zewnątrz przez Rejestracja EAX. Jednakże, ponieważ próbuję zwrócić 128-bitową wartość, zakładam, że parametr wyjściowy jest do zrobienia. Jak widać na liście złożenia, przypisanie parametru out (movaps [result]) powoduje naruszenie zasad dostępu (Lokalizacja odczytu naruszenia dostępu 0x00000000). Sprawdziłem adres wyniku w debugerze i wygląda dobrze.

Co robię źle?

+0

Czy adres jest odpowiednio wyrównany? – Mehrdad

+0

Czy to możliwe, że można zmodyfikować wywołującego, aby zamiast tego zwrócił wskaźnik do __m128d? – mbomb007

+0

@Mehrdad. Tak to jest. '__m128d' jest zdefiniowany przez __declspec, aby właściwie go wyrównać i dwukrotnie sprawdziłem adres w debugerze. – jaket

Odpowiedz

3

Dla celów edukacyjnych, napisałem nawet wersję swojej funkcji, która używa intrinsics:

#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)); 
} 

Potem skompilowany przy użyciu VC++ x64 kompilator używając cl /c /O2 /FA absmax.cpp wygenerować listę montażową (edytowane do usuwania komentarzy linii):

; 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 [email protected] 
    npad 3 
[email protected]: 
    minpd xmm2, XMMWORD PTR [rcx] 
    maxpd xmm1, XMMWORD PTR [rcx] 
    lea rcx, QWORD PTR [rcx+16] 
    dec edx 
    jne SHORT [email protected] 
[email protected]: 
    subpd xmm3, xmm2 
    maxpd xmm1, xmm3 
    movaps XMMWORD PTR [r8], xmm1 
    ret 0 
AbsMax ENDP 
_TEXT ENDS 
END 

Stwierdzając, że 64 wykorzystuje __fastcall konwencję domyślnie i cienie parametry na stosie, widzę, że parametr out jest w rzeczywistości napisane jest pośrednio poprzez r8, który jest trzecim całkowitym parametrem dla kodu x64, za MSDN. Myślę, że jeśli twój kod zespołu przyjmuje tę konwencję parametru, zadziała.

Zacienione stosy nie są inicjowane rzeczywistymi wartościami parametrów; jest przeznaczony dla osób odwołujących się, jeśli potrzebują miejsca do przechowywania wartości podczas korzystania z rejestrów. Właśnie dlatego w kodzie pojawia się błąd dereferencji o wartości zerowej. Istnieje niezgodność konwencji wywoływania. Debugger wie o konwencji wywołującej, dzięki czemu może pokazać ci zarejestrowaną wartość dla parametru.

+0

Korzystanie z właściwości wewnętrznych jest dla mnie niemożliwe we wszystkich przypadkach. Kod emitowany przez VC++ przynajmniej może być dość przerażający w niektórych przypadkach, a wbudowany zestaw, który próbuję przenieść, to wysoce zoptymalizowane wewnętrzne pętle do przetwarzania sygnałów. Podoba mi się pomysł wykorzystania intrisincs do przynajmniej modelowania prototypów funkcji. Dzięki. – jaket

Powiązane problemy