Похоже, вам было бы легче (и вы получите asm, который такой же или более эффективный), если бы вы писали на C ++, чтобы вы могли использовать встроенную поддержку языка для виртуальных функций и для запуска конструкторов при инициализации. Не говоря уже о том, чтобы не запускать деструкторы вручную. Вам не понадобится ваш struct Class
хак.
Я бы хотел неявно передать указатель *this
, потому что, как показано во второй части asm, он делает то же самое дважды, да, это то, что я ищу, bPush является частью структуры и не может вызываться извне, но мне нужно передать указатель S1, который у него уже есть.
Вы получаете неэффективный Asm, потому что вы отключили оптимизацию.
MSVC -O2
или -Ox
не перезагружает статический указатель дважды. Он тратит впустую mov
инструкцию копирования между регистрами, но если вы хотите лучше asm, используйте лучший компилятор (например, gcc или clang).
Самым старым MSVC в проводнике компилятора Godbolt является CL19.0 из MSVC 2015, который компилирует этот источник
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
Как ни странно, clang решает использовать только младшие байты регистров при назначении Windows ABI с __attribute__((ms_abi))
. Он использует mov esi, 1
, чтобы избежать ложных зависимостей при настройке соглашения о вызовах в Linux по умолчанию, а не mov sil, 1
.
Или, если вы используете оптимизацию, то это потому, что даже более старый MSVC еще хуже. В этом случае вы, вероятно, не сможете ничего сделать в исходном коде C, чтобы исправить это, хотя вы можете попробовать использовать локальную переменную struct Stack *p = S1
, чтобы вручную удерживать компилятор в загрузке его в регистр и повторном его использовании оттуда.)