2014-12-09 20 views
9

Sto eseguendo il porting di una funzione da assembly inline a MASM in Visual Studio 2013 e sto avendo problemi a ottenere un valore di ritorno da esso.Restituzione di un __m128d dalla procedura MASM a un chiamante C

Ecco il chiamante C e la funzione di assemblaggio del prototipo:

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

E la funzione di montaggio:

.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 

quanto ho capito la convenzione per MASM, i valori sono normalmente restituiti attraverso il ritorno Registro EAX. Tuttavia, poiché sto cercando di restituire un valore a 128 bit, sto assumendo che un parametro out sia la strada da percorrere. Come si può vedere nella lista degli assiemi, l'assegnazione del parametro out (movaps [result]) causa una violazione di accesso (posizione di lettura della violazione di accesso 0x00000000). Ho convalidato l'indirizzo del risultato nel debugger e sembra a posto.

Cosa sto sbagliando?

+0

L'indirizzo è allineato correttamente? – Mehrdad

+0

È possibile modificare il chiamante per restituire invece un puntatore a __m128d? – mbomb007

+0

@ Mehrdad. Sì. '__m128d' è definito con __declspec per allinearlo correttamente e ho ricontrollato l'indirizzo nel debugger. – jaket

risposta

3

per scopi didattici, ho scritto una versione della funzione che utilizza intrinseche:

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

Poi ho compilato utilizzando il VC++ compilatore x64 utilizzando cl /c /O2 /FA absmax.cpp per generare un elenco di montaggio (a cura di rimuovere i commenti di linea):

; 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 

Osservando che x64 utilizza una convenzione __fastcall per impostazione predefinita e ombre i parametri nello stack, vedo che il parametro out viene infatti scritto indirettamente attraverso r8, che è il terzo parametro intero per il codice x64, per MSDN. Penso che se il tuo codice assembly adotta questa convenzione sui parametri, funzionerà.

Lo spazio di stack ombreggiato non viene inizializzato con i valori dei parametri effettivi; è destinato ai callee, se hanno bisogno di un posto dove riporre i valori mentre usano i registri. Ecco perché stai ricevendo un errore di deferenziazione a valore zero nel tuo codice. C'è una mancata corrispondenza nella convenzione di chiamata. Il debugger conosce la convenzione di chiamata, quindi può mostrarti il ​​valore registrato per il parametro.

+0

L'uso di intrinsics non è possibile per me in tutti i casi. Il codice emesso da VC++ può essere alquanto orribile in alcuni casi e l'assembly inline che sto cercando di eseguire è un loop interno altamente ottimizzato per l'elaborazione del segnale. Mi piace l'idea però di usare intrisincs almeno per modellare i miei prototipi di funzioni. Grazie. – jaket