Dra*_*ion 6 c++ x86 this inline-assembly visual-c++
根据MSDN文档,当使用类函数的默认__thiscall调用约定时,"this"指针存储在ECX中.尽管在翻译常规C++代码时肯定会出现这种情况,但在尝试使用内联汇编访问"this"时遇到了问题.
这是测试程序:
#include <cstdio>
class TestClass
{
long x;
public:
inline TestClass(long x):x(x){}
public:
inline long getX1(){return x;}
inline long getX2()
{
_asm
{
mov eax,dword ptr[ecx]
}
}
};
int main()
{
TestClass c(42);
printf("c.getX1() = %d\n",c.getX1());
printf("c.getX2() = %d\n",c.getX2());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
两个Get函数的翻译如下:
?getX1@TestClass@@QAEJXZ (public: long __thiscall TestClass::getX1(void)):
00000000: 8B 01 mov eax,dword ptr [ecx]
00000002: C3 ret
?getX2@TestClass@@QAEJXZ (public: long __thiscall TestClass::getX2(void)):
00000000: 8B 01 mov eax,dword ptr [ecx]
00000002: C3 ret
Run Code Online (Sandbox Code Playgroud)
我认为这两个功能完全相同是可以肯定的.不过,这是程序的输出:
c.getX1() = 42
c.getX2() = 1
Run Code Online (Sandbox Code Playgroud)
显然,当调用第二个Get函数时,"this" 不会存储在ECX中,所以我的问题是:我如何确保包含内联汇编的类函数遵循调用约定和/或以与常规/非内联相同的方式调用功能?
编辑:主要功能翻译如下:
_main:
00000000: 51 push ecx
00000001: 6A 2A push 2Ah
00000003: 68 00 00 00 00 push offset $SG3948
00000008: E8 00 00 00 00 call _printf
0000000D: 83 C4 08 add esp,8
00000010: 8D 0C 24 lea ecx,[esp]
00000013: E8 00 00 00 00 call ?getX2@TestClass@@QAEJXZ
00000018: 50 push eax
00000019: 68 00 00 00 00 push offset $SG3949
0000001E: E8 00 00 00 00 call _printf
00000023: 33 C0 xor eax,eax
00000025: 83 C4 0C add esp,0Ch
00000028: C3 ret
Run Code Online (Sandbox Code Playgroud)
我不知道你是否会误读的文档,还是它的写得不好,但__thiscall确实不是意味着this
指针存储在ECX; 这意味着指向对象的指针
在ECX中传递.在较大的函数中,我已经看到它在函数的不同位置从一个寄存器移动到另一个寄存器,在某些情况下,我已经看到它溢出到内存中.你不能指望它在ECX中.它将在哪里根据函数中的其他代码进行更改,并将优化标志传递给编译器.
在您的情况下,由于您的函数是内联的,并且可能已经内联,因此问题更加复杂.(除非这
_asm可能会抑制内联.)常量传播(一种非常简单且广泛使用的优化技术)几乎肯定意味着您的调用c.getX1()只会使用42,没有函数调用且无法访问c任何内容.
通常,内联汇编程序是一个棘手的问题,正是因为您不知道编译器正在使用什么寄存器.通常,除了实际的汇编程序指令之外,还有指令告诉编译器诸如哪些寄存器和您使用的变量之类的东西,并且您将能够在汇编程序中引用变量本身以及其他此类信息.除非你使用这些,否则你可以假设内联汇编程序非常非常少.
但每个编译器都有自己的规则.经常使用特殊语法.喜欢的东西mov eax, [cx].x,例如,或者mov eax, x,可能是微软的内联汇编的需求是什么.无论如何,你编写的文件可能无法推断出你正在访问的内容c.x.并且由于所有其他用途已通过常量传播消除,因此即使生成变量也是一个非常差的编译器c.
编辑:
FWIW:Microsoft内联汇编程序的文档位于
http://msdn.microsoft.com/en-us/library/4ks26t93%28v=vs.71%29.aspx.我没有详细介绍它,但有一节关于"在__asm块中使用C或C++符号".这可能会解释如何x以编译器知道x已访问的方式访问内联汇编程序.
这就是我如何使该函数正确工作(即在 ECX 中传递“this”):
测试类.hpp:
class TestClass
{
long x;
public:
inline TestClass(long x):x(x){}
public:
long getX1();
long getX2();
};
Run Code Online (Sandbox Code Playgroud)
测试类.cpp:
#include "testclass.hpp"
long TestClass::getX1()
{
return x;
}
long TestClass::getX2()
{
_asm
{
mov eax,dword ptr[ecx]
}
}
Run Code Online (Sandbox Code Playgroud)
测试主.cpp:
#include <cstdio>
#include "testclass.hpp"
int main()
{
TestClass c(42);
printf("c.getX1() = %d\n",c.getX1());
printf("c.getX2() = %d\n",c.getX2());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
c.getX1() = 42
c.getX2() = 42
Run Code Online (Sandbox Code Playgroud)
问题是 MSVC 2010 中的内联类函数不一定遵循 MSDN 指定的调用约定。我不认为这是一个错误,但如果您计划在内联函数中使用内联汇编,您至少应该意识到这一点。我的建议是你不要这样做。如果您需要在类函数中进行内联汇编,请将声明和实现分开。