gpt4 book ai didi

c++ - MSVC汇编函数自变量C++与_asm

转载 作者:行者123 更新时间:2023-11-28 00:13:58 29 4
gpt4 key购买 nike

我有一个函数,它接受3个参数,dest,src0,src1,每个参数指向大小为12的数据。我制作了两个版本。一个是用C编写并由编译器优化的,另一个是完全用_asm编写的。是的。 3个论点?我自然会做类似的事情:

mov ecx, [src0]
mov edx, [src1]
mov eax, [dest]


我对编译器有些困惑,因为它适合添加以下内容:

_src0$ = -8                     ; size = 4
_dest$ = -4 ; size = 4
_src1$ = 8 ; size = 4
?vm_vec_add_scalar_asm@@YAXPAUvec3d@@PBU1@1@Z PROC ; vm_vec_add_scalar_asm
; _dest$ = ecx
; _src0$ = edx

; 20 : {

sub esp, 8
mov DWORD PTR _src0$[esp+8], edx
mov DWORD PTR _dest$[esp+8], ecx

; 21 : _asm
; 22 : {
; 23 : mov ecx, [src0]

mov ecx, DWORD PTR _src0$[esp+8]

; 24 : mov edx, [src1]

mov edx, DWORD PTR _src1$[esp+4]

; 25 : mov eax, [dest]

mov eax, DWORD PTR _dest$[esp+8]

Function body etc.

add esp, 8
ret 0


_src0 $ [esp + 8]等的含义是什么?为什么在我的代码之前执行所有这些操作?为什么它会试图[显然]堆叠如此之差的东西?

相比之下,C ++版本的正文之前只有以下内容,这非常相似:

_src1$ = 8                      ; size = 4
?vm_vec_add@@YAXPAUvec3d@@PBU1@1@Z PROC ; vm_vec_add
; _dest$ = ecx
; _src0$ = edx

mov eax, DWORD PTR _src1$[esp-4]


为什么这还不够?

最佳答案

Mats Petersson的答案解释了__fastcall。但是我想那不完全是你要的...

实际上,_src0$[esp+8]仅表示[_src0$ + esp + 8],而_src0$的定义如上:

_src0$ = -8                     ; size = 4


因此,整个表达式 _src0$[esp+8]只不过是 [esp] ...

要了解为什么要执行所有这些操作,您可能应该首先了解Mats Petersson在他的帖子中所说的 __fastcall,或更一般地说,什么是调用约定。有关详细信息,请参见他的文章中的链接。

假设您已经了解了 __fastcall,现在让我们看看您的代码会发生什么。编译器正在使用 __fastcall。您的被调用方函数是 f(dst, src0, src1),它需要 3参数,因此根据调用约定,当调用方调用 f时,它将执行以下操作:


dst移至 ecx并将 src0移至 edx
src1推入堆栈
将4个字节的返回地址压入堆栈
转到函数 f的起始地址


被调用方 f在其代码开始时便知道参数的位置: dstsrc0分别位于寄存器 ecxedx中; esp指向4个字节的返回地址,但它下面的4个字节(即DWORD PTR [esp + 4])恰好是 src1

因此,在您的“ C ++版本”中,函数 f会执行应做的工作:

mov eax, DWORD PTR _src1$[esp-4]


在这里 _src1$ = 8,所以 _src1$[esp-4]恰好是 [esp+4]。看,它只是检索参数 src1并将其存储在 eax中。

但是,这里有一个棘手的问题。在 f代码中,如果要多次使用参数 src1,则可以这样做,因为它始终存储在堆栈中,位于返回地址的正下方;但是如果您想多次使用 dstsrc0怎么办?它们在寄存器中,可以随时销毁。

因此,在这种情况下,编译器应该执行以下操作:在输入函数 f之后,它应该记住 ecxedx的当前值(通过将它们压入堆栈)。这8个字节就是所谓的“影子空间”。在您的“ C ++版本”中未完成此操作,可能是因为编译器确定不会多次使用这两个参数,或者可以以其他方式正确地处理这两个参数。

现在,您的 _asm版本会怎样?这里的问题是您正在使用内联汇编。然后,编译器将失去对寄存器的控制,并且不能假定寄存器 ecxedx_asm块中是安全的(实际上不是,因为您在 _asm块中使用了它们)。因此,必须在函数开始时保存它们。

保存过程如下:首先将 esp增大8个字节( sub esp, 8),然后将 edxecx分别移到 [esp][esp+4]

然后它可以安全地输入您的 _asm块。现在想到的是(如果有),图片是 [esp]src0[esp+4]dst[esp+8]是4字节返回地址,而 [esp+12]src1。它不再考虑 ecxedx

因此,您在 _asm块中的第一条指令 mov ecx, [src0]应该解释为 mov ecx, [esp],与

mov ecx, DWORD PTR _src0$[esp+8]


与其他两个指令相同。

在这一点上,您可能会说,啊哈,它正在做愚蠢的事情,我不希望它浪费时间和空间,有没有办法?

好吧,有一种方法-不要使用内联汇编...这很方便,但是有一个折衷。

您可以在 f源文件中编写汇编函数 .asm,然后在 public中编写。在 C/C++代码中,将其声明为 extern 'C' f(...)。然后,当您开始使用汇编功能 f时,可以直接使用 ecxedx进行播放。

关于c++ - MSVC汇编函数自变量C++与_asm,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31527692/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com