有没有办法在进入功能之前保存寄存器?
Is there any way to save registers before jumping into function?
这是我的第一个问题,因为我找不到与此主题相关的任何内容。
最近,在为我的 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。
I'd like to implicitly pass *this
pointer, because as shown in second asm part it does the same thing twice, yes, it is what I'm looking for, bPush is a part of a struct and it cannot be called from outside, but I have to pass the pointer S1, which it already has.
你的 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
}
# 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++ 模式下编译,因此我可以编写 S1 = new(Stack)
而无需复制您的 github 代码,并使用非常量初始化程序在全局范围内编写它。)
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 sil, 1
.
时使用 mov esi, 1
来避免错误依赖
或者,如果您正在使用优化,那是因为更旧的 MSVC 更糟糕。在那种情况下,您可能无法在 C 源代码中做任何事情来修复它,尽管您可以尝试使用 struct Stack *p = S1
局部变量来手动控制编译器将其加载到寄存器中一次并从那里重新使用它。 )
这是我的第一个问题,因为我找不到与此主题相关的任何内容。
最近,在为我的 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。
I'd like to implicitly pass
*this
pointer, because as shown in second asm part it does the same thing twice, yes, it is what I'm looking for, bPush is a part of a struct and it cannot be called from outside, but I have to pass the pointer S1, which it already has.
你的 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
}
# 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++ 模式下编译,因此我可以编写 S1 = new(Stack)
而无需复制您的 github 代码,并使用非常量初始化程序在全局范围内编写它。)
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 sil, 1
.
mov esi, 1
来避免错误依赖
或者,如果您正在使用优化,那是因为更旧的 MSVC 更糟糕。在那种情况下,您可能无法在 C 源代码中做任何事情来修复它,尽管您可以尝试使用 struct Stack *p = S1
局部变量来手动控制编译器将其加载到寄存器中一次并从那里重新使用它。 )