如何在Visual Studio的内联asm中获取变量的地址

时间:2014-10-14 14:45:58

标签: variables visual-studio-2012 assembly sse inline-assembly

我在Visual Studio环境中学习内联汇编程序。 所以,我正在实现一个简单的点积函数,我似乎无法找到一个返回浮点结果的正确方法。

float dot(vec3 &a,vec3 &b)
{
    float result;
    float *p_result=&result;
    _asm
    {
        mov eax,dword ptr a
        mov ebx,dword ptr b
        movups xmm0,[eax]
        movups xmm1,[ebx]
        mulps xmm0,xmm1
        movaps xmm1,xmm0
        shufps xmm1,xmm1,0b1h
        addps xmm1,xmm0
        movaps xmm2,xmm1
        shufps xmm2,xmm2,02h
        addps xmm2,xmm1
        mov eax,dword ptr p_result
        movss [eax],xmm2
    }
    return result;
}

有没有办法可以在我的函数中传递float结果和float *p_result的声明?

1 个答案:

答案 0 :(得分:1)

首先:如果可以使用EAX,ECX或EDX,请不要使用EBX寄存器。根据{{​​3}},是保存了EBX,ESI和EDI的被调用者,即该函数必须将它们保持不变。 Visual Studio会在必要时管理这些寄存器的存储和恢复,但这是不必要的。

您不需要指向结果的指针。内联汇编器可以直接访问局部变量。此外,如果汇编程序可以识别适当的大小,则不需要大小指令(DWORD PTR)。

float dot(vec3 &a,vec3 &b)      // no ebx, no pointer, no size directive
{
    float result;
    _asm
    {
        mov eax,a
        mov edx,b
        movups xmm0,[eax]
        movups xmm1,[edx]
        mulps xmm0,xmm1
        movaps xmm1,xmm0
        shufps xmm1,xmm1,0b1h
        addps xmm1,xmm0
        movaps xmm2,xmm1
        shufps xmm2,xmm2,02h
        addps xmm2,xmm1
        movss result,xmm2
    }
    return result;
}

如果您自己指定返回值,则可以省略返回行。最终您会收到警告,但是该函数将正确返回。如果返回值为浮点型,则必须位于FPU的ST(0)中。

float dot(vec3 &a,vec3 &b)      // omit return, set ST(0) manually
{
    float result;
    _asm
    {
        mov eax,a
        mov edx,b
        movups xmm0,[eax]
        movups xmm1,[edx]
        mulps xmm0,xmm1
        movaps xmm1,xmm0
        shufps xmm1,xmm1,0b1h
        addps xmm1,xmm0
        movaps xmm2,xmm1
        shufps xmm2,xmm2,02h
        addps xmm2,xmm1
        movss result,xmm2
        fld result
    }
}

编译器会在函数的开头和结尾生成其他代码,称为“序言”和“结尾”。您可以绕开尾声(leave-ret),但这非常脏,因为您不知道序言到底做了什么。要绕过序言和结尾,都将函数声明为cdecl calling convention,并使用ESP作为基本指针。仍然需要内存才能将XMM的结果传输到FPU。我使用传递的变量中的第一个-不再需要它。

__declspec(naked) float dot(vec3 &a,vec3 &b)    // naked
{
    _asm
    {
        mov eax,[esp+4]             ; a
        mov edx,[esp+8]             ; b
        movups xmm0,[eax]
        movups xmm1,[edx]
        mulps xmm0,xmm1
        movaps xmm1,xmm0
        shufps xmm1,xmm1,0b1h
        addps xmm1,xmm0
        movaps xmm2,xmm1
        shufps xmm2,xmm2,02h
        addps xmm2,xmm1
        movss [esp+4],xmm2          ; a
        fld [esp+4]                 ; Result in ST(0)
        ret
    }
}

使用naked消除了最后剩下的堆栈开销。现在,参数已在ECX和EDX寄存器中传递。但是这样您就没有更多的存储空间可以存储结果了。我的建议:为此使用全局变量。

float dummy;                        // global
__declspec(naked) float __fastcall dot(vec3 &a,vec3 &b) // fastcall, global variable
{
    _asm
    {
        movups xmm0,[ecx]           ; a
        movups xmm1,[edx]           ; b
        mulps xmm0,xmm1
        movaps xmm1,xmm0
        shufps xmm1,xmm1,0b1h
        addps xmm1,xmm0
        movaps xmm2,xmm1
        shufps xmm2,xmm2,02h
        addps xmm2,xmm1
        sub esp, 4
        movss [dummy],xmm2
        fld [dummy]
        add esp, 4
        ret
    }
}

有些人不喜欢全局变量,我不确定,如果在使用线程时遇到麻烦。使用堆栈有点麻烦,因为Windows不知道像Linux这样的红色区域。

__declspec(naked) float __fastcall dot(vec3 &a,vec3 &b) // fastcall, stack access
{
    _asm
    {
        movups xmm0,[ecx]           ; a
        movups xmm1,[edx]           ; b
        mulps xmm0,xmm1
        movaps xmm1,xmm0
        shufps xmm1,xmm1,0b1h
        addps xmm1,xmm0
        movaps xmm2,xmm1
        shufps xmm2,xmm2,02h
        addps xmm2,xmm1
        sub esp, 4
        movss [esp],xmm2
        fld [esp]
        add esp, 4
        ret
    }
}

为什么不使用宏?为什么不对齐向量以使用更快的指令(例如MOVAPS)?这是一个显示将汇编块用作宏的程序:

    #include <windows.h>
    #include <stdio.h>

    typedef __declspec(align(16)) float vec3[4];    // https://docs.microsoft.com/cpp/cpp/align-cpp#vclrf_declspecaligntypedef

    #define dotproduct(dest,a,b) __asm  \
        { \
            __asm movaps xmm0,[a]       \
            __asm movaps xmm1,[b]       \
            __asm mulps xmm0,xmm1       \
            __asm movaps xmm1,xmm0      \
            __asm shufps xmm1,xmm1,0b1h \
            __asm addps xmm1,xmm0       \
            __asm movaps xmm2,xmm1      \
            __asm shufps xmm2,xmm2,02h  \
            __asm addps xmm2,xmm1       \
            __asm movss [dest],xmm2     \
        }

    int main ( void )
    {
        vec3 f1 = {1.0,2.0,3.0};
        vec3 f2 = {5.0,6.0,8.0};
        float R;

        dotproduct (R,f1,f2)

        printf ("%f\n",R);
        return 0;
    }