这是我的第一个问题,因为我找不到与此主题相关的任何内容。
最近,在为我的C游戏引擎项目制作 class 时,我发现了一些有趣的东西:
struct Stack *S1 = new(Stack);
struct Stack *S2 = new(Stack);
S1->bPush(S1, 1, 2); //at this point
bPush
是结构中的函数指针。所以我想知道,在这种情况下,运算符
->
是什么,我发现: mov r8b,2 ; a char, written to a low point of register r8
mov dl,1 ; also a char, but to d this time
mov rcx,qword ptr [S1] ; this is the 1st parameter of function
mov rax,qword ptr [S1] ; !Why cannot I use this one?
call qword ptr [rax+1A0h] ; pointer call
所以我假设->写一个指向rcx的对象指针,并且我想在函数中使用它(应该使用的方法)。所以问题是,我该怎么做
push rcx
// do other call vars
pop rcx
mov qword ptr [this], rcx
在开始编写函数的其他变量之前。预处理器有东西吗?
最佳答案
如果您使用C++编写,看起来您会更轻松(并且获得相同或更高效率的asm),以便可以使用对虚拟函数以及初始化时运行构造函数的内置语言支持。更不用说不必手动运行析构函数。您不需要struct Class
hack。
我想隐式地传递*this
指针,因为正如第二个asm部分中所示,它做了两次相同的事情,是的,这正是我想要的,bPush是结构的一部分,不能从外部调用,但是我必须传递它已经拥有的指针S1。
由于禁用了优化,您的asm效率低下。
MSVC -O2
或-Ox
不会两次重新加载静态指针。确实浪费了寄存器之间的mov
指令复制,但是如果您想要更好的asm,请使用更好的编译器(例如gcc或clang)。
Godbolt编译器资源管理器上最古老的MSVC是MSVC 2015中的CL19.0,它可以编译此源代码
struct Stack {
int stuff[4];
void (*bPush)(struct Stack*, unsigned char value, unsigned char length);
};
struct Stack *const S1 = new(Stack);
int foo(){
S1->bPush(S1, 1, 2);
//S1->bPush(S1, 1, 2);
return 0; // prevent tailcall optimization
}
into this asm (Godbolt)
# MSVC 2015 -O2
int foo(void) PROC ; foo, COMDAT
$LN4:
sub rsp, 40 ; 00000028H
mov rax, QWORD PTR Stack * __ptr64 __ptr64 S1
mov r8b, 2
mov dl, 1
mov rcx, rax ;; copy RAX to the arg-passing register
call QWORD PTR [rax+16]
xor eax, eax
add rsp, 40 ; 00000028H
ret 0
int foo(void) ENDP ; foo
(我以C++模式编译,因此无需复制github代码即可编写
S1 = new(Stack)
,并使用非常量初始化程序在全局范围内编写它。)Clang7.0
-O3
立即加载到RCX
中:# clang -O3
foo():
sub rsp, 40
mov rcx, qword ptr [rip + S1]
mov dl, 1
mov r8b, 2
call qword ptr [rcx + 16] # uses the arg-passing register
xor eax, eax
add rsp, 40
ret
奇怪的是,当使用
__attribute__((ms_abi))
定位Windows ABI时,clang只决定使用低字节寄存器。当使用默认的Linux调用约定而不是mov esi, 1
时,它使用mov sil, 1
避免错误的依赖关系。或者,如果您使用的是优化,那是因为即使是较旧的MSVC甚至更糟。在这种情况下,尽管您可以尝试使用
struct Stack *p = S1
局部变量来使编译器一次将其加载到寄存器中并从那里重新使用,但是您可能无法在C源代码中进行任何操作来对其进行修复。)关于c - 在进入功能之前,有什么方法可以保存寄存器吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55196055/