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

问题描述 投票:0回答:1

我正在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的声明?

variables visual-studio-2012 assembly sse inline-assembly
1个回答
0
投票

首先:如果可以使用EAX,ECX或EDX,请不要使用EBX寄存器。根据cdecl calling convention,有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),但这非常脏,因为您不完全了解序言所做的事情。要绕开序言和结尾,都将函数声明为naked,并使用naked作为基本指针。仍然需要内存才能将XMM的结果传输到FPU。我为此使用了第一个传递的变量-不再需要它。

ESP

使用__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 } } ,您消除了最后剩余的堆栈开销。现在,参数已在ECX和EDX寄存器中传递。但是这样您就没有更多的存储空间了。我的建议:为此使用全局变量。

__fastcall calling convention

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

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

为什么不使用宏?为什么不对齐向量以使用更快的指令(例如__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
© www.soinside.com 2019 - 2024. All rights reserved.