2014-12-09 11 views
9

Ich portiere eine Funktion von Inline-Assembly zu MASM in Visual Studio 2013 und habe Probleme beim Abrufen eines Rückgabewerts. HierZurückgeben einer __m128d aus MASM-Prozedur zu einem C-Aufrufer

ist der C Anrufer und der Montag Funktionsprototyp:

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

und die Montage-Funktion:

.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 

Als ich die Konvention für MASM verstehen, Werte zurückgeben werden in der Regel durch die zurückgeführt EAX registrieren. Da ich versuche, einen 128-Bit-Wert zurückzugeben, nehme ich an, dass ein out-Parameter der richtige Weg ist. Wie Sie im Assembly-Listing sehen können, verursacht das Zuweisen des out-Parameters (movaps [result]) eine Zugriffsverletzung (Access violation reading location 0x00000000). Ich habe die Adresse des Ergebnisses im Debugger überprüft und es sieht gut aus.

Was mache ich falsch?

+0

Ist die Adresse richtig ausgerichtet? – Mehrdad

+0

Ist es möglich, dass Sie den Aufrufer so ändern können, dass stattdessen ein Zeiger auf __m128d zurückgegeben wird? – mbomb007

+0

@Mehrdad. Ja, so ist es. '__m128d' ist mit einer __declspec definiert, um es richtig auszurichten und ich habe die Adresse im Debugger überprüft. – jaket

Antwort

3

Für pädagogische Zwecke, schrieb ich eine Version Ihrer Funktion auf die Spezifika verwendet:

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

Dann kompiliert ich die x64-Compiler VC++ mit cl /c /O2 /FA absmax.cpp mit einem Assembler-Liste zu generieren (bearbeitete Zeile Kommentare zu entfernen):

; 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 

Anbetracht dessen, dass ein x64 __fastcall convention standardmäßig verwendet, um die Parameter und Schatten auf dem Stapel, sehe ich, dass die out-Parameter in der Tat indirekt übergeschrieben, der der dritte Ganzzahlparameter für x64-Code ist, per MSDN. Ich denke, wenn Ihr Assemblercode diese Parameterkonvention annimmt, wird es funktionieren.

Der schattierte Stapelspeicherbereich wird nicht mit den tatsächlichen Parameterwerten initialisiert. Es ist für Callees gedacht, wenn sie einen Platz brauchen, um die Werte zu speichern, während sie die Register benutzen. Aus diesem Grund erhalten Sie in Ihrem Code einen Nullwert-Dereferenzierungsfehler. Es gibt einen Konflikt bei der Aufrufkonvention. Der Debugger kennt die Aufrufkonvention, sodass er den registrierten Wert für den Parameter anzeigen kann.

+0

Verwenden von intrinsics ist nicht für mich in allen Fällen möglich. Der von VC++ ausgegebene Code kann in einigen Fällen ziemlich entsetzlich sein, und die Inline-Assembly, die ich portieren möchte, ist hoch optimierte innere Schleifen für die Signalverarbeitung. Mir gefällt die Idee, Intrins zu verwenden, um zumindest meine Funktionsprototypen zu modellieren. Vielen Dank. – jaket